OXYGENHUB

Coverage / crates/oxy-core/src/ids.rs 81.3%

39 of 48 lines run, 33 of 57 functions · run #298 on main · run missed

1
//! Random 128-bit identifiers: ChangeId, NodeId, WorkspaceId (spec §6.3, §7.1, §12.2).
2
3
use rand::RngCore;
4
use std::fmt;
5
6
macro_rules! id128 {
7
    ($name:ident, $doc:literal) => {
8
        #[doc = $doc]
9
        #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
10
        pub struct $name(pub [u8; 16]);
11
12
        impl $name {
13
            pub const ZERO: $name = $name([0; 16]);
14
15
            /// A fresh identifier from the OS CSPRNG.
1610133×
            pub fn random() -> $name {
1710133×
                let mut b = [0u8; 16];
1810133×
                rand::rngs::OsRng.fill_bytes(&mut b);
1910133×
                $name(b)
2010133×
            }
2144502×
            pub fn to_hex(&self) -> String {
2244502×
                hex::encode(self.0)
2344502×
            }
241752×
            pub fn from_hex(s: &str) -> Option<$name> {
251752×
                let v = hex::decode(s).ok()?;
261752×
                Some($name(v.try_into().ok()?))
271752×
            }
28202364×
            pub fn from_slice(b: &[u8]) -> Option<$name> {
29202364×
                Some($name(b.try_into().ok()?))
30202364×
            }
3119915×
            pub fn short(&self, n: usize) -> String {
3219915×
                let h = self.to_hex();
3319915×
                h[..n.min(h.len())].to_string()
3419915×
            }
3526×
            pub fn starts_with_hex(&self, prefix: &str) -> bool {
3626×
                self.to_hex().starts_with(prefix)
3726×
            }
38
        }
39
40
        impl fmt::Display for $name {
410×
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
420×
                f.write_str(&self.to_hex())
430×
            }
44
        }
45
        impl fmt::Debug for $name {
4618624×
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4718624×
                write!(f, "{}({})", stringify!($name), self.short(8))
4818624×
            }
49
        }
50
    };
51
}
52
53
id128!(
54
    ChangeId,
55
    "Stable identity of a change across rewrites (spec §6.2)."
56
);
57
id128!(NodeId, "Stable identity of a file, directory, or symlink across renames (spec §7.1). `NodeId::ZERO` is the root directory.");
58
id128!(
59
    WorkspaceId,
60
    "Identity of a checkout directory (spec §12.2)."
61
);
62
63
impl NodeId {
64
    pub const ROOT: NodeId = NodeId::ZERO;
656824277×
    pub fn is_root(&self) -> bool {
666824277×
        self.is_zero()
676824277×
    }
68
}
69
70
impl ChangeId {
710×
    pub fn is_zero(&self) -> bool {
720×
        *self == Self::ZERO
730×
    }
74
}
75
impl NodeId {
766824277×
    pub fn is_zero(&self) -> bool {
776824277×
        *self == Self::ZERO
786824277×
    }
79
}
80
impl WorkspaceId {
810×
    pub fn is_zero(&self) -> bool {
820×
        *self == Self::ZERO
830×
    }
84
}
85
86
#[cfg(test)]
87
mod tests {
88
    use super::*;
89
90
    #[test]
911×
    fn random_ids_differ_and_round_trip() {
921×
        let a = ChangeId::random();
931×
        let b = ChangeId::random();
941×
        assert_ne!(a, b);
951×
        assert_eq!(ChangeId::from_hex(&a.to_hex()), Some(a));
961×
        assert!(NodeId::ROOT.is_root());
971×
        assert!(!NodeId::random().is_root());
981×
    }
99
}