use serde::{de::IntoDeserializer, Deserialize, Serialize};
use serde_reflection::{
ContainerFormat, Error, Format, FormatHolder, Named, Samples, Tracer, TracerConfig, Value,
VariantFormat,
};
use std::collections::BTreeMap;
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
enum E {
Unit,
Newtype(u16),
Tuple(u16, Option<bool>),
Struct { a: u32 },
NewTupleArray((u16, u16, u16)),
}
fn test_variant(tracer: &mut Tracer, expr: E, expected_value: Value) {
let mut samples = Samples::new();
let (format, value) = tracer.trace_value(&mut samples, &expr).unwrap();
assert_eq!(format, Format::TypeName("E".into()));
assert_eq!(value, expected_value);
assert_eq!(expr, E::deserialize(value.into_deserializer()).unwrap());
}
#[test]
fn test_tracers() {
let mut tracer = Tracer::new(TracerConfig::default());
test_variant(
&mut tracer,
E::Unit,
Value::Variant(0, Box::new(Value::Unit)),
);
test_variant(
&mut tracer,
E::Newtype(1),
Value::Variant(1, Box::new(Value::U16(1))),
);
test_variant(
&mut tracer,
E::Tuple(1, Some(true)),
Value::Variant(
2,
Box::new(Value::Seq(vec![
Value::U16(1),
Value::Option(Some(Box::new(Value::Bool(true)))),
])),
),
);
test_variant(
&mut tracer,
E::Struct { a: 1 },
Value::Variant(3, Box::new(Value::Seq(vec![Value::U32(1)]))),
);
test_variant(
&mut tracer,
E::NewTupleArray((1, 2, 3)),
Value::Variant(
4,
Box::new(Value::Seq(vec![
Value::U16(1),
Value::U16(2),
Value::U16(3),
])),
),
);
let registry = tracer.registry().unwrap();
let format = registry.get("E").unwrap();
let variants = match format {
ContainerFormat::Enum(variants) => variants,
_ => {
unreachable!();
}
};
assert_eq!(variants.len(), 5);
assert_eq!(variants.get(&0).unwrap().name, "Unit");
assert_eq!(variants.get(&1).unwrap().name, "Newtype");
assert_eq!(variants.get(&2).unwrap().name, "Tuple");
assert_eq!(variants.get(&3).unwrap().name, "Struct");
assert_eq!(variants.get(&4).unwrap().name, "NewTupleArray");
assert_eq!(variants.get(&0).unwrap().value, VariantFormat::Unit);
assert_eq!(
variants.get(&1).unwrap().value,
VariantFormat::NewType(Box::new(Format::U16))
);
assert_eq!(
variants.get(&2).unwrap().value,
VariantFormat::Tuple(vec![Format::U16, Format::Option(Box::new(Format::Bool))])
);
assert_eq!(
variants.get(&3).unwrap().value,
VariantFormat::Struct(vec![Named {
name: "a".into(),
value: Format::U32
}])
);
assert_eq!(
variants.get(&4).unwrap().value,
VariantFormat::NewType(Box::new(Format::TupleArray {
content: Box::new(Format::U16),
size: 3
})),
);
let data = serde_json::to_string_pretty(format).unwrap();
let format2 = serde_json::from_str::<ContainerFormat>(&data).unwrap();
assert_eq!(*format, format2);
let data = serde_yaml::to_string(format).unwrap();
let format3 = serde_yaml::from_str::<ContainerFormat>(&data).unwrap();
assert_eq!(*format, format3);
let data = bincode::serialize(format).unwrap();
let format4 = bincode::deserialize(&data).unwrap();
assert_eq!(*format, format4);
let samples = Samples::new();
let mut tracer = Tracer::new(TracerConfig::default());
let (mut ident, samples) = tracer.trace_type::<E>(&samples).unwrap();
ident.normalize().unwrap();
assert_eq!(ident, Format::TypeName("E".into()));
assert_eq!(tracer.registry().unwrap().get("E").unwrap(), format);
assert_eq!(
samples,
vec![
E::Unit,
E::Newtype(0),
E::Tuple(0, Some(false)),
E::Struct { a: 0 },
E::NewTupleArray((0, 0, 0))
]
);
}
#[derive(Serialize, PartialEq, Eq, Debug, Clone)]
struct Name(String);
impl<'de> Deserialize<'de> for Name {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(rename = "Name")]
struct InternalValue(String);
let value = InternalValue::deserialize(deserializer)?.0;
if value.len() >= 2 && value.chars().all(char::is_alphabetic) {
Ok(Name(value))
} else {
Err(<D::Error as ::serde::de::Error>::custom(format!(
"Invalid name {}",
value
)))
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
enum Person {
NickName(Name),
FullName { first: Name, last: Name },
}
#[test]
fn test_trace_deserialization_with_custom_invariants() {
let mut samples = Samples::new();
let mut tracer = Tracer::new(TracerConfig::default());
assert_eq!(
tracer.trace_type::<Person>(&samples).unwrap_err(),
Error::Custom(format!(
"Failed to deserialize value: \"Invalid name {}\"",
""
)),
);
let bob = Name("Bob".into());
let (format, value) = tracer.trace_value(&mut samples, &bob).unwrap();
assert_eq!(format, Format::TypeName("Name".into()));
assert_eq!(value, Value::Str("Bob".into()));
assert_eq!(samples.value("Name"), Some(&value));
let (format, samples) = tracer.trace_type::<Person>(&samples).unwrap();
assert_eq!(format, Format::TypeName("Person".into()));
assert_eq!(
samples,
vec![
Person::NickName(bob.clone()),
Person::FullName {
first: bob.clone(),
last: bob,
}
]
);
let registry = tracer.registry().unwrap();
assert_eq!(
registry.get("Name").unwrap(),
&ContainerFormat::NewTypeStruct(Box::new(Format::Str))
);
let mut variants: BTreeMap<_, _> = BTreeMap::new();
variants.insert(
0,
Named {
name: "NickName".into(),
value: VariantFormat::NewType(Box::new(Format::TypeName("Name".into()))),
},
);
variants.insert(
1,
Named {
name: "FullName".into(),
value: VariantFormat::Struct(vec![
Named {
name: "first".into(),
value: Format::TypeName("Name".into()),
},
Named {
name: "last".into(),
value: Format::TypeName("Name".into()),
},
]),
},
);
assert_eq!(
registry.get("Person").unwrap(),
&ContainerFormat::Enum(variants)
);
}
mod foo {
#[derive(super::Serialize)]
pub struct A;
}
mod bar {
#[derive(super::Serialize)]
pub struct A(pub u32);
}
#[test]
fn test_name_clash_not_supported() {
let mut samples = Samples::new();
let mut tracer = Tracer::new(TracerConfig::default());
tracer.trace_value(&mut samples, &foo::A).unwrap();
assert!(tracer.trace_value(&mut samples, &foo::A).is_ok());
assert!(tracer.trace_value(&mut samples, &bar::A(0)).is_err());
}
#[test]
fn test_borrowed_slice() {
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
struct Borrowed<'a>(&'a [u8]);
let bytes = [1u8; 4];
let mut samples = Samples::new();
let mut tracer = Tracer::new(TracerConfig::default());
let (format, value) = tracer.trace_value(&mut samples, &Borrowed(&bytes)).unwrap();
assert_eq!(format, Format::TypeName("Borrowed".into()));
assert_eq!(value, Value::Seq(vec![Value::U8(1); 4]));
assert_eq!(
tracer.trace_type::<Borrowed>(&samples),
Err(Error::UnexpectedDeserializationFormat(
"Borrowed",
ContainerFormat::NewTypeStruct(Box::new(Format::Seq(Box::new(Format::U8)))),
"bytes"
)),
);
}
#[test]
fn test_borrowed_bytes() {
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
struct Borrowed<'a>(#[serde(with = "serde_bytes")] &'a [u8]);
let bytes = [1u8; 4];
let mut samples = Samples::new();
let mut tracer = Tracer::new(TracerConfig::default());
let (format, value) = tracer.trace_value(&mut samples, &Borrowed(&bytes)).unwrap();
assert_eq!(format, Format::TypeName("Borrowed".into()));
assert_eq!(value, Value::Bytes(bytes.to_vec()));
let (format, samples) = tracer.trace_type::<Borrowed>(&samples).unwrap();
assert_eq!(format, Format::TypeName("Borrowed".into()));
assert_eq!(samples, vec![Borrowed(&bytes),]);
let registry = tracer.registry().unwrap();
assert_eq!(
registry.get("Borrowed").unwrap(),
&ContainerFormat::NewTypeStruct(Box::new(Format::Bytes))
);
}
#[test]
fn test_trace_deserialization_with_recursive_types() {
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
enum List<T> {
Empty,
Cons { head: T, tail: Box<List<T>> },
}
let samples = Samples::new();
let mut tracer = Tracer::new(TracerConfig::default());
tracer.trace_type::<List<u32>>(&samples).unwrap();
let registry = tracer.registry().unwrap();
let variants = match registry.get("List").unwrap() {
ContainerFormat::Enum(variants) => variants,
_ => panic!("should be an enum"),
};
assert_eq!(variants.len(), 2);
assert_eq!(variants.get(&0).unwrap().name, "Empty");
assert_eq!(variants.get(&1).unwrap().name, "Cons");
}
#[test]
fn test_tracing_deserialization_for_lists() {
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
struct Node {
data: u64,
next: Option<Box<Node>>,
}
let samples = Samples::new();
let mut tracer = Tracer::new(TracerConfig::default());
tracer.trace_type::<Node>(&samples).unwrap();
let registry = tracer.registry().unwrap();
match registry.get("Node").unwrap() {
ContainerFormat::Struct(fields) => assert_eq!(fields.len(), 2),
_ => panic!("should be a struct"),
}
}
#[test]
fn test_tracing_deserialization_for_trees() {
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
struct Node {
data: u64,
children: Vec<Node>,
}
let samples = Samples::new();
let mut tracer = Tracer::new(TracerConfig::default());
tracer.trace_type::<Node>(&samples).unwrap();
let registry = tracer.registry().unwrap();
match registry.get("Node").unwrap() {
ContainerFormat::Struct(fields) => assert_eq!(fields.len(), 2),
_ => panic!("should be a struct"),
}
}
#[test]
fn test_mixed_tracing_for_multiple_enums() {
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
enum Foo {
A,
B(Bar),
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
enum Bar {
C,
D,
}
let mut samples = Samples::new();
let mut tracer = Tracer::new(TracerConfig::default());
tracer.trace_value(&mut samples, &Bar::D).unwrap();
tracer.trace_type::<Foo>(&samples).unwrap();
tracer.trace_type::<Bar>(&samples).unwrap();
let registry = tracer.registry().unwrap();
let variants = match registry.get("Foo").unwrap() {
ContainerFormat::Enum(variants) => variants,
_ => panic!("should be an enum"),
};
assert_eq!(variants.len(), 2);
}
#[test]
fn test_value_recording_for_structs() {
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
struct R(u32);
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
struct S {
a: u32,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
struct T(u32, u64);
let mut tracer = Tracer::new(
TracerConfig::default()
.record_samples_for_newtype_structs(false) .record_samples_for_tuple_structs(true)
.record_samples_for_structs(true),
);
let mut samples = Samples::new();
tracer.trace_value(&mut samples, &R(1)).unwrap();
tracer.trace_value(&mut samples, &S { a: 2 }).unwrap();
tracer.trace_value(&mut samples, &T(3, 4)).unwrap();
assert!(samples.value("R").is_none());
assert!(samples.value("S").is_some());
assert!(samples.value("T").is_some());
assert_eq!(tracer.trace_type_once::<R>(&samples).unwrap().1, R(0));
assert_eq!(tracer.trace_type_once::<S>(&samples).unwrap().1, S { a: 2 });
assert_eq!(tracer.trace_type_once::<T>(&samples).unwrap().1, T(3, 4));
}