1use core::fmt;
15
16use serde::{
17 Deserialize, Deserializer, Serialize, Serializer,
18 de::{self, Visitor},
19};
20
21use crate::Uuid;
22
23impl Serialize for Uuid {
24 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
25 serializer.collect_str(self)
26 }
27}
28
29struct UuidVisitor;
30
31impl<'de> Visitor<'de> for UuidVisitor {
32 type Value = Uuid;
33
34 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 f.write_str("a UUID as a 36-character hex string or 16-byte buffer")
36 }
37
38 fn visit_str<E: de::Error>(self, v: &str) -> Result<Uuid, E> {
39 Uuid::parse(v).map_err(de::Error::custom)
40 }
41
42 fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Uuid, E> {
43 match v.len() {
46 16 => Uuid::from_slice(v).map_err(de::Error::custom),
47 36 => Uuid::parse(v).map_err(de::Error::custom),
48 _ => Err(de::Error::custom("expected 16 bytes (binary) or 36 bytes (hex string)")),
49 }
50 }
51}
52
53impl<'de> Deserialize<'de> for Uuid {
54 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Uuid, D::Error> {
55 deserializer.deserialize_bytes(UuidVisitor)
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use serde::de::{
62 IntoDeserializer,
63 value::{BorrowedBytesDeserializer, StrDeserializer},
64 };
65
66 use super::*;
67
68 const S: &str = "f47ac10b-58cc-4372-a567-0e02b2c3d479";
69
70 #[test]
71 fn round_trip() {
72 let uuid = Uuid::parse(S).unwrap();
73 let json = serde_json::to_string(&uuid).unwrap();
74 assert_eq!(json, format!("\"{S}\""));
75 let got: Uuid = serde_json::from_str(&json).unwrap();
76 assert_eq!(got, uuid);
77 }
78
79 #[test]
80 fn deserialize_from_str() {
81 let json = format!("\"{S}\"");
82 let uuid: Uuid = serde_json::from_str(&json).unwrap();
83 assert_eq!(uuid.to_string(), S);
84 }
85
86 #[test]
87 fn deserialize_uppercase_normalizes_to_lowercase() {
88 let json = format!("\"{}\"", S.to_uppercase());
89 let uuid: Uuid = serde_json::from_str(&json).unwrap();
90 assert_eq!(uuid.to_string(), S);
91 }
92
93 #[test]
94 fn deserialize_array_rejected() {
95 let json = "[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]";
96 let result: Result<Uuid, _> = serde_json::from_str(json);
97 assert!(result.is_err());
98 }
99
100 #[test]
101 fn deserialize_errors() {
102 for bad in [
103 "\"nope\"",
104 "\"f47ac10b-58cc-4372-a567-0e02b2c3d47\"",
105 "\"00112233-4455-6677-8899-aabbccddeeff0\"",
106 "123",
107 "null",
108 "true",
109 "{}",
110 ] {
111 let result: Result<Uuid, _> = serde_json::from_str(bad);
112 assert!(result.is_err(), "should reject {bad}, got {result:?}");
113 }
114 }
115
116 #[test]
117 fn nil_round_trip() {
118 let uuid = Uuid::nil();
119 let json = serde_json::to_string(&uuid).unwrap();
120 assert_eq!(json, "\"00000000-0000-0000-0000-000000000000\"");
121 let got: Uuid = serde_json::from_str(&json).unwrap();
122 assert_eq!(got, uuid);
123 }
124
125 #[test]
126 fn max_round_trip() {
127 let uuid = Uuid::max();
128 let json = serde_json::to_string(&uuid).unwrap();
129 assert_eq!(json, "\"ffffffff-ffff-ffff-ffff-ffffffffffff\"");
130 let got: Uuid = serde_json::from_str(&json).unwrap();
131 assert_eq!(got, uuid);
132 }
133
134 #[test]
135 fn deserialize_binary_16_byte_buffer() {
136 let uuid = Uuid::parse(S).unwrap();
137 let bytes = uuid.as_bytes();
138 let de: BorrowedBytesDeserializer<'_, serde::de::value::Error> = BorrowedBytesDeserializer::new(&bytes);
139 assert_eq!(Uuid::deserialize(de).unwrap(), uuid);
140 }
141
142 #[test]
143 fn deserialize_36_byte_hex_buffer() {
144 let uuid = Uuid::parse(S).unwrap();
145 let de: BorrowedBytesDeserializer<'_, serde::de::value::Error> = BorrowedBytesDeserializer::new(S.as_bytes());
146 assert_eq!(Uuid::deserialize(de).unwrap(), uuid);
147 }
148
149 #[test]
150 fn deserialize_wrong_lengths_error() {
151 for len in [0usize, 1, 15, 17, 35, 37, 64] {
152 let buf = vec![0u8; len];
153 let de: BorrowedBytesDeserializer<'_, serde::de::value::Error> = BorrowedBytesDeserializer::new(&buf);
154 assert!(Uuid::deserialize(de).is_err(), "length {len} must error");
155 }
156 }
157
158 #[test]
159 fn deserialize_visit_str_directly() {
160 let de: StrDeserializer<'_, serde::de::value::Error> = S.into_deserializer();
161 assert_eq!(Uuid::deserialize(de).unwrap(), Uuid::parse(S).unwrap());
162
163 let de: StrDeserializer<'_, serde::de::value::Error> = "zzz".into_deserializer();
164 assert!(Uuid::deserialize(de).is_err());
165 }
166
167 #[test]
168 fn serialize_does_not_allocate_extra() {
169 let uuid = Uuid::parse(S).unwrap();
170 let (_, c) = alloc_counter::measure(|| serde_json::to_writer(std::io::sink(), &uuid).unwrap());
172 assert_eq!(c.calls, 0, "to_writer must not allocate");
173
174 let (_, c) = alloc_counter::measure(|| serde_json::to_string(&uuid).unwrap());
176 assert!(
177 c.calls <= 1,
178 "to_string should allocate at most the output string, got {}",
179 c.calls
180 );
181 }
182
183 #[test]
184 fn serialize_matches_display() {
185 let uuid = Uuid::parse(S).unwrap();
186 assert_eq!(serde_json::to_string(&uuid).unwrap(), format!("\"{uuid}\""));
187 }
188}