1use serde::{Deserialize, Serialize};
13use std::fmt;
14use std::hash::Hash;
15use std::str::FromStr;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
22pub struct UuidValue(pub [u8; 16]);
23
24impl UuidValue {
25 pub const fn from_bytes(bytes: [u8; 16]) -> Self {
27 Self(bytes)
28 }
29
30 pub fn from_slice(slice: &[u8]) -> Result<Self, UuidError> {
32 if slice.len() != 16 {
33 return Err(UuidError::InvalidLength(slice.len()));
34 }
35 let mut bytes = [0u8; 16];
36 bytes.copy_from_slice(slice);
37 Ok(Self(bytes))
38 }
39
40 pub fn parse_str(s: &str) -> Result<Self, UuidError> {
42 let hex_str: String = s.chars().filter(|&c| c != '-').collect();
44
45 if hex_str.len() != 32 {
46 return Err(UuidError::InvalidFormat(s.to_string()));
47 }
48
49 let mut bytes = [0u8; 16];
51 for (i, chunk) in hex_str.as_bytes().chunks(2).enumerate() {
52 let hex_chunk =
53 std::str::from_utf8(chunk).map_err(|_| UuidError::InvalidFormat(s.to_string()))?;
54 bytes[i] = u8::from_str_radix(hex_chunk, 16)
55 .map_err(|_| UuidError::InvalidFormat(s.to_string()))?;
56 }
57
58 Ok(Self(bytes))
59 }
60
61 pub fn new_v4() -> Self {
63 use rand::Rng;
64 let mut bytes = [0u8; 16];
65 rand::thread_rng().fill(&mut bytes);
66
67 bytes[6] = (bytes[6] & 0x0f) | 0x40; bytes[8] = (bytes[8] & 0x3f) | 0x80; Self(bytes)
72 }
73
74 pub const fn as_bytes(&self) -> &[u8; 16] {
76 &self.0
77 }
78
79 pub const fn to_bytes(self) -> [u8; 16] {
81 self.0
82 }
83
84 pub fn version(&self) -> u8 {
86 (self.0[6] >> 4) & 0x0f
87 }
88
89 pub fn variant(&self) -> UuidVariant {
91 match self.0[8] >> 6 {
92 0b00 => UuidVariant::NCS,
93 0b10 => UuidVariant::RFC4122,
94 0b11 => UuidVariant::Microsoft,
95 _ => UuidVariant::Future,
96 }
97 }
98
99 pub fn to_hyphenated_string(&self) -> String {
101 format!(
102 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
103 self.0[0], self.0[1], self.0[2], self.0[3],
104 self.0[4], self.0[5],
105 self.0[6], self.0[7],
106 self.0[8], self.0[9],
107 self.0[10], self.0[11], self.0[12], self.0[13], self.0[14], self.0[15]
108 )
109 }
110
111 pub fn to_simple_string(&self) -> String {
113 format!(
114 "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
115 self.0[0], self.0[1], self.0[2], self.0[3],
116 self.0[4], self.0[5], self.0[6], self.0[7],
117 self.0[8], self.0[9], self.0[10], self.0[11],
118 self.0[12], self.0[13], self.0[14], self.0[15]
119 )
120 }
121
122 pub fn to_urn_string(&self) -> String {
124 format!("urn:uuid:{}", self.to_hyphenated_string())
125 }
126
127 pub const fn estimated_size(&self) -> usize {
129 std::mem::size_of::<Self>()
130 }
131
132 pub const fn nil() -> Self {
134 Self([0u8; 16])
135 }
136
137 pub const fn max() -> Self {
139 Self([0xffu8; 16])
140 }
141}
142
143impl Default for UuidValue {
144 fn default() -> Self {
145 Self::nil()
146 }
147}
148
149impl fmt::Display for UuidValue {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 write!(f, "{}", self.to_hyphenated_string())
152 }
153}
154
155impl FromStr for UuidValue {
156 type Err = UuidError;
157
158 fn from_str(s: &str) -> Result<Self, Self::Err> {
159 Self::parse_str(s)
160 }
161}
162
163impl AsRef<[u8]> for UuidValue {
164 fn as_ref(&self) -> &[u8] {
165 &self.0
166 }
167}
168
169impl From<[u8; 16]> for UuidValue {
170 fn from(bytes: [u8; 16]) -> Self {
171 Self(bytes)
172 }
173}
174
175impl From<UuidValue> for [u8; 16] {
176 fn from(uuid: UuidValue) -> Self {
177 uuid.0
178 }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
183pub enum UuidVariant {
184 NCS,
186 RFC4122,
188 Microsoft,
190 Future,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq)]
196pub enum UuidError {
197 InvalidLength(usize),
198 InvalidFormat(String),
199}
200
201impl fmt::Display for UuidError {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 match self {
204 UuidError::InvalidLength(len) => {
205 write!(f, "Invalid UUID length: expected 16, got {}", len)
206 }
207 UuidError::InvalidFormat(s) => write!(f, "Invalid UUID format: {}", s),
208 }
209 }
210}
211
212impl std::error::Error for UuidError {}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_uuid_parse() {
220 let uuid_str = "550e8400-e29b-41d4-a716-446655440000";
221 let uuid = UuidValue::parse_str(uuid_str).unwrap();
222 assert_eq!(uuid.to_hyphenated_string(), uuid_str);
223 }
224
225 #[test]
226 fn test_uuid_parse_simple() {
227 let uuid_str = "550e8400e29b41d4a716446655440000";
228 let uuid = UuidValue::parse_str(uuid_str).unwrap();
229 assert_eq!(uuid.to_simple_string(), uuid_str);
230 }
231
232 #[test]
233 fn test_uuid_version() {
234 let uuid = UuidValue::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
236 assert_eq!(uuid.version(), 4);
237 }
238
239 #[test]
240 fn test_uuid_variant() {
241 let uuid = UuidValue::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
242 assert_eq!(uuid.variant(), UuidVariant::RFC4122);
243 }
244
245 #[test]
246 fn test_uuid_nil() {
247 let nil = UuidValue::nil();
248 assert_eq!(nil.to_simple_string(), "00000000000000000000000000000000");
249 }
250
251 #[test]
252 fn test_uuid_max() {
253 let max = UuidValue::max();
254 assert_eq!(max.to_simple_string(), "ffffffffffffffffffffffffffffffff");
255 }
256
257 #[test]
258 fn test_uuid_from_bytes() {
259 let bytes = [
260 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
261 0x00, 0x00,
262 ];
263 let uuid = UuidValue::from_bytes(bytes);
264 assert_eq!(uuid.as_bytes(), &bytes);
265 }
266
267 #[test]
268 fn test_uuid_invalid_format() {
269 let result = UuidValue::parse_str("invalid");
270 assert!(result.is_err());
271 }
272
273 #[test]
274 fn test_uuid_invalid_length() {
275 let result = UuidValue::from_slice(&[1, 2, 3]);
276 assert!(result.is_err());
277 }
278
279 #[test]
280 fn test_uuid_display() {
281 let uuid = UuidValue::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
282 assert_eq!(format!("{}", uuid), "550e8400-e29b-41d4-a716-446655440000");
283 }
284
285 #[test]
286 fn test_uuid_urn() {
287 let uuid = UuidValue::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
288 assert_eq!(
289 uuid.to_urn_string(),
290 "urn:uuid:550e8400-e29b-41d4-a716-446655440000"
291 );
292 }
293
294 #[test]
295 fn test_uuid_equality() {
296 let uuid1 = UuidValue::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
297 let uuid2 = UuidValue::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
298 let uuid3 = UuidValue::parse_str("660e8400-e29b-41d4-a716-446655440000").unwrap();
299 assert_eq!(uuid1, uuid2);
300 assert_ne!(uuid1, uuid3);
301 }
302
303 #[test]
304 fn test_uuid_from_str_trait() {
305 let uuid: UuidValue = "550e8400-e29b-41d4-a716-446655440000".parse().unwrap();
306 assert_eq!(
307 uuid.to_hyphenated_string(),
308 "550e8400-e29b-41d4-a716-446655440000"
309 );
310 }
311}