#[macro_use] extern crate yaserde; #[macro_use] extern crate yaserde_derive; use log::debug; use std::io::Read; use yaserde::de::from_str; use yaserde::YaDeserialize; fn init() { let _ = env_logger::builder().is_test(true).try_init(); } macro_rules! convert_and_validate { ($content: expr, $struct: tt, $model: expr) => { debug!("convert_and_validate @ {}:{}", file!(), line!()); let loaded: Result<$struct, String> = from_str($content); assert_eq!(loaded, Ok($model)); }; } #[test] fn de_basic() { init(); #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "book")] pub struct Book { author: String, title: String, } let content = "Antoine de Saint-ExupéryLittle prince"; convert_and_validate!( content, Book, Book { author: "Antoine de Saint-Exupéry".to_owned(), title: "Little prince".to_owned(), } ); let content = "Little princeAntoine de Saint-Exupéry"; convert_and_validate!( content, Book, Book { author: "Antoine de Saint-Exupéry".to_owned(), title: "Little prince".to_owned(), } ); } #[test] fn de_keyword() { init(); #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "book")] pub struct Book { #[yaserde(attribute = true, rename = "ref")] pub r#ref: String, } let content = ""; convert_and_validate!( content, Book, Book { r#ref: "978-1522968122".to_string() } ); } #[test] fn de_dash_param() { init(); #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "book")] pub struct Book { #[yaserde(rename = "author-release")] author: String, title: String, } let content = "Antoine de Saint-ExupéryLittle prince"; convert_and_validate!( content, Book, Book { author: "Antoine de Saint-Exupéry".to_owned(), title: "Little prince".to_owned(), } ); let content = "Little princeAntoine de Saint-Exupéry"; convert_and_validate!( content, Book, Book { author: "Antoine de Saint-Exupéry".to_owned(), title: "Little prince".to_owned(), } ); } #[test] fn de_multiple_segments() { init(); mod other_mod { #[derive(YaDeserialize, PartialEq, Debug)] pub struct Page { pub number: i32, pub text: String, } } #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "book")] pub struct Book { author: String, title: String, page: other_mod::Page, } let content = r#" Antoine de Saint-Exupéry Little prince 40 The Earth is not just an ordinary planet! "#; convert_and_validate!( content, Book, Book { author: "Antoine de Saint-Exupéry".to_owned(), title: "Little prince".to_owned(), page: other_mod::Page { number: 40, text: "The Earth is not just an ordinary planet!".to_owned(), }, } ); } #[test] fn de_list_of_items() { init(); #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "library")] pub struct Library { books: Vec, } let content = "Little PrinceHarry Potter"; convert_and_validate!( content, Library, Library { books: vec!["Little Prince".to_owned(), "Harry Potter".to_owned()], } ); #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "libraries")] pub struct Libraries { library: Vec, } let content = "Little PrinceHarry Potter"; convert_and_validate!( content, Libraries, Libraries { library: vec![ Library { books: vec!["Little Prince".to_owned()], }, Library { books: vec!["Harry Potter".to_owned()], }, ], } ); } #[test] fn de_attributes() { init(); #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "base")] pub struct XmlStruct { #[yaserde(attribute = true)] item: String, sub: SubStruct, } #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "sub")] pub struct SubStruct { #[yaserde(attribute = true)] subitem: String, } let content = ""; convert_and_validate!( content, XmlStruct, XmlStruct { item: "something".to_string(), sub: SubStruct { subitem: "sub-something".to_string(), }, } ); } #[test] fn de_attributes_custom_deserializer() { init(); mod other_mod { use super::*; use xml::reader::XmlEvent; #[derive(Debug, PartialEq)] pub struct Attributes { pub items: Vec, } impl YaDeserialize for Attributes { fn deserialize(reader: &mut yaserde::de::Deserializer) -> Result { loop { match reader.next_event()? { XmlEvent::StartElement { .. } => {} XmlEvent::Characters(ref text_content) => { let items: Vec = text_content .split(' ') .map(|item| item.to_owned()) .collect(); return Ok(Attributes { items }); } _ => { break; } } } Err("Unable to parse attribute".to_string()) } } } #[derive(YaDeserialize, PartialEq, Debug)] pub struct Struct { #[yaserde(attribute = true)] attr_option_string: Option, #[yaserde(attribute = true)] attr_option_struct: Option, } convert_and_validate!( r#""#, Struct, Struct { attr_option_string: None, attr_option_struct: None } ); convert_and_validate!( r#""#, Struct, Struct { attr_option_string: Some("some value".to_string()), attr_option_struct: Some(other_mod::Attributes { items: vec!["variant2".to_string(), "variant3".to_string()] }) } ); } #[test] fn de_attributes_complex() { init(); mod other_mod { #[derive(Default, YaDeserialize, PartialEq, Debug)] pub enum AttrEnum { #[default] #[yaserde(rename = "variant 1")] Variant1, #[yaserde(rename = "variant 2")] Variant2, } } #[derive(YaDeserialize, PartialEq, Debug)] pub struct Struct { #[yaserde(attribute = true)] attr_option_string: Option, #[yaserde(attribute = true)] attr_option_enum: Option, } convert_and_validate!( r#""#, Struct, Struct { attr_option_string: None, attr_option_enum: None } ); convert_and_validate!( r#""#, Struct, Struct { attr_option_string: Some("some value".to_string()), attr_option_enum: Some(other_mod::AttrEnum::Variant2) } ); } #[test] fn de_attributes_with_same_name_field() { init(); #[derive(YaDeserialize, PartialEq, Debug)] pub struct Struct { #[yaserde(attribute = true, rename = "content")] attribute: Option, content: Option, } convert_and_validate!( r#""#, Struct, Struct { attribute: None, content: None } ); convert_and_validate!( r#""#, Struct, Struct { attribute: Some("attribute".to_string()), content: None } ); convert_and_validate!( r#"Field"#, Struct, Struct { attribute: Some("attribute".to_string()), content: Some("Field".to_string()) } ); } #[test] fn de_rename() { init(); #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "base")] pub struct XmlStruct { #[yaserde(attribute = true, rename = "Item")] item: String, #[yaserde(rename = "sub")] sub_struct: SubStruct, #[yaserde(rename = "maj.min.bug")] with_dots: String, } #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "sub")] pub struct SubStruct { #[yaserde(attribute = true, rename = "sub_item")] subitem: String, } let content = "2.0.1"; convert_and_validate!( content, XmlStruct, XmlStruct { item: "something".to_string(), sub_struct: SubStruct { subitem: "sub_something".to_string(), }, with_dots: "2.0.1".into() } ); } #[test] fn de_text_content_with_attributes() { init(); #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "base")] pub struct XmlStruct { #[yaserde(attribute = true, rename = "Item")] item: String, #[yaserde(rename = "sub")] sub_struct: SubStruct, } #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "sub")] pub struct SubStruct { #[yaserde(attribute = true, rename = "sub_item")] subitem: String, #[yaserde(text = true)] text: String, } let content = "text_content"; convert_and_validate!( content, XmlStruct, XmlStruct { item: "something".to_string(), sub_struct: SubStruct { subitem: "sub_something".to_string(), text: "text_content".to_string(), }, } ); } #[test] fn de_text_attribute_on_optional_string() { #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "base")] pub struct XmlStruct { #[yaserde(text = true)] text: Option, } let model = XmlStruct { text: Some("Testing text".to_string()), }; let content = r#"Testing text"#; convert_and_validate!(content, XmlStruct, model); let model = XmlStruct { text: None }; let content = r#""#; convert_and_validate!(content, XmlStruct, model); } #[test] fn de_enum() { init(); #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "base")] pub struct XmlStruct { background: Color, } #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "base")] pub struct Colors { items: Vec, } #[derive(Default, YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "color")] pub enum Color { #[default] White, Black, } #[derive(YaDeserialize, PartialEq, Debug)] pub struct RGBColor { red: String, green: String, blue: String, } let content = "Black"; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Black, } ); let content = "BlackWhite"; convert_and_validate!( content, Colors, Colors { items: vec![Color::Black, Color::White], } ); } #[test] fn de_attribute_enum() { init(); #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "base")] pub struct XmlStruct { #[yaserde(attribute = true)] background: Color, } #[derive(Default, YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "color")] pub enum Color { #[default] White, Black, } let content = ""; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Black, } ); } #[test] fn de_complex_enum() { init(); #[derive(YaDeserialize, PartialEq, Debug)] pub struct XmlStruct { background: Color, } #[derive(YaDeserialize, PartialEq, Debug)] pub struct OtherStruct { fi: i32, se: i32, } #[derive(Default, YaDeserialize, PartialEq, Debug)] pub enum Color { #[default] White, Black(String), Orange(String), Red(i32), Green(OtherStruct), Yellow(Option), Brown(Option), Blue(Vec), Purple(Vec), Magenta(Vec), #[yaserde(rename = "NotSoCyan")] Cyan(Vec), #[yaserde(rename = "renamed.with.dots")] Dotted(u32), } let content = r#" text "#; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Black("text".to_owned()), } ); let content = r#" text "#; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Orange("text".to_owned()), } ); let content = r#" 56 "#; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Red(56), } ); let content = r#" 12 23 "#; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Green(OtherStruct { fi: 12, se: 23 }), } ); let content = r#" text "#; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Yellow(Some("text".to_owned())), } ); let content = r#" 12 23 "#; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Brown(Some(OtherStruct { fi: 12, se: 23 })), } ); let content = r#" abc def "#; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Blue(vec!["abc".to_owned(), "def".to_owned()]), } ); let content = r#" 12 43 "#; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Purple(vec![12, 43]), } ); let content = r#" 1223 6398 "#; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Magenta(vec![ OtherStruct { fi: 12, se: 23 }, OtherStruct { fi: 63, se: 98 } ]), } ); let content = r#" 1223 6398 "#; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Cyan(vec![ OtherStruct { fi: 12, se: 23 }, OtherStruct { fi: 63, se: 98 } ]) } ); let content = r#" 54 "#; convert_and_validate!( content, XmlStruct, XmlStruct { background: Color::Dotted(54) } ); } #[test] fn de_name_issue_21() { init(); #[derive(YaDeserialize, PartialEq, Debug)] #[yaserde(rename = "book")] pub struct Book { name: String, } let content = "Little prince"; convert_and_validate!( content, Book, Book { name: "Little prince".to_owned(), } ); } #[test] fn de_custom() { init(); #[derive(PartialEq, Debug, YaDeserialize)] struct Date { #[yaserde(rename = "Year")] year: i32, #[yaserde(rename = "Month")] month: i32, #[yaserde(rename = "Day")] day: Day, } #[derive(PartialEq, Debug)] struct Day { value: i32, } impl YaDeserialize for Day { fn deserialize(reader: &mut yaserde::de::Deserializer) -> Result { use std::str::FromStr; if let xml::reader::XmlEvent::StartElement { name, .. } = reader.peek()?.to_owned() { let expected_name = "Day".to_owned(); if name.local_name != expected_name { return Err(format!( "Wrong StartElement name: {}, expected: {}", name, expected_name )); } let _next = reader.next_event(); } else { return Err("StartElement missing".to_string()); } if let xml::reader::XmlEvent::Characters(text) = reader.peek()?.to_owned() { Ok(Day { value: 2 * i32::from_str(&text).unwrap(), }) } else { Err("Characters missing".to_string()) } } } let content = "20200111"; let model: Date = from_str(content).unwrap(); assert_eq!( model, Date { year: 2020, month: 1, day: Day { value: 11 * 2 } } ); } #[test] fn de_subitem_issue_12() { init(); #[derive(PartialEq, Debug, YaDeserialize)] pub struct Struct { id: i32, } convert_and_validate!( r#" 54 86 "#, Struct, Struct { id: 54 } ); } #[test] fn de_subitem_issue_12_with_sub() { init(); #[derive(PartialEq, Debug, YaDeserialize)] pub struct SubStruct { id: i32, } #[derive(PartialEq, Debug, YaDeserialize)] pub struct Struct { id: i32, #[yaserde(rename = "SubStruct")] sub: SubStruct, } convert_and_validate!( r#" 54 86 "#, Struct, Struct { id: 54, sub: SubStruct { id: 86 } } ); } #[test] fn de_subitem_issue_12_attributes() { init(); #[derive(PartialEq, Debug, YaDeserialize)] pub struct Struct { #[yaserde(attribute = true)] id: i32, } convert_and_validate!( r#" "#, Struct, Struct { id: 54 } ); } #[test] fn de_subitem_issue_12_attributes_with_sub() { init(); #[derive(PartialEq, Debug, YaDeserialize)] pub struct SubStruct { #[yaserde(attribute = true)] id: i32, } #[derive(PartialEq, Debug, YaDeserialize)] pub struct Struct { #[yaserde(attribute = true)] id: i32, sub1: SubStruct, sub2: SubStruct, } convert_and_validate!( r#" "#, Struct, Struct { id: 54, sub1: SubStruct { id: 63 }, sub2: SubStruct { id: 72 } } ); } #[test] fn de_same_field_name_sub() { init(); #[derive(PartialEq, Debug, YaDeserialize)] pub struct SubStruct { sub: Option, } #[derive(PartialEq, Debug, YaDeserialize)] pub struct Struct { sub: SubStruct, } convert_and_validate!( "", Struct, Struct { sub: SubStruct { sub: None } } ); convert_and_validate!( "42", Struct, Struct { sub: SubStruct { sub: Some(42) } } ); } #[test] fn de_same_field_name_sub_sub() { init(); #[derive(PartialEq, Debug, YaDeserialize)] pub struct SubSubStruct { sub: i32, } #[derive(PartialEq, Debug, YaDeserialize)] pub struct SubStruct { sub: Option, } #[derive(PartialEq, Debug, YaDeserialize)] pub struct Struct { sub: SubStruct, } convert_and_validate!( "", Struct, Struct { sub: SubStruct { sub: None } } ); convert_and_validate!( " 42 ", Struct, Struct { sub: SubStruct { sub: Some(SubSubStruct { sub: 42 }) } } ); } #[test] fn de_same_field_name_but_some_other_fields_or_something() { init(); #[derive(PartialEq, Debug, YaDeserialize, YaSerialize)] #[yaserde(rename = "foo")] pub struct FooOuter { pub other: bool, #[yaserde(rename = "foo")] pub foo: Option, } #[derive(PartialEq, Debug, YaDeserialize, YaSerialize)] pub struct FooInner { pub blah: bool, } let content = r#" false false "#; let model = FooOuter { other: false, foo: Some(FooInner { blah: false }), }; serialize_and_validate!(model, content); deserialize_and_validate!(content, model, FooOuter); } #[test] fn de_attribute_sequence() { init(); #[derive(Default, Debug, YaDeserialize, PartialEq)] pub enum Inner { #[default] #[yaserde(rename = "foo")] Foo, #[yaserde(rename = "bar")] Bar, } #[derive(Debug, PartialEq, YaDeserialize)] pub struct Outer { #[yaserde(attribute = true, rename = "seq1")] seq1: Vec, #[yaserde(attribute = true, rename = "seq2")] seq2: Vec, #[yaserde(attribute = true, rename = "seq3")] seq3: Vec, } let content = r#""#; let model = Outer { seq1: vec![1, 2, 3, 4], seq2: vec![Inner::Foo, Inner::Foo, Inner::Bar], seq3: vec!["one".to_string(), "two".to_string()], }; //serialize_and_validate!(model, content); deserialize_and_validate!(content, model, Outer); } #[test] fn de_nested_macro_rules() { init(); macro_rules! float_attrs { ($type:ty) => { #[derive(PartialEq, Debug, YaDeserialize)] pub struct Outer { #[yaserde(attribute = true)] pub inner: Option<$type>, } }; } float_attrs!(f32); }