const_uuid/
lib.rs

1
2use std::str::FromStr;
3
4pub use const_uuid_proc_macros::*;
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub struct ConstUuidError {
8    err: &'static str,
9}
10
11impl ConstUuidError {
12    pub fn new(err: &'static str) -> Self {
13        Self { err }
14    }
15}
16
17impl std::fmt::Display for ConstUuidError {
18    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
19        write!(f, "{}", self.err)
20    }
21}
22
23impl std::error::Error for ConstUuidError {}
24
25/// Lightweight UUID container for const contexts.
26#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct ConstUuid(pub u128);
28
29impl Default for ConstUuid {
30    fn default() -> Self {
31        ConstUuid(0)
32    }
33}
34
35impl FromStr for ConstUuid {
36    type Err = ConstUuidError;
37
38    fn from_str(s: &str) -> Result<Self, Self::Err> {
39        let s = s.trim();
40        if s.len() != 36 {
41            return Err(ConstUuidError::new("Invalid UUID length"));
42        }
43
44        let mut bytes = [0u8; 16];
45        let mut i = 0;
46        for (j, c) in s.chars().enumerate() {
47            if j == 8 || j == 13 || j == 18 || j == 23 {
48                if c != '-' {
49                    return Err(ConstUuidError::new("Invalid UUID format"));
50                }
51                continue;
52            }
53
54            if c.is_ascii_hexdigit() {
55                let b = c.to_digit(16).unwrap() as u8;
56                if i % 2 == 0 {
57                    bytes[i / 2] = b << 4;
58                } else {
59                    bytes[i / 2] |= b;
60                }
61                i += 1;
62            } else {
63                return Err(ConstUuidError::new("Invalid UUID format"));
64            }
65        }
66
67        Ok(ConstUuid(u128::from_be_bytes(bytes)))
68    }
69}
70
71impl std::fmt::Display for ConstUuid {
72    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
73        let bytes = self.0.to_be_bytes();
74        let string = bytes.iter().map(|b| format!("{:02x}", b)).collect::<String>();
75        write!(f, "{}-{}-{}-{}-{}", &string[0..8], &string[8..12], &string[12..16], &string[16..20], &string[20..32])
76    }
77}
78
79impl std::fmt::Debug for ConstUuid {
80    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
81        write!(f, "ConstUuid({})", self)
82    }
83}
84
85impl ConstUuid {
86    pub const NIL: ConstUuid = ConstUuid(0);
87
88    /// Extracts the inner `u128` value.
89    pub fn to_u128(&self) -> u128 {
90        self.0
91    }
92
93    /// Converts the UUID to a byte array.
94    pub fn to_bytes(&self) -> [u8; 16] {
95        self.0.to_be_bytes()
96    }
97
98    /// Checks if the UUID is nil.
99    pub fn is_nil(&self) -> bool {
100        self.0 == 0
101    }
102}
103
104/// Creates a `ConstUuid` from a string literal.
105#[macro_export]
106macro_rules! const_uuid {
107    ($uuid:expr) => {
108        $crate::ConstUuid($crate::const_uuid_u128!($uuid))
109    };
110}
111
112#[cfg(test)]
113mod test {
114    use super::*;
115
116    #[test]
117    fn default() {
118        let uuid = ConstUuid::default();
119        assert_eq!(uuid.to_u128(), 0);
120        let parsed = const_uuid!("00000000-0000-0000-0000-000000000000");
121        assert_eq!(uuid, parsed);
122    }
123
124    #[test]
125    fn macro_vs_parse() {
126        let uuid = const_uuid!("01234567-89ab-cdef-0123-456789abcdef");
127        let parsed = ConstUuid::from_str("01234567-89ab-cdef-0123-456789abcdef").unwrap();
128        assert_eq!(uuid, parsed);
129    }
130
131    #[test]
132    fn parse_and_back() {
133        let uuid = ConstUuid::from_str("01234567-89ab-cdef-0123-456789abcdef").unwrap();
134        assert_eq!(uuid.to_string(), "01234567-89ab-cdef-0123-456789abcdef");
135    }
136}