1use super::{DbError, FromColumn, ToColumn, Value};
15
16#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub struct Uuid([u8; 16]);
29
30impl Uuid {
31 pub const NIL: Uuid = Uuid([0; 16]);
34
35 pub const fn from_bytes(bytes: [u8; 16]) -> Uuid {
37 Uuid(bytes)
38 }
39
40 pub const fn as_bytes(&self) -> &[u8; 16] {
42 &self.0
43 }
44
45 pub fn is_nil(&self) -> bool {
47 self.0 == [0; 16]
48 }
49
50 pub fn version(&self) -> u8 {
52 self.0[6] >> 4
53 }
54
55 pub fn new_v4() -> Uuid {
57 let mut bytes = random_bytes();
58 bytes[6] = (bytes[6] & 0x0f) | 0x40;
59 bytes[8] = (bytes[8] & 0x3f) | 0x80;
60 Uuid(bytes)
61 }
62
63 pub fn now_v7() -> Uuid {
66 use std::time::{SystemTime, UNIX_EPOCH};
67 let millis = SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_millis() as u64).unwrap_or(0);
68 let mut bytes = random_bytes();
69 bytes[..6].copy_from_slice(&millis.to_be_bytes()[2..]);
70 bytes[6] = (bytes[6] & 0x0f) | 0x70;
71 bytes[8] = (bytes[8] & 0x3f) | 0x80;
72 Uuid(bytes)
73 }
74
75 pub fn parse(text: &str) -> Result<Uuid, DbError> {
78 parse(text.trim()).ok_or_else(|| {
79 DbError::new(format!("cannot read {text:?} as a UUID: expected 8-4-4-4-12 hex digits, e.g. 67e55044-10b1-426f-9247-bb680e5fe0c8"))
80 })
81 }
82}
83
84fn random_bytes() -> [u8; 16] {
85 use aws_lc_rs::rand::SecureRandom;
86 let mut bytes = [0u8; 16];
87 aws_lc_rs::rand::SystemRandom::new().fill(&mut bytes).expect("the operating system's random number generator failed");
90 bytes
91}
92
93fn parse(text: &str) -> Option<Uuid> {
94 let hex: Vec<u8> = match text.len() {
95 36 => {
96 let b = text.as_bytes();
97 if [8, 13, 18, 23].iter().any(|&i| b[i] != b'-') {
98 return None;
99 }
100 b.iter().copied().filter(|&c| c != b'-').collect()
101 }
102 32 => text.as_bytes().to_vec(),
103 _ => return None,
104 };
105 if hex.len() != 32 {
106 return None;
107 }
108 let mut bytes = [0u8; 16];
109 for (i, pair) in hex.chunks(2).enumerate() {
110 let hi = (pair[0] as char).to_digit(16)?;
111 let lo = (pair[1] as char).to_digit(16)?;
112 bytes[i] = (hi * 16 + lo) as u8;
113 }
114 Some(Uuid(bytes))
115}
116
117impl std::fmt::Display for Uuid {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 for (i, b) in self.0.iter().enumerate() {
120 if matches!(i, 4 | 6 | 8 | 10) {
121 f.write_str("-")?;
122 }
123 write!(f, "{b:02x}")?;
124 }
125 Ok(())
126 }
127}
128
129impl std::fmt::Debug for Uuid {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 write!(f, "Uuid({self})")
132 }
133}
134
135impl std::str::FromStr for Uuid {
136 type Err = DbError;
137 fn from_str(s: &str) -> Result<Self, DbError> {
138 Uuid::parse(s)
139 }
140}
141
142impl FromColumn for Uuid {
143 fn from_column(v: Value) -> Result<Self, DbError> {
144 match v {
145 Value::Text(s) => Uuid::parse(&s),
146 Value::Bytes(b) if b.len() == 16 => {
147 let mut bytes = [0u8; 16];
148 bytes.copy_from_slice(&b);
149 Ok(Uuid(bytes))
150 }
151 Value::Null => Err(DbError::new("unexpected NULL for Uuid (use Option<Uuid>)")),
152 other => Err(DbError::new(format!("cannot convert {other:?} to Uuid"))),
153 }
154 }
155}
156
157impl ToColumn for Uuid {
158 fn to_column(&self) -> Value {
159 Value::Text(self.to_string())
160 }
161}
162
163#[cfg(feature = "serde")]
164impl serde::Serialize for Uuid {
165 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
166 serializer.collect_str(self)
167 }
168}
169
170#[cfg(feature = "serde")]
171impl<'de> serde::Deserialize<'de> for Uuid {
172 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
173 let text = String::deserialize(deserializer)?;
174 Uuid::parse(&text).map_err(|e| serde::de::Error::custom(e.0))
175 }
176}