diff --git a/yaserde/tests/de_flatten.rs b/yaserde/tests/de_flatten.rs new file mode 100644 index 0000000..d385430 --- /dev/null +++ b/yaserde/tests/de_flatten.rs @@ -0,0 +1,166 @@ +#[macro_use] +extern crate yaserde_derive; + +use std::io::Read; +use yaserde::de::from_str; +use yaserde::YaDeserialize; + +macro_rules! convert_and_validate { + ($content: expr, $struct: tt, $model: expr) => { + let loaded: Result<$struct, String> = from_str($content); + assert_eq!(loaded, Ok($model)); + }; +} + +#[test] +fn de_root_flatten_struct() { + #[derive(Default, PartialEq, Debug, YaDeserialize)] + #[yaserde(flatten)] + struct Content { + binary_data: String, + string_data: String, + } + + let content = r#" + + binary + string + "#; + + convert_and_validate!( + content, + Content, + Content { + binary_data: "binary".to_string(), + string_data: "string".to_string(), + } + ); +} + +#[test] +fn de_root_flatten_enum() { + #[derive(PartialEq, Debug, YaDeserialize)] + #[yaserde(flatten)] + pub enum Content { + Binary(Binary), + Data(Data), + Unknown, + } + + impl Default for Content { + fn default() -> Self { + Content::Unknown + } + } + + #[derive(Default, PartialEq, Debug, YaDeserialize)] + pub struct Binary { + binary_data: String, + } + + #[derive(Default, PartialEq, Debug, YaDeserialize)] + pub struct Data { + string_data: String, + } + + let content = r#" + + + binary + + "#; + + convert_and_validate!( + content, + Content, + Content::Binary(Binary { + binary_data: "binary".to_string(), + }) + ); +} + +#[test] +fn de_flatten() { + #[derive(Default, PartialEq, Debug, YaDeserialize)] + struct DateTime { + #[yaserde(flatten)] + date: Date, + time: String, + #[yaserde(flatten)] + kind: DateKind, + } + + #[derive(Default, PartialEq, Debug, YaDeserialize)] + struct Date { + year: i32, + month: i32, + day: i32, + #[yaserde(flatten)] + extra: Extra, + #[yaserde(flatten)] + optional_extra: Option, + } + + #[derive(Default, PartialEq, Debug, YaDeserialize)] + pub struct Extra { + week: i32, + century: i32, + } + + #[derive(Default, PartialEq, Debug, YaDeserialize)] + pub struct OptionalExtra { + lunar_day: i32, + } + + #[derive(PartialEq, Debug, YaDeserialize)] + pub enum DateKind { + #[yaserde(rename = "holidays")] + Holidays(Vec), + #[yaserde(rename = "working")] + Working, + } + + impl Default for DateKind { + fn default() -> Self { + DateKind::Working + } + }; + + let content = r#" + + + 2020 + 1 + 1 + 1 + 21 + 1 + + New Year's Day + Novy God Day + Polar Bear Swim Day + + "#; + convert_and_validate!( + content, + DateTime, + DateTime { + date: Date { + year: 2020, + month: 1, + day: 1, + extra: Extra { + week: 1, + century: 21, + }, + optional_extra: Some(OptionalExtra { lunar_day: 1 }), + }, + time: "10:40:03".to_string(), + kind: DateKind::Holidays(vec![ + "New Year's Day".into(), + "Novy God Day".into(), + "Polar Bear Swim Day".into() + ]) + } + ); +} diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index 21df4c0..dd212ce 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -691,92 +691,6 @@ fn de_custom() { ); } -#[test] -fn de_flatten() { - #[derive(Default, PartialEq, Debug, YaDeserialize)] - struct DateTime { - #[yaserde(flatten)] - date: Date, - time: String, - #[yaserde(flatten)] - kind: DateKind, - } - - #[derive(Default, PartialEq, Debug, YaDeserialize)] - struct Date { - year: i32, - month: i32, - day: i32, - #[yaserde(flatten)] - extra: Extra, - #[yaserde(flatten)] - optional_extra: Option, - } - - #[derive(Default, PartialEq, Debug, YaDeserialize)] - pub struct Extra { - week: i32, - century: i32, - } - - #[derive(Default, PartialEq, Debug, YaDeserialize)] - pub struct OptionalExtra { - lunar_day: i32, - } - - #[derive(PartialEq, Debug, YaDeserialize)] - pub enum DateKind { - #[yaserde(rename = "holidays")] - Holidays(Vec), - #[yaserde(rename = "working")] - Working, - } - - impl Default for DateKind { - fn default() -> Self { - DateKind::Working - } - }; - - let content = r#" - - - 2020 - 1 - 1 - 1 - 21 - 1 - - New Year's Day - Novy God Day - Polar Bear Swim Day - - "#; - convert_and_validate!( - content, - DateTime, - DateTime { - date: Date { - year: 2020, - month: 1, - day: 1, - extra: Extra { - week: 1, - century: 21, - }, - optional_extra: Some(OptionalExtra { lunar_day: 1 }), - }, - time: "10:40:03".to_string(), - kind: DateKind::Holidays(vec![ - "New Year's Day".into(), - "Novy God Day".into(), - "Polar Bear Swim Day".into() - ]) - } - ); -} - #[test] fn de_subitem_issue_12() { #[derive(Default, PartialEq, Debug, YaDeserialize)] diff --git a/yaserde/tests/ser_flatten.rs b/yaserde/tests/ser_flatten.rs new file mode 100644 index 0000000..0127ed1 --- /dev/null +++ b/yaserde/tests/ser_flatten.rs @@ -0,0 +1,156 @@ +#[macro_use] +extern crate yaserde_derive; + +use std::io::Write; +use yaserde::ser::to_string; +use yaserde::YaSerialize; + +macro_rules! convert_and_validate { + ($model: expr, $content: expr) => { + let data: Result = to_string(&$model); + assert_eq!( + data, + Ok( + String::from($content) + .split("\n") + .map(|s| s.trim()) + .collect::() + ) + ); + }; +} + +#[test] +fn ser_flatten() { + #[derive(Default, PartialEq, Debug, YaSerialize)] + struct DateTime { + #[yaserde(flatten)] + date: Date, + time: String, + #[yaserde(flatten)] + kind: DateKind, + } + + #[derive(Default, PartialEq, Debug, YaSerialize)] + struct Date { + year: i32, + month: i32, + day: i32, + #[yaserde(flatten)] + extra: Extra, + #[yaserde(flatten)] + optional_extra: Option, + } + + #[derive(Default, PartialEq, Debug, YaSerialize)] + pub struct Extra { + week: i32, + century: i32, + } + + #[derive(Default, PartialEq, Debug, YaSerialize)] + pub struct OptionalExtra { + lunar_day: i32, + } + + #[derive(PartialEq, Debug, YaSerialize)] + pub enum DateKind { + #[yaserde(rename = "holidays")] + Holidays(Vec), + #[yaserde(rename = "working")] + Working, + } + + impl Default for DateKind { + fn default() -> Self { + DateKind::Working + } + }; + + let model = DateTime { + date: Date { + year: 2020, + month: 1, + day: 1, + extra: Extra { + week: 1, + century: 21, + }, + optional_extra: Some(OptionalExtra { lunar_day: 1 }), + }, + time: "10:40:03".to_string(), + kind: DateKind::Holidays(vec![ + "New Year's Day".into(), + "Novy God Day".into(), + "Polar Bear Swim Day".into(), + ]), + }; + + let content = r#" + + + 2020 + 1 + 1 + 1 + 21 + 1 + + New Year's Day + Novy God Day + Polar Bear Swim Day + "#; + + convert_and_validate!(model, content); +} + +#[test] +fn ser_root_flatten_struct() { + #[derive(YaSerialize, PartialEq, Debug)] + #[yaserde(flatten)] + pub struct Content { + binary_data: String, + string_data: String, + } + + let model = Content { + binary_data: "binary".to_string(), + string_data: "string".to_string(), + }; + let content = r#"binarystring"#; + convert_and_validate!(model, content); +} + +#[test] +fn ser_root_flatten_enum() { + #[derive(YaSerialize, PartialEq, Debug)] + #[yaserde(flatten)] + pub enum Content { + Binary(Binary), + Data(Data), + } + + #[derive(YaSerialize, PartialEq, Debug)] + pub struct Binary { + binary_data: String, + } + + #[derive(YaSerialize, PartialEq, Debug)] + pub struct Data { + string_data: String, + } + + let model = Content::Binary(Binary { + binary_data: "binary".to_string(), + }); + let content = + r#"binary"#; + convert_and_validate!(model, content); + + let model = Content::Data(Data { + string_data: "string".to_string(), + }); + let content = + r#"string"#; + convert_and_validate!(model, content); +} diff --git a/yaserde/tests/serializer.rs b/yaserde/tests/serializer.rs index 0e1e8a1..c3ff11b 100644 --- a/yaserde/tests/serializer.rs +++ b/yaserde/tests/serializer.rs @@ -329,87 +329,3 @@ fn ser_custom() { let content = "2020110"; convert_and_validate!(model, content); } - -#[test] -fn ser_flatten() { - #[derive(Default, PartialEq, Debug, YaSerialize)] - struct DateTime { - #[yaserde(flatten)] - date: Date, - time: String, - #[yaserde(flatten)] - kind: DateKind, - } - - #[derive(Default, PartialEq, Debug, YaSerialize)] - struct Date { - year: i32, - month: i32, - day: i32, - #[yaserde(flatten)] - extra: Extra, - #[yaserde(flatten)] - optional_extra: Option, - } - - #[derive(Default, PartialEq, Debug, YaSerialize)] - pub struct Extra { - week: i32, - century: i32, - } - - #[derive(Default, PartialEq, Debug, YaSerialize)] - pub struct OptionalExtra { - lunar_day: i32, - } - - #[derive(PartialEq, Debug, YaSerialize)] - pub enum DateKind { - #[yaserde(rename = "holidays")] - Holidays(Vec), - #[yaserde(rename = "working")] - Working, - } - - impl Default for DateKind { - fn default() -> Self { - DateKind::Working - } - }; - - let model = DateTime { - date: Date { - year: 2020, - month: 1, - day: 1, - extra: Extra { - week: 1, - century: 21, - }, - optional_extra: Some(OptionalExtra { lunar_day: 1 }), - }, - time: "10:40:03".to_string(), - kind: DateKind::Holidays(vec![ - "New Year's Day".into(), - "Novy God Day".into(), - "Polar Bear Swim Day".into(), - ]), - }; - - let content = r#" - - - 2020 - 1 - 1 - 1 - 21 - 1 - - New Year's Day - Novy God Day - Polar Bear Swim Day - "#; - - convert_and_validate!(model, content); -} diff --git a/yaserde_derive/src/de/expand_enum.rs b/yaserde_derive/src/de/expand_enum.rs index 4fdb0ed..c43d246 100644 --- a/yaserde_derive/src/de/expand_enum.rs +++ b/yaserde_derive/src/de/expand_enum.rs @@ -1,7 +1,6 @@ use crate::attribute::*; use crate::field_type::*; use proc_macro2::TokenStream; -use std::collections::BTreeMap; use syn::spanned::Spanned; use syn::DataEnum; use syn::Fields; @@ -11,7 +10,7 @@ pub fn parse( data_enum: &DataEnum, name: &Ident, root: &str, - _namespaces: &BTreeMap, + root_attributes: &YaSerdeAttribute, ) -> TokenStream { let match_to_enum: TokenStream = data_enum .variants @@ -20,6 +19,8 @@ pub fn parse( .filter_map(|f| f) .collect(); + let flatten = root_attributes.flatten; + quote! { use xml::reader::XmlEvent; use yaserde::Visitor; @@ -47,7 +48,7 @@ pub fn parse( match name.local_name.as_str() { #match_to_enum - named_element => { + _named_element => { let _root = reader.next_event(); } } @@ -68,6 +69,13 @@ pub fn parse( XmlEvent::Characters(ref text_content) => { let _root = reader.next_event(); } + XmlEvent::EndDocument => { + if #flatten { + break; + } + + return Err(format!("End of document, missing some content ?")) + } event => { return Err(format!("unknown event {:?}", event)) } diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index 1f1d51e..3c48215 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -11,13 +11,13 @@ pub fn parse( data_struct: &DataStruct, name: &Ident, root: &str, - prefix: &Option, - namespaces: &BTreeMap, + root_attributes: &YaSerdeAttribute, ) -> TokenStream { - let namespaces_matches: TokenStream = namespaces + let namespaces_matches: TokenStream = root_attributes + .namespaces .iter() .map(|(p, ns)| { - if prefix.as_ref() == Some(p) { + if root_attributes.prefix.as_ref() == Some(p) { Some(quote!(#ns => {})) } else { None @@ -199,7 +199,7 @@ pub fn parse( &action, &field_attrs, label, - &namespaces, + &root_attributes.namespaces, field.span(), ) }; @@ -368,6 +368,8 @@ pub fn parse( build_code_for_unused_xml_events(&call_flatten_visitors) }; + let flatten = root_attributes.flatten; + quote! { use xml::reader::{XmlEvent, EventReader}; use xml::writer::EventWriter; @@ -407,7 +409,6 @@ pub fn parse( loop { let event = reader.peek()?.to_owned(); - match event { XmlEvent::StartElement{ref name, ref attributes, ..} => { let mut skipped = false; @@ -443,6 +444,11 @@ pub fn parse( #write_unused depth -= 1; } + XmlEvent::EndDocument => { + if #flatten { + break; + } + } XmlEvent::Characters(ref text_content) => { #set_text let event = reader.next_event()?; diff --git a/yaserde_derive/src/de/mod.rs b/yaserde_derive/src/de/mod.rs index 4e3e8ab..84f1f17 100644 --- a/yaserde_derive/src/de/mod.rs +++ b/yaserde_derive/src/de/mod.rs @@ -16,16 +16,10 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result expand_struct::parse( - data_struct, - name, - &root, - &root_attrs.prefix, - &root_attrs.namespaces, - ), - syn::Data::Enum(ref data_enum) => { - expand_enum::parse(data_enum, name, &root, &root_attrs.namespaces) + syn::Data::Struct(ref data_struct) => { + expand_struct::parse(data_struct, name, &root, &root_attrs) } + syn::Data::Enum(ref data_enum) => expand_enum::parse(data_enum, name, &root, &root_attrs), syn::Data::Union(ref _data_union) => unimplemented!(), }; diff --git a/yaserde_derive/src/ser/expand_enum.rs b/yaserde_derive/src/ser/expand_enum.rs index 2a63988..a357272 100644 --- a/yaserde_derive/src/ser/expand_enum.rs +++ b/yaserde_derive/src/ser/expand_enum.rs @@ -1,7 +1,6 @@ use crate::attribute::*; use crate::field_type::*; use proc_macro2::TokenStream; -use std::collections::BTreeMap; use syn::spanned::Spanned; use syn::DataEnum; use syn::Fields; @@ -11,8 +10,7 @@ pub fn serialize( data_enum: &DataEnum, name: &Ident, root: &str, - namespaces: &BTreeMap, - default_namespace: &Option, + root_attributes: &YaSerdeAttribute, ) -> TokenStream { let write_enum_content: TokenStream = data_enum .variants @@ -224,10 +222,11 @@ pub fn serialize( .filter_map(|x| x) .collect(); - let add_namespaces: TokenStream = namespaces + let add_namespaces: TokenStream = root_attributes + .namespaces .iter() .map(|(prefix, namespace)| { - if let Some(dn) = default_namespace { + if let Some(dn) = &root_attributes.default_namespace { if dn == prefix { return Some(quote!( .default_ns(#namespace) @@ -241,6 +240,8 @@ pub fn serialize( .filter_map(|x| x) .collect(); + let flatten = root_attributes.flatten; + quote! { use xml::writer::XmlEvent; @@ -250,7 +251,7 @@ pub fn serialize( -> Result<(), String> { let skip = writer.skip_start_end(); - if !skip { + if !#flatten && !skip { if let Some(label) = writer.get_start_event_name() { let struct_start_event = XmlEvent::start_element(label.as_ref()); writer.write(struct_start_event).map_err(|e| e.to_string())?; @@ -264,7 +265,7 @@ pub fn serialize( #write_enum_content } - if !skip { + if !#flatten && !skip { let struct_end_event = XmlEvent::end_element(); writer.write(struct_end_event).map_err(|e| e.to_string())?; } diff --git a/yaserde_derive/src/ser/expand_struct.rs b/yaserde_derive/src/ser/expand_struct.rs index 1411a03..83bbd19 100644 --- a/yaserde_derive/src/ser/expand_struct.rs +++ b/yaserde_derive/src/ser/expand_struct.rs @@ -2,7 +2,6 @@ use crate::attribute::*; use crate::field_type::*; use crate::ser::element::*; use proc_macro2::TokenStream; -use std::collections::BTreeMap; use std::string::ToString; use syn::spanned::Spanned; use syn::DataStruct; @@ -12,8 +11,7 @@ pub fn serialize( data_struct: &DataStruct, name: &Ident, root: &str, - namespaces: &BTreeMap, - default_namespace: &Option, + root_attributes: &YaSerdeAttribute, ) -> TokenStream { let build_attributes: TokenStream = data_struct .fields @@ -26,7 +24,7 @@ pub fn serialize( let label = &field.ident; - let label_name = build_label_name(&field, &field_attrs, default_namespace); + let label_name = build_label_name(&field, &field_attrs, &root_attributes.default_namespace); get_field_type(field).and_then(|f| match f { FieldType::FieldTypeString @@ -205,10 +203,11 @@ pub fn serialize( .filter_map(|x| x) .collect(); - let add_namespaces: TokenStream = namespaces + let add_namespaces: TokenStream = root_attributes + .namespaces .iter() .map(|(prefix, namespace)| { - if let Some(dn) = default_namespace { + if let Some(dn) = &root_attributes.default_namespace { if dn == prefix { return Some(quote!( .default_ns(#namespace) @@ -239,7 +238,7 @@ pub fn serialize( )); } - let label_name = build_label_name(&field, &field_attrs, default_namespace); + let label_name = build_label_name(&field, &field_attrs, &root_attributes.default_namespace); let conditions = condition_generator(label, &field_attrs); get_field_type(field).and_then(|f| match f { @@ -381,6 +380,8 @@ pub fn serialize( .filter_map(|x| x) .collect(); + let flatten = root_attributes.flatten; + quote! { use xml::writer::XmlEvent; @@ -390,7 +391,7 @@ pub fn serialize( -> Result<(), String> { let skip = writer.skip_start_end(); - if !skip { + if !#flatten && !skip { let yaserde_label = writer.get_start_event_name().unwrap_or_else(|| #root.to_string()); let struct_start_event = XmlEvent::start_element(yaserde_label.as_ref())#add_namespaces; #build_attributes @@ -399,7 +400,7 @@ pub fn serialize( #struct_inspector - if !skip { + if !#flatten && !skip { let struct_end_event = XmlEvent::end_element(); writer.write(struct_end_event).map_err(|e| e.to_string())?; } diff --git a/yaserde_derive/src/ser/mod.rs b/yaserde_derive/src/ser/mod.rs index 68a1532..dec8dfa 100644 --- a/yaserde_derive/src/ser/mod.rs +++ b/yaserde_derive/src/ser/mod.rs @@ -2,7 +2,7 @@ pub mod element; pub mod expand_enum; pub mod expand_struct; -use crate::attribute; +use crate::attribute::YaSerdeAttribute; use proc_macro2::TokenStream; use syn; use syn::Ident; @@ -12,13 +12,14 @@ pub fn expand_derive_serialize(ast: &syn::DeriveInput) -> Result Result expand_struct::serialize( - data_struct, - name, - &root, - &root_attrs.namespaces, - &root_attrs.default_namespace, - ), - syn::Data::Enum(ref data_enum) => expand_enum::serialize( - data_enum, - name, - &root, - &root_attrs.namespaces, - &root_attrs.default_namespace, - ), + syn::Data::Struct(ref data_struct) => { + expand_struct::serialize(data_struct, name, &root, &root_attrs) + } + syn::Data::Enum(ref data_enum) => expand_enum::serialize(data_enum, name, &root, &root_attrs), syn::Data::Union(ref _data_union) => unimplemented!(), };