1use crate::utils::*;
2use serde::{Deserializer, Serializer};
3use alloc::{fmt, string::{ToString, String}};
4use fmt::{Display, Debug, Formatter};
5
6#[derive(Copy, Clone, PartialEq, Hash, Eq)]
7pub struct UUID4 {
8 raw: u128,
9}
10
11impl Display for UUID4 {
12 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
13 f.write_str(self.hex().as_str())
14 }
15}
16
17impl Debug for UUID4 {
18 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
19 f.write_str("UUID4{")?;
20 f.write_str(self.hex().as_str())?;
21 f.write_str("}")
22 }
23}
24
25impl From<u128> for UUID4 {
26 fn from(raw: u128) -> Self {
27 UUID4 { raw }
28 }
29}
30
31impl serde::Serialize for UUID4 {
32 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
33 where
34 S: Serializer,
35 {
36 serializer.serialize_str(self.to_string().as_str())
37 }
38}
39
40impl<'de> serde::Deserialize<'de> for UUID4 {
41 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
42 where
43 D: Deserializer<'de>,
44 {
45 struct Visitor;
46 impl serde::de::Visitor<'_> for Visitor {
47 type Value = UUID4;
48
49 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
50 write!(formatter, "a string representing the UUID")
51 }
52
53 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
54 if let Some(id) = UUID4::parse(v) {
55 Ok(id)
56 } else {
57 Err(serde::de::Error::invalid_value(
58 serde::de::Unexpected::Str(v),
59 &self,
60 ))
61 }
62 }
63 }
64
65 deserializer.deserialize_str(Visitor {})
66 }
67}
68
69impl UUID4 {
70 pub fn parse(from: &str) -> Option<UUID4> {
71 RawUUID::from_str(from).and_then(move |raw| raw.parse4())
72 }
73
74 #[cfg(feature = "std")]
75 pub fn random() -> Self {
76 UUID4 {
77 raw: rand::random(),
78 }
79 }
80
81 pub fn to_u128(self) -> u128 {
82 self.raw
83 }
84
85 pub fn hex(self) -> String {
86 let bytes = self.raw.to_be_bytes();
87 let parts = [
88 hex(&bytes[..4]),
89 hex(&bytes[4..6]),
90 hex(&bytes[6..8]),
91 hex(&bytes[8..10]),
92 hex(&bytes[10..16]),
93 ];
94 parts.join("-")
95 }
96}
97
98struct RawUUID<'a> {
99 parts: [&'a str; 5],
100}
101
102impl<'a> RawUUID<'a> {
103 fn from_str(from: &'a str) -> Option<RawUUID<'a>> {
104 const DASH: &'static str = "-";
105 let (s0, mut from) = str_split(from, 8)?;
108 str_check_hex(s0)?;
109 let (from1, has_dash) = str_tag_optional(from, DASH);
110 from = from1;
111 let consume_dash_f = if has_dash {
112 |str: &'a str| str_tag(str, DASH)
113 } else {
114 |str: &'a str| Some(str)
115 };
116
117 let (s1, from) = str_split(from, 4)?;
118 str_check_hex(s1)?;
119 let from = consume_dash_f(from)?;
120 let (s2, from) = str_split(from, 4)?;
121 str_check_hex(s2)?;
122 let from = consume_dash_f(from)?;
123 let (s3, from) = str_split(from, 4)?;
124 str_check_hex(s3)?;
125 let from = consume_dash_f(from)?;
126 let (s4, from) = str_split(from, 12)?;
127 str_check_hex(s4)?;
128 str_check_eof(from)?;
129
130 Some(Self{
131 parts: [s0, s1, s2, s3, s4],
132 })
133 }
134
135 fn parse4(self) -> Option<UUID4> {
136 let mut bit_index: usize = 0;
137 let mut raw: u128 = 0;
138
139 for part in &self.parts {
140 for char in part.chars() {
141 if let Some(parsed) = parse_hex_char(char as u8) {
142 raw |= (parsed as u128) << (124 - bit_index);
143 bit_index += 4;
144 } else {
145 return None;
146 }
147 }
148 }
149
150 Some(UUID4 { raw })
151 }
152}
153
154fn str_tag<'a>(source: &'a str, tag: &str) -> Option<&'a str> {
155 let (front, back) = str_split(source, tag.len())?;
156 if front != tag {
157 None
158 } else {
159 Some(back)
160 }
161}
162
163fn str_tag_optional<'a>(source: &'a str, tag: &str) -> (&'a str, bool) {
164 str_tag(source, tag)
165 .map(move |v| (v, true))
166 .unwrap_or_else(|| (source, false))
167}
168
169fn str_check_eof(source: &str) -> Option<()> {
170 if source.is_empty() {
171 Some(())
172 } else {
173 None
174 }
175}
176
177fn str_check_hex(mut source: &str) -> Option<()> {
178 if source.is_empty() {
179 return None
180 }
181
182 loop {
183 let (part, rest) = str_split(source, 2)?;
184 for c in part.chars() {
185 parse_hex_char(c as u8)?;
186 }
187
188 source = rest;
189 if source.is_empty() {
190 return Some(());
191 }
192 }
193}
194
195fn str_split(source: &str, n: usize) -> Option<(&str, &str)> {
196 if source.len() < n {
197 None
198 } else {
199 Some(source.split_at(n))
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::UUID4;
206 #[cfg(feature = "std")]
207 use alloc::string::ToString;
208
209 #[cfg(feature = "std")]
210 #[test]
211 fn test_random_uuid4() {
212 UUID4::random();
213 }
214
215 const VALID_UUID: &str = "e1cde35a-0758-47f6-adf8-9dcb44884e5d";
216
217 #[test]
218 fn test_uuid4_parse() {
219 UUID4::parse(VALID_UUID).expect("should parse valid uuid correctly");
220 }
221
222 const VALID_UUID_NO_DASHES: &str = "e1cde35a075847f6adf89dcb44884e5d";
223
224 #[test]
225 fn test_uuid4_parse_nodash() {
226 UUID4::parse(VALID_UUID_NO_DASHES).expect("should parse valid uuid with no dashes correctly");
227 }
228
229 #[test]
230 fn test_parsed_uuid4_to_hex() {
231 let uuid_hex = UUID4::parse(VALID_UUID)
232 .expect("should parse valid uuid correctly")
233 .hex();
234
235 assert_eq!(uuid_hex.as_str(), VALID_UUID)
236 }
237
238 #[test]
239 fn test_uuid4_equal() {
240 let uuid_a = UUID4::parse(VALID_UUID).expect("should parse valid uuid correctly");
241 let uuid_b = UUID4::parse(VALID_UUID).expect("should parse valid uuid correctly");
242 assert_eq!(uuid_a, uuid_b);
243 }
244
245 #[cfg(feature = "std")]
246 #[test]
247 fn test_random_uuid4_hex() {
248 let src_uuid = UUID4::random();
249 let uuid_hex = src_uuid.hex();
250 let uuid_parsed =
251 UUID4::parse(uuid_hex.as_str()).expect("should parse generated uuid correctly");
252 assert_eq!(src_uuid, uuid_parsed);
253 let uuid_parsed_hex = uuid_parsed.hex();
254 assert_eq!(uuid_hex, uuid_parsed_hex);
255 }
256
257 #[cfg(feature = "std")]
258 #[test]
259 fn test_display_uuid() {
260 println!("got uuid {}", UUID4::random());
261 }
262
263 #[cfg(feature = "std")]
264 #[test]
265 fn test_debug_uuid() {
266 println!("got uuid {:?}", UUID4::random());
267 }
268
269 #[cfg(feature = "std")]
270 #[test]
271 fn test_to_json() {
272 let id = UUID4::random();
273 let str = serde_json::to_string(&id).expect("should serialize fine");
274 assert_eq!(str, format!("\"{}\"", id.to_string()))
275 }
276
277 #[cfg(feature = "std")]
278 #[test]
279 fn test_from_json() {
280 let id = UUID4::random();
281 let json = format!("\"{}\"", id.to_string());
282 let deserialized: UUID4 = serde_json::from_str(json.as_str()).expect("should read fine");
283 assert_eq!(deserialized, id);
284 }
285
286 #[cfg(all(feature = "std", feature = "bench"))]
287 #[bench]
288 fn bench_parse_uuid4(b: &mut test::Bencher) {
289 let rand = UUID4::random();
290 let str = rand.to_string();
291 b.bytes = str.bytes().len() as u64;
292 b.iter(|| {
293 UUID4::parse(str.as_str()).expect("should parse fine")
294 })
295 }
296
297 #[cfg(all(feature = "std", feature = "bench"))]
298 #[bench]
299 fn bench_uuid4_to_str(b: &mut test::Bencher) {
300 let rand = UUID4::random();
301 b.bytes = 128;
302 b.iter(|| {
303 rand.to_string()
304 })
305 }
306}