use std::{fmt, str::FromStr};
use serde::{
de::{self, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use crate::{UUID, UUID_BYTES};
impl Serialize for UUID {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
struct UUIDVisitor;
impl<'de> Visitor<'de> for UUIDVisitor {
type Value = UUID;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a UUID as a string, 16-byte array, or u128")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
UUID::from_str(v).map_err(E::custom)
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
if v.len() != UUID_BYTES {
return Err(E::invalid_length(v.len(), &self));
}
let mut bytes = [0u8; UUID_BYTES];
bytes.copy_from_slice(v);
Ok(UUID { bytes })
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut bytes = [0u8; UUID_BYTES];
for (index, byte) in bytes.iter_mut().enumerate() {
*byte = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(index, &self))?;
}
Ok(UUID { bytes })
}
fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(UUID::from_bytes(v.to_be_bytes()))
}
}
impl<'de> Deserialize<'de> for UUID {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(UUIDVisitor)
}
}
#[cfg(all(test, feature = "serde"))]
mod tests {
#![allow(clippy::expect_used)]
use super::UUID;
const fn sample_uuid() -> UUID {
UUID {
bytes: [
0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
0x00, 0x00,
],
}
}
#[test]
fn serialize_to_canonical_string() {
let uuid = sample_uuid();
let s = serde_json::to_string(&uuid)
.expect("serde_json roundtrip should succeed for valid UUID values");
assert_eq!(s, "\"550e8400-e29b-41d4-a716-446655440000\"");
}
#[test]
fn deserialize_from_canonical_string() {
let s = "\"550e8400-e29b-41d4-a716-446655440000\"";
let uuid: UUID = serde_json::from_str(s)
.expect("serde_json roundtrip should succeed for valid UUID values");
assert_eq!(uuid, sample_uuid());
}
#[test]
fn deserialize_from_string_without_hyphens() {
let s = "\"550e8400e29b41d4a716446655440000\"";
let uuid: UUID = serde_json::from_str(s)
.expect("serde_json roundtrip should succeed for valid UUID values");
assert_eq!(uuid, sample_uuid());
}
#[test]
fn deserialize_from_string_with_braces() {
let s = "\"{550e8400-e29b-41d4-a716-446655440000}\"";
let uuid: UUID = serde_json::from_str(s)
.expect("serde_json roundtrip should succeed for valid UUID values");
assert_eq!(uuid, sample_uuid());
}
#[test]
fn deserialize_from_uppercase_string() {
let s = "\"550E8400-E29B-41D4-A716-446655440000\"";
let uuid: UUID = serde_json::from_str(s)
.expect("serde_json roundtrip should succeed for valid UUID values");
assert_eq!(uuid, sample_uuid());
}
#[test]
fn deserialize_from_bytes_array() {
let bytes = sample_uuid().bytes;
let json = serde_json::to_string(&bytes)
.expect("serde_json roundtrip should succeed for valid UUID values");
let uuid: UUID = serde_json::from_str(&json)
.expect("serde_json roundtrip should succeed for valid UUID values");
assert_eq!(uuid.bytes, bytes);
}
#[test]
fn round_trip_json() {
let uuid = UUID {
bytes: [
0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
0xde, 0xf0,
],
};
let s = serde_json::to_string(&uuid)
.expect("serde_json roundtrip should succeed for valid UUID values");
let back: UUID = serde_json::from_str(&s)
.expect("serde_json roundtrip should succeed for valid UUID values");
assert_eq!(uuid, back);
}
#[test]
fn invalid_string_too_short() {
let s = "\"1234\"";
let res: Result<UUID, _> = serde_json::from_str(s);
assert!(res.is_err());
}
#[test]
fn invalid_string_non_hex() {
let s = "\"550e8400-e29b-41d4-a716-44665544zzzz\"";
let res: Result<UUID, _> = serde_json::from_str(s);
assert!(res.is_err());
}
#[test]
fn invalid_bytes_wrong_length() {
let json = "[1,2,3]";
let res: Result<UUID, _> = serde_json::from_str(json);
assert!(res.is_err());
}
#[test]
fn invalid_sequence_wrong_length() {
let json = "[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]";
let res: Result<UUID, _> = serde_json::from_str(json);
assert!(res.is_err());
}
#[test]
fn invalid_negative_number() {
let json = "-42";
let res: Result<UUID, _> = serde_json::from_str(json);
assert!(res.is_err());
}
#[test]
fn round_trip_multiple_randoms() {
use rand::RngCore;
let mut rng = rand::rng();
for _ in 0..100 {
let mut bytes = [0u8; 16];
rng.fill_bytes(&mut bytes);
let uuid = UUID { bytes };
let s = serde_json::to_string(&uuid)
.expect("serde_json roundtrip should succeed for valid UUID values");
let back: UUID = serde_json::from_str(&s)
.expect("serde_json roundtrip should succeed for valid UUID values");
assert_eq!(uuid, back);
}
}
}