use std::collections::HashMap;
use facet::Facet;
use facet_testhelpers::test;
#[derive(Clone, Debug, Facet, PartialEq)]
#[facet(tag = "type")]
#[repr(C)]
pub enum TaggedEnum {
VariantA { value: f64 },
VariantB { x: f64, y: f64 },
}
#[derive(Clone, Debug, Facet, PartialEq)]
pub struct FlattenedItem {
#[facet(flatten)]
pub inner: TaggedEnum,
pub name: String,
}
#[test]
fn vec_flattened_tagged_enum_fields_in_order() {
let json = r#"[
{"type": "VariantA", "value": 1.0, "name": "first"},
{"type": "VariantB", "x": 2.0, "y": 3.0, "name": "second"}
]"#;
let items: Vec<FlattenedItem> =
facet_json::from_str(json).expect("fields in order should work");
assert_eq!(items.len(), 2);
assert_eq!(
items[0],
FlattenedItem {
inner: TaggedEnum::VariantA { value: 1.0 },
name: "first".into()
}
);
assert_eq!(
items[1],
FlattenedItem {
inner: TaggedEnum::VariantB { x: 2.0, y: 3.0 },
name: "second".into()
}
);
}
#[test]
fn vec_flattened_tagged_enum_fields_out_of_order() {
let json = r#"[
{"value": 1.0, "type": "VariantA", "name": "first"},
{"x": 2.0, "name": "second", "type": "VariantB", "y": 3.0}
]"#;
let items: Vec<FlattenedItem> =
facet_json::from_str(json).expect("fields out of order should work");
assert_eq!(items.len(), 2);
assert_eq!(
items[0],
FlattenedItem {
inner: TaggedEnum::VariantA { value: 1.0 },
name: "first".into()
}
);
assert_eq!(
items[1],
FlattenedItem {
inner: TaggedEnum::VariantB { x: 2.0, y: 3.0 },
name: "second".into()
}
);
}
#[test]
fn vec_flattened_tagged_enum_tag_last() {
let json = r#"[
{"value": 1.0, "name": "first", "type": "VariantA"},
{"x": 2.0, "y": 3.0, "name": "second", "type": "VariantB"}
]"#;
let items: Vec<FlattenedItem> = facet_json::from_str(json).expect("tag last should work");
assert_eq!(items.len(), 2);
}
#[derive(Clone, Debug, Facet, PartialEq)]
pub struct NestedContainer {
pub groups: HashMap<String, Vec<FlattenedItem>>,
}
#[test]
fn hashmap_of_vec_flattened_tagged_enum() {
let json = r#"{
"groups": {
"group1": [
{"type": "VariantA", "value": 1.0, "name": "a1"},
{"x": 2.0, "type": "VariantB", "y": 3.0, "name": "b1"}
],
"group2": [
{"name": "a2", "value": 4.0, "type": "VariantA"}
]
}
}"#;
let container: NestedContainer =
facet_json::from_str(json).expect("nested HashMap<String, Vec<...>> should work");
assert_eq!(container.groups.len(), 2);
assert_eq!(container.groups["group1"].len(), 2);
assert_eq!(container.groups["group2"].len(), 1);
}
#[derive(Clone, Debug, Facet, PartialEq)]
pub struct Matrix {
pub rows: Vec<Vec<FlattenedItem>>,
}
#[test]
fn nested_vec_of_vec_flattened_tagged_enum() {
let json = r#"{
"rows": [
[
{"type": "VariantA", "value": 1.0, "name": "r0c0"},
{"x": 2.0, "type": "VariantB", "y": 3.0, "name": "r0c1"}
],
[
{"name": "r1c0", "value": 4.0, "type": "VariantA"}
]
]
}"#;
let matrix: Matrix = facet_json::from_str(json).expect("Vec<Vec<...>> should work");
assert_eq!(matrix.rows.len(), 2);
assert_eq!(matrix.rows[0].len(), 2);
assert_eq!(matrix.rows[1].len(), 1);
}
#[test]
fn large_vec_flattened_tagged_enum() {
let mut elements = Vec::new();
for i in 0..100 {
if i % 3 == 0 {
elements.push(format!(
r#"{{"type": "VariantA", "value": {}.0, "name": "item{}"}}"#,
i, i
));
} else if i % 3 == 1 {
elements.push(format!(
r#"{{"x": {}.0, "type": "VariantB", "y": {}.0, "name": "item{}"}}"#,
i,
i + 1,
i
));
} else {
elements.push(format!(
r#"{{"value": {}.0, "name": "item{}", "type": "VariantA"}}"#,
i, i
));
}
}
let json = format!("[{}]", elements.join(","));
let items: Vec<FlattenedItem> = facet_json::from_str(&json).expect("large Vec should work");
assert_eq!(items.len(), 100);
assert_eq!(
items[0],
FlattenedItem {
inner: TaggedEnum::VariantA { value: 0.0 },
name: "item0".into()
}
);
assert_eq!(
items[1],
FlattenedItem {
inner: TaggedEnum::VariantB { x: 1.0, y: 2.0 },
name: "item1".into()
}
);
}
#[derive(Clone, Debug, Facet, PartialEq)]
pub struct ItemWithOptionalFields {
#[facet(flatten)]
pub inner: TaggedEnum,
pub optional_name: Option<String>,
pub optional_count: Option<i32>,
}
#[test]
fn vec_with_optional_fields() {
let json = r#"[
{"type": "VariantA", "value": 1.0, "optional_name": "has_name"},
{"x": 2.0, "type": "VariantB", "y": 3.0, "optional_count": 42},
{"value": 4.0, "type": "VariantA"}
]"#;
let items: Vec<ItemWithOptionalFields> =
facet_json::from_str(json).expect("Vec with optional fields should work");
assert_eq!(items.len(), 3);
assert_eq!(items[0].optional_name, Some("has_name".into()));
assert_eq!(items[0].optional_count, None);
assert_eq!(items[1].optional_name, None);
assert_eq!(items[1].optional_count, Some(42));
assert_eq!(items[2].optional_name, None);
assert_eq!(items[2].optional_count, None);
}
#[derive(Clone, Debug, Facet, PartialEq)]
#[facet(tag = "meta_type")]
#[repr(C)]
pub enum MetaEnum {
MetaA { meta_value: String },
MetaB { meta_x: i32, meta_y: i32 },
}
#[derive(Clone, Debug, Facet, PartialEq)]
pub struct ItemWithTwoFlattened {
#[facet(flatten)]
pub data: TaggedEnum,
#[facet(flatten)]
pub meta: MetaEnum,
pub id: String,
}
#[test]
fn vec_with_two_flattened_enums() {
let json = r#"[
{
"type": "VariantA", "value": 1.0,
"meta_type": "MetaA", "meta_value": "info1",
"id": "first"
},
{
"meta_x": 10, "type": "VariantB", "x": 2.0,
"id": "second", "meta_type": "MetaB", "y": 3.0, "meta_y": 20
}
]"#;
let items: Vec<ItemWithTwoFlattened> =
facet_json::from_str(json).expect("Vec with two flattened enums should work");
assert_eq!(items.len(), 2);
assert_eq!(items[0].id, "first");
assert_eq!(items[1].id, "second");
assert!(matches!(items[0].data, TaggedEnum::VariantA { .. }));
assert!(matches!(items[0].meta, MetaEnum::MetaA { .. }));
assert!(matches!(items[1].data, TaggedEnum::VariantB { .. }));
assert!(matches!(items[1].meta, MetaEnum::MetaB { .. }));
}
#[derive(Clone, Debug, Facet, PartialEq)]
pub struct Wrapper {
pub items: Option<Vec<FlattenedItem>>,
}
#[derive(Clone, Debug, Facet, PartialEq)]
pub struct DeepNested {
pub wrappers: Vec<Wrapper>,
}
#[test]
fn deeply_nested_vec_option_vec() {
let json = r#"{
"wrappers": [
{"items": [{"type": "VariantA", "value": 1.0, "name": "w0i0"}]},
{"items": null},
{"items": [
{"x": 2.0, "type": "VariantB", "y": 3.0, "name": "w2i0"},
{"value": 4.0, "name": "w2i1", "type": "VariantA"}
]}
]
}"#;
let nested: DeepNested =
facet_json::from_str(json).expect("deeply nested structure should work");
assert_eq!(nested.wrappers.len(), 3);
assert_eq!(nested.wrappers[0].items.as_ref().unwrap().len(), 1);
assert!(nested.wrappers[1].items.is_none());
assert_eq!(nested.wrappers[2].items.as_ref().unwrap().len(), 2);
}
#[derive(Clone, Debug, Facet, PartialEq)]
#[repr(C)]
pub enum ExternallyTagged {
Alpha { value: f64 },
Beta { x: f64, y: f64 },
}
#[derive(Clone, Debug, Facet, PartialEq)]
pub struct ItemWithExternallyTagged {
#[facet(flatten)]
pub data: ExternallyTagged,
pub name: String,
}
#[test]
fn vec_externally_tagged_enum() {
let json = r#"[
{"Alpha": {"value": 1.0}, "name": "first"},
{"Beta": {"x": 2.0, "y": 3.0}, "name": "second"}
]"#;
let items: Vec<ItemWithExternallyTagged> =
facet_json::from_str(json).expect("externally tagged enum in Vec should work");
assert_eq!(items.len(), 2);
}
#[test]
fn empty_vec() {
let json = r#"[]"#;
let items: Vec<FlattenedItem> = facet_json::from_str(json).expect("empty Vec should work");
assert!(items.is_empty());
}
#[test]
fn single_element_vec_tag_last() {
let json = r#"[{"value": 1.0, "name": "only", "type": "VariantA"}]"#;
let items: Vec<FlattenedItem> =
facet_json::from_str(json).expect("single element Vec should work");
assert_eq!(items.len(), 1);
}
#[test]
fn roundtrip_vec_flattened_tagged_enum() {
let original = vec![
FlattenedItem {
inner: TaggedEnum::VariantA { value: 1.0 },
name: "first".into(),
},
FlattenedItem {
inner: TaggedEnum::VariantB { x: 2.0, y: 3.0 },
name: "second".into(),
},
];
let json = facet_json::to_string(&original).expect("serialization should work");
let deserialized: Vec<FlattenedItem> =
facet_json::from_str(&json).expect("deserialization should work");
assert_eq!(original, deserialized);
}