use facet::Facet;
use facet_testhelpers::test;
use facet_xml as xml;
#[test]
fn struct_variant() {
#[derive(Debug, PartialEq, Facet)]
#[repr(u8)]
enum Shape {
Circle { radius: f64 },
}
let result: Shape = facet_xml::from_str("<circle><radius>5.0</radius></circle>").unwrap();
assert_eq!(result, Shape::Circle { radius: 5.0 });
}
#[test]
fn newtype_variant() {
#[derive(Debug, PartialEq, Facet)]
#[repr(u8)]
enum Message {
Text(String),
}
let result: Message = facet_xml::from_str("<text>hello</text>").unwrap();
assert_eq!(result, Message::Text("hello".into()));
}
#[test]
fn unit_variant() {
#[derive(Debug, PartialEq, Facet)]
#[repr(u8)]
enum Status {
Active,
Inactive,
}
let result: Status = facet_xml::from_str("<active/>").unwrap();
assert_eq!(result, Status::Active);
let result: Status = facet_xml::from_str("<inactive/>").unwrap();
assert_eq!(result, Status::Inactive);
}
#[test]
fn multiple_struct_variants() {
#[derive(Debug, PartialEq, Facet)]
#[repr(u8)]
enum Shape {
Circle { radius: f64 },
Rect { width: f64, height: f64 },
}
let result: Shape = facet_xml::from_str("<circle><radius>5.0</radius></circle>").unwrap();
assert_eq!(result, Shape::Circle { radius: 5.0 });
let result: Shape =
facet_xml::from_str("<rect><width>10.0</width><height>20.0</height></rect>").unwrap();
assert_eq!(
result,
Shape::Rect {
width: 10.0,
height: 20.0
}
);
}
#[test]
fn variant_with_rename() {
#[derive(Debug, PartialEq, Facet)]
#[repr(u8)]
enum Event {
#[facet(rename = "mouse-click")]
MouseClick { x: i32, y: i32 },
}
let result: Event =
facet_xml::from_str("<mouse-click><x>100</x><y>200</y></mouse-click>").unwrap();
assert_eq!(result, Event::MouseClick { x: 100, y: 200 });
}
#[test]
fn vec_of_enum_variants() {
#[derive(Debug, PartialEq, Facet)]
#[repr(u8)]
enum Shape {
Circle { radius: f64 },
Rect { width: f64, height: f64 },
}
#[derive(Debug, PartialEq, Facet)]
struct Drawing {
#[facet(flatten, default)]
shapes: Vec<Shape>,
}
let result: Drawing = facet_xml::from_str(
"<drawing><circle><radius>5.0</radius></circle><rect><width>10.0</width><height>20.0</height></rect></drawing>",
)
.unwrap();
assert_eq!(result.shapes.len(), 2);
assert_eq!(result.shapes[0], Shape::Circle { radius: 5.0 });
assert_eq!(
result.shapes[1],
Shape::Rect {
width: 10.0,
height: 20.0
}
);
}
#[test]
fn enum_as_attribute_value() {
#[derive(Debug, Clone, Copy, PartialEq, Facet)]
#[repr(C)]
enum Name {
#[facet(rename = "voltage")]
Voltage,
#[facet(rename = "value")]
Value,
#[facet(rename = "adValue")]
AdValue,
}
#[derive(Debug, Clone, PartialEq, Facet)]
#[facet(rename = "Property")]
struct XmlScaleRangeProperty {
#[facet(xml::attribute)]
value: f32,
#[facet(xml::attribute)]
name: Name,
}
let property: XmlScaleRangeProperty =
facet_xml::from_str(r#"<Property value="5" name="voltage" />"#).unwrap();
assert_eq!(property.value, 5.0);
assert!(matches!(property.name, Name::Voltage));
let property2: XmlScaleRangeProperty =
facet_xml::from_str(r#"<Property value="10" name="adValue" />"#).unwrap();
assert_eq!(property2.value, 10.0);
assert!(matches!(property2.name, Name::AdValue));
}
#[test]
fn enum_as_attribute_value_with_option() {
#[derive(Debug, Clone, Copy, PartialEq, Facet)]
#[repr(C)]
enum Priority {
#[facet(rename = "low")]
Low,
#[facet(rename = "medium")]
Medium,
#[facet(rename = "high")]
High,
}
#[derive(Debug, Clone, PartialEq, Facet)]
#[facet(rename = "Task")]
struct Task {
#[facet(xml::attribute)]
name: String,
#[facet(xml::attribute)]
priority: Option<Priority>,
}
let task: Task = facet_xml::from_str(r#"<Task name="test" priority="high" />"#).unwrap();
assert_eq!(task.name, "test");
assert_eq!(task.priority, Some(Priority::High));
let task2: Task = facet_xml::from_str(r#"<Task name="test2" />"#).unwrap();
assert_eq!(task2.name, "test2");
assert_eq!(task2.priority, None);
}
#[test]
fn enum_as_attribute_value_roundtrip() {
#[derive(Debug, Clone, Copy, PartialEq, Facet)]
#[repr(C)]
enum Status {
#[facet(rename = "active")]
Active,
#[facet(rename = "inactive")]
Inactive,
}
#[derive(Debug, Clone, PartialEq, Facet)]
#[facet(rename = "Item")]
struct Item {
#[facet(xml::attribute)]
id: u32,
#[facet(xml::attribute)]
status: Status,
}
let item = Item {
id: 42,
status: Status::Active,
};
let xml = facet_xml::to_string(&item).unwrap();
assert!(xml.contains(r#"status="active""#), "xml was: {}", xml);
let parsed: Item = facet_xml::from_str(&xml).unwrap();
assert_eq!(parsed.id, 42);
assert_eq!(parsed.status, Status::Active);
}
#[test]
fn enum_as_attribute_without_rename() {
#[derive(Debug, Clone, Copy, PartialEq, Facet)]
#[repr(C)]
enum MyStatus {
IsActive,
IsInactive,
}
#[derive(Debug, Clone, PartialEq, Facet)]
#[facet(rename = "Item")]
struct Item {
#[facet(xml::attribute)]
status: MyStatus,
}
let item = Item {
status: MyStatus::IsActive,
};
let xml = facet_xml::to_string(&item).unwrap();
assert!(xml.contains(r#"status="isActive""#), "xml was: {}", xml);
let parsed: Item = facet_xml::from_str(&xml).unwrap();
assert_eq!(parsed.status, MyStatus::IsActive);
}
#[test]
fn enum_variant_fields_with_attributes() {
#[derive(Debug, PartialEq, Facet)]
#[repr(C)]
enum Foo {
#[facet(rename = "Foo")]
Variant {
#[facet(xml::attribute)]
name: String,
#[facet(xml::attribute)]
value: String,
},
}
let x: Foo = facet_xml::from_str(r#"<Foo name="bar" value="baz" />"#).unwrap();
assert_eq!(
x,
Foo::Variant {
name: "bar".to_string(),
value: "baz".to_string()
}
);
let y = facet_xml::to_string_pretty(&x).unwrap();
assert!(y.contains(r#"name="bar""#), "name should be an attribute");
assert!(y.contains(r#"value="baz""#), "value should be an attribute");
assert!(!y.contains("<name>"), "name should not be a child element");
assert!(
!y.contains("<value>"),
"value should not be a child element"
);
}
#[test]
fn enum_variant_mixed_attributes_and_elements() {
#[derive(Debug, PartialEq, Facet)]
#[non_exhaustive]
#[repr(C)]
enum XmlParameter {
#[facet(rename = "Property")]
Property {
#[facet(xml::attribute)]
name: String,
#[facet(xml::attribute)]
value: String,
},
#[facet(rename = "Array")]
Array {
#[facet(xml::attribute)]
name: String,
#[facet(flatten)]
value: Vec<XmlParameter>,
},
#[facet(rename = "Group")]
Group {
#[facet(xml::attribute)]
name: String,
#[facet(flatten)]
value: Vec<XmlParameter>,
},
}
let p: XmlParameter = facet_xml::from_str(
r#"
<Array name="State_Text">
<Property name="State_Text" value="A" />
<Property name="State_Text" value="B Text" />
<Property name="State_Text" value="C text" />
<Group name="Voltage_Range">
<Property name="Voltage_Range" value="foo" />
</Group>
</Array>
"#,
)
.unwrap();
let serialized = facet_xml::to_string_pretty(&p).unwrap();
assert!(
serialized.contains(r#"<Array name="State_Text">"#),
"Array should have name attribute"
);
assert!(
serialized.contains(r#"<Property name="State_Text" value="A""#),
"Property should have attributes"
);
assert!(
serialized.contains(r#"<Group name="Voltage_Range">"#),
"Group should have name attribute"
);
let roundtrip: XmlParameter = facet_xml::from_str(&serialized).unwrap();
let original_serialized = facet_xml::to_string_pretty(&p).unwrap();
let roundtrip_serialized = facet_xml::to_string_pretty(&roundtrip).unwrap();
assert_eq!(
original_serialized, roundtrip_serialized,
"Roundtrip should produce identical XML"
);
}
#[test]
fn enum_rename_all_with_variant_attributes() {
#[derive(Debug, PartialEq, Facet)]
#[facet(rename_all = "PascalCase")]
#[repr(C)]
#[allow(clippy::enum_variant_names)] enum MyTag {
TagFoo {
#[facet(xml::attribute)]
name: String,
#[facet(xml::attribute)]
value: u32,
},
TagBar {
#[facet(xml::attribute)]
name: String,
#[facet(xml::attribute, rename = "bar_value")]
bar_value: String,
},
TagBaz {
#[facet(xml::attribute)]
name: String,
#[facet(xml::attribute)]
baz: Option<String>,
},
}
#[derive(Debug, PartialEq, Facet)]
#[facet(rename = "Object")]
struct Container {
#[facet(xml::attribute)]
id: String,
#[facet(xml::elements)]
elements: Vec<MyTag>,
}
#[derive(Debug, PartialEq, Facet)]
#[facet(rename = "Outer")]
struct Outer {
#[facet(xml::elements)]
objects: Vec<Container>,
}
let result: Outer = facet_xml::from_str(
r#"
<Outer>
<Object id="first">
<TagFoo Name="Foo" Value="420" />
<TagBar Name="Bar" bar_value="Bar Value" />
<TagBaz Name="BazNotUsableAsAtag" />
<TagBaz Name="BazNotUsableAsAtag" Baz="bazbazbaz" />
</Object>
<Object id="second">
</Object>
</Outer>
"#,
)
.unwrap();
assert_eq!(result.objects.len(), 2);
let first = &result.objects[0];
assert_eq!(first.id, "first");
assert_eq!(first.elements.len(), 4);
assert_eq!(
first.elements[0],
MyTag::TagFoo {
name: "Foo".into(),
value: 420
}
);
assert_eq!(
first.elements[1],
MyTag::TagBar {
name: "Bar".into(),
bar_value: "Bar Value".into()
}
);
assert_eq!(
first.elements[2],
MyTag::TagBaz {
name: "BazNotUsableAsAtag".into(),
baz: None
}
);
assert_eq!(
first.elements[3],
MyTag::TagBaz {
name: "BazNotUsableAsAtag".into(),
baz: Some("bazbazbaz".into())
}
);
let second = &result.objects[1];
assert_eq!(second.id, "second");
assert_eq!(second.elements.len(), 0);
}