Skip to main content

uv_cache_info/
timestamp.rs

1use serde::{Deserialize, Serialize};
2use std::path::Path;
3
4/// A timestamp used to measure changes to a file.
5///
6/// On Unix, this uses `ctime` as a conservative approach. `ctime` should detect all
7/// modifications, including some that we don't care about, like hardlink modifications.
8/// On other platforms, it uses `mtime`.
9///
10/// See: <https://github.com/restic/restic/issues/2179>
11/// See: <https://apenwarr.ca/log/20181113>
12#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
13pub struct Timestamp(std::time::SystemTime);
14
15impl Timestamp {
16    /// Return the [`Timestamp`] for the given path.
17    pub fn from_path(path: impl AsRef<Path>) -> std::io::Result<Self> {
18        let metadata = fs_err::metadata(path.as_ref())?;
19        Ok(Self::from_metadata(&metadata))
20    }
21
22    /// Return the [`Timestamp`] for the given metadata.
23    pub fn from_metadata(metadata: &std::fs::Metadata) -> Self {
24        cfg_select! {
25            unix => {
26                use std::os::unix::fs::MetadataExt;
27
28                let ctime =
29                    u64::try_from(metadata.ctime()).expect("ctime to be representable as u64");
30                let ctime_nsec = u32::try_from(metadata.ctime_nsec())
31                    .expect("ctime_nsec to be representable as u32");
32                let duration = std::time::Duration::new(ctime, ctime_nsec);
33                Self(std::time::UNIX_EPOCH + duration)
34            },
35            _ => {
36                let modified = metadata.modified().expect("modified time to be available");
37                Self(modified)
38            },
39        }
40    }
41
42    /// Return the current [`Timestamp`].
43    pub fn now() -> Self {
44        Self(std::time::SystemTime::now())
45    }
46}
47
48impl From<std::time::SystemTime> for Timestamp {
49    fn from(system_time: std::time::SystemTime) -> Self {
50        Self(system_time)
51    }
52}