1use std::fmt;
6use std::str::FromStr;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct Id([u8; 16]);
11
12impl Id {
13 pub fn new() -> Self {
15 let mut bytes = [0u8; 16];
16
17 use std::collections::hash_map::RandomState;
19 use std::hash::{BuildHasher, Hasher};
20
21 let hasher1 = RandomState::new().build_hasher();
22 let hasher2 = RandomState::new().build_hasher();
23
24 let h1 = hasher1.finish();
25 let h2 = hasher2.finish();
26
27 bytes[0..8].copy_from_slice(&h1.to_le_bytes());
28 bytes[8..16].copy_from_slice(&h2.to_le_bytes());
29
30 bytes[6] = (bytes[6] & 0x0f) | 0x40; bytes[8] = (bytes[8] & 0x3f) | 0x80; Self(bytes)
35 }
36
37 pub fn parse(s: &str) -> Result<Self, ParseError> {
39 let s = s.replace("-", "");
40 if s.len() != 32 {
41 return Err(ParseError::InvalidLength);
42 }
43
44 let mut bytes = [0u8; 16];
45 for (i, chunk) in s.as_bytes().chunks(2).enumerate() {
46 let hex = std::str::from_utf8(chunk).map_err(|_| ParseError::InvalidChar)?;
47 bytes[i] = u8::from_str_radix(hex, 16).map_err(|_| ParseError::InvalidChar)?;
48 }
49
50 Ok(Self(bytes))
51 }
52
53 pub fn as_bytes(&self) -> &[u8; 16] {
55 &self.0
56 }
57
58 pub fn to_string(&self) -> String {
60 format!(
61 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
62 self.0[0], self.0[1], self.0[2], self.0[3],
63 self.0[4], self.0[5],
64 self.0[6], self.0[7],
65 self.0[8], self.0[9],
66 self.0[10], self.0[11], self.0[12], self.0[13], self.0[14], self.0[15]
67 )
68 }
69
70 pub fn nil() -> Self {
72 Self([0u8; 16])
73 }
74
75 pub fn is_nil(&self) -> bool {
77 self.0.iter().all(|&b| b == 0)
78 }
79}
80
81impl Default for Id {
82 fn default() -> Self {
83 Self::new()
84 }
85}
86
87impl fmt::Display for Id {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 write!(f, "{}", self.to_string())
90 }
91}
92
93impl FromStr for Id {
94 type Err = ParseError;
95
96 fn from_str(s: &str) -> Result<Self, Self::Err> {
97 Self::parse(s)
98 }
99}
100
101#[derive(Debug, Clone)]
102pub enum ParseError {
103 InvalidLength,
104 InvalidChar,
105}
106
107impl fmt::Display for ParseError {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 match self {
110 ParseError::InvalidLength => write!(f, "Invalid ID length"),
111 ParseError::InvalidChar => write!(f, "Invalid character in ID"),
112 }
113 }
114}
115
116impl std::error::Error for ParseError {}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_id_generation() {
124 let id1 = Id::new();
125 let id2 = Id::new();
126 assert_ne!(id1, id2);
127 }
128
129 #[test]
130 fn test_id_parse() {
131 let id = Id::new();
132 let s = id.to_string();
133 let parsed = Id::parse(&s).unwrap();
134 assert_eq!(id, parsed);
135 }
136
137 #[test]
138 fn test_nil() {
139 let nil = Id::nil();
140 assert!(nil.is_nil());
141 assert_eq!(nil.to_string(), "00000000-0000-0000-0000-000000000000");
142 }
143}
144
145#[cfg(feature = "serde")]
147impl avila_serde::Serialize for Id {
148 fn to_value(&self) -> avila_serde::Value {
149 avila_serde::Value::String(self.to_string())
151 }
152}
153
154#[cfg(feature = "serde")]
155impl avila_serde::Deserialize for Id {
156 fn from_value(value: avila_serde::Value) -> Result<Self, avila_serde::Error> {
157 match value {
158 avila_serde::Value::String(s) => {
159 Self::parse(&s).map_err(|e| avila_serde::Error::Parse(format!("Invalid ID: {}", e)))
160 }
161 _ => Err(avila_serde::Error::Parse("Expected string for Id".to_string()))
162 }
163 }
164}