1use std::collections::hash_map::DefaultHasher;
8#[cfg(feature = "async")]
9use std::future::Future;
10use std::hash::Hash;
11use std::hash::Hasher;
12use std::marker;
13use std::ops::Deref;
14use std::ops::DerefMut;
15use std::path::Path;
16use std::pin::Pin;
17
18use crate::error::VfsResult;
19use crate::prelude::*;
20#[cfg(feature = "async")]
21use crate::traits::async_vfs::VfsAsync;
22#[cfg(feature = "async")]
23use crate::traits::async_vfs::WriteSupportingVfsAsync;
24use crate::traits::vfs;
25use crate::traits::vfs::PathType;
26#[cfg(feature = "async")]
27use crate::traits::vfs::VfsCore;
28
29#[cfg_attr(feature = "assert_eq", derive(assert_eq::AssertEq))]
51pub struct VersionedHash<T: Hash, P: PathType + ?Sized = Path, H: Hasher + Default = DefaultHasher>
52{
53 value: T,
54 hash: u64,
55 path: P::OwnedPath,
56 _hasher: marker::PhantomData<H>,
57}
58
59impl<T: Hash, P: PathType + ?Sized, H: Hasher + Default> VersionedHash<T, P, H> {
60 pub fn into_inner(self) -> T {
76 self.value
77 }
78
79 fn new_with_hash(path: P::OwnedPath, value: T, hash: u64) -> Self {
80 Self {
81 value,
82 hash,
83 path,
84 _hasher: marker::PhantomData,
85 }
86 }
87
88 fn hash_value(value: &T) -> u64 {
89 let mut hasher = H::default();
90 T::hash(value, &mut hasher);
91 hasher.finish()
92 }
93
94 pub fn new_clean(path: P::OwnedPath, value: T) -> Self {
107 let hash = Self::hash_value(&value);
108
109 Self::new_with_hash(path, value, hash)
110 }
111
112 pub fn new_dirty(path: P::OwnedPath, value: T) -> Self {
125 let hash = Self::hash_value(&value);
126 Self::new_with_hash(path, value, hash.wrapping_add(1))
127 }
128
129 pub fn is_clean(&self) -> bool {
146 self.hash == Self::hash_value(&self.value)
147 }
148
149 pub fn is_dirty(&self) -> bool {
166 !self.is_clean()
167 }
168
169 #[expect(unsafe_code, reason = "Inherently unsafe function, see documentation")]
196 pub unsafe fn reset(&mut self) {
197 self.hash = Self::hash_value(&self.value);
198 }
199}
200
201impl<T: Hash, P: PathType + ?Sized, H: Hasher + Default> Deref for VersionedHash<T, P, H> {
202 type Target = T;
203
204 fn deref(&self) -> &Self::Target {
205 &self.value
206 }
207}
208
209impl<T: Hash, P: PathType + ?Sized, H: Hasher + Default> DerefMut for VersionedHash<T, P, H> {
210 fn deref_mut(&mut self) -> &mut Self::Target {
211 &mut self.value
212 }
213}
214
215impl<'a, T, H, Vfs: vfs::Vfs<'a>> ReadFrom<'a, Vfs> for VersionedHash<T, Vfs::Path, H>
216where
217 T: ReadFrom<'a, Vfs> + Hash + 'a,
218 H: Hasher + Default + 'a,
219{
220 fn read_from(path: &Vfs::Path, vfs: Pin<&'a Vfs>) -> VfsResult<Self, Vfs> {
221 let value = T::read_from(path, vfs)?;
222 let mut hasher = H::default();
223 T::hash(&value, &mut hasher);
224 let hash = hasher.finish();
225 Ok(VersionedHash {
226 value,
227 hash,
228 path: path.owned(),
229 _hasher: marker::PhantomData,
230 })
231 }
232}
233
234impl<'a, T, H, Vfs: vfs::WriteSupportingVfs<'a>> WriteTo<'a, Vfs> for VersionedHash<T, Vfs::Path, H>
235where
236 T: WriteTo<'a, Vfs> + Hash,
237 Vfs::Path: PartialEq,
238 H: Hasher + Default,
239{
240 fn write_to(&self, path: &Vfs::Path, vfs: Pin<&'a Vfs>) -> VfsResult<(), Vfs> {
241 if self.path.as_ref() == path && self.is_clean() {
242 return Ok(());
243 }
244
245 T::write_to(&self.value, path, vfs)
246 }
247}
248
249#[cfg(feature = "async")]
250#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
251impl<'a, T, H, Vfs: VfsAsync + 'a> ReadFromAsync<'a, Vfs> for VersionedHash<T, Vfs::Path, H>
252where
253 T: ReadFromAsync<'a, Vfs> + Hash + 'a,
254 H: Hasher + Default + 'a,
255{
256 type Future
257 = Pin<Box<dyn Future<Output = VfsResult<Self, Vfs>> + Send + 'a>>
258 where
259 Self: 'a;
260 fn read_from_async(
261 path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
262 vfs: Pin<&'a Vfs>,
263 ) -> Self::Future {
264 use std::future::poll_fn;
265
266 let mut fut = Box::pin(T::read_from_async(path.clone(), vfs));
267
268 Box::pin(poll_fn(move |cx| {
269 fut.as_mut().poll(cx).map_ok(|value| {
270 let path = path.clone();
271 let mut hasher = H::default();
272 T::hash(&value, &mut hasher);
273 let hash = hasher.finish();
274 VersionedHash {
275 value,
276 hash,
277 path,
278 _hasher: marker::PhantomData,
279 }
280 })
281 }))
282 }
283}
284
285#[cfg(feature = "async")]
286#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
287impl<'a, T, P, H, Vfs: WriteSupportingVfsAsync<Path = P> + 'a> WriteToAsync<'a, Vfs>
288 for VersionedHash<T, P, H>
289where
290 T: WriteToAsync<'a, Vfs> + Hash + 'a,
291 P: PathType + ?Sized + PartialEq + 'a,
292 H: Hasher + Default + 'a,
293{
294 type Future
295 = Pin<Box<dyn Future<Output = VfsResult<(), Vfs>> + Send + 'a>>
296 where
297 Self: 'a;
298
299 fn write_to_async(
300 self,
301 path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
302 vfs: Pin<&'a Vfs>,
303 ) -> Self::Future {
304 if self.path.as_ref() == path.as_ref() && self.is_clean() {
305 return Box::pin(async { Ok(()) });
306 }
307
308 let fut = Box::pin(T::write_to_async(self.value, path, vfs));
309
310 Box::pin(fut)
311 }
312}
313
314#[cfg(feature = "async")]
315#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
316impl<'a, T, H, Vfs: WriteSupportingVfsAsync + 'a> WriteToAsyncRef<'a, Vfs>
317 for VersionedHash<T, Vfs::Path, H>
318where
319 T: WriteToAsyncRef<'a, Vfs> + Hash + 'a,
320 H: Hasher + Default + 'a,
321{
322 type Future<'f>
323 = Pin<Box<dyn Future<Output = VfsResult<(), Vfs>> + Send + 'f>>
324 where
325 Self: 'f,
326 'a: 'f,
327 Vfs: 'f;
328
329 fn write_to_async_ref<'f>(
330 &'f self,
331 path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
332 vfs: Pin<&'f Vfs>,
333 ) -> Self::Future<'f>
334 where
335 'a: 'f,
336 {
337 if self.path == path && self.is_clean() {
338 return Box::pin(async { Ok(()) });
339 }
340
341 let fut = Box::pin(T::write_to_async_ref(&self.value, path, vfs));
342
343 Box::pin(fut)
344 }
345}