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#[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 pub fn to_u128(&self) -> u128 {
90 self.0
91 }
92
93 pub fn to_bytes(&self) -> [u8; 16] {
95 self.0.to_be_bytes()
96 }
97
98 pub fn is_nil(&self) -> bool {
100 self.0 == 0
101 }
102}
103
104#[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}