From db9fc0abd98f4b153373ef2ca3709c84a0faad94 Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Mon, 30 Dec 2024 10:45:43 +0100 Subject: [PATCH 01/13] fix(deserialization): nested deserialization (#200) changes condition of start element next_event call for structs, always break for EndDocument --- yaserde/tests/deserializer.rs | 77 ++++++++++++++++++++++++++ yaserde_derive/src/de/expand_struct.rs | 7 +-- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index 5b0e3af..c1f03c8 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -1117,3 +1117,80 @@ fn de_nested_macro_rules() { float_attrs!(f32); } + + +#[test] +fn de_nested_element_equality() { + + + #[derive(YaDeserialize, Debug, PartialEq)] + #[yaserde(rename = "EBMLSchema")] + struct Schema { + #[yaserde(rename = "element")] + elements: Vec, + } + + #[derive(YaDeserialize, Debug, PartialEq)] + #[yaserde(rename = "element")] + struct Element { + #[yaserde(rename = "documentation")] + documentation: Vec, + } + + #[derive(YaDeserialize, Debug, PartialEq)] + #[yaserde(rename = "documentation")] + struct Documentation { + #[yaserde(text=true)] + body: String, + } + + let parent = r#" + +Test + +"#; + let parent: Schema = yaserde::de::from_str(parent).unwrap(); + + let child = r#" +Test +"#; + let child: Element = yaserde::de::from_str(child).unwrap(); + + assert_ne!(parent.elements, vec![]); + assert_eq!(parent.elements[0], child); +} + +#[test] +fn de_nested_3_levels(){ + + #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] + struct A { + id: String, + #[yaserde(rename = "SAME")] + many:Vec + } + + #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] + struct B { + id: Option, + #[yaserde(rename = "SAME")] + many:Vec + + } + + #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] + struct C { + id: Option, + } + + let content = r#"a1b1c1c2"#; + let model = A { + id: "a1".to_string(), + many: vec![B { id: Some("b1".to_string()), many: vec![ + C { id: Some("c1".to_string() )}, + C { id: Some("c2".to_string() )}, + ] }], + }; + deserialize_and_validate!(content, model, A); + +} diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index fca7486..9c1b30e 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -421,7 +421,7 @@ pub fn parse( match event { ::yaserde::__xml::reader::XmlEvent::StartElement{ref name, ref attributes, ..} => { let namespace = name.namespace.clone().unwrap_or_default(); - if depth == 0 && name.local_name == #root && namespace.as_str() == #root_namespace { + if depth == 0 { // Consume root element. We must do this first. In the case it shares a name with a child element, we don't // want to prematurely match the child element below. let event = reader.next_event()?; @@ -457,9 +457,8 @@ pub fn parse( depth -= 1; } ::yaserde::__xml::reader::XmlEvent::EndDocument => { - if #flatten { - break; - } + // once we receive this once, we will keep getting it, potentially looping forever + break; } ::yaserde::__xml::reader::XmlEvent::Characters(ref text_content) => { #set_text From 358e0b7c3d9d87297f411d6c2d48ee24a7288e35 Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Mon, 30 Dec 2024 17:34:06 +0100 Subject: [PATCH 02/13] style: fix formatting --- yaserde/tests/deserializer.rs | 44 ++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index c1f03c8..dc15760 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -1118,11 +1118,8 @@ fn de_nested_macro_rules() { float_attrs!(f32); } - #[test] fn de_nested_element_equality() { - - #[derive(YaDeserialize, Debug, PartialEq)] #[yaserde(rename = "EBMLSchema")] struct Schema { @@ -1140,10 +1137,10 @@ fn de_nested_element_equality() { #[derive(YaDeserialize, Debug, PartialEq)] #[yaserde(rename = "documentation")] struct Documentation { - #[yaserde(text=true)] + #[yaserde(text = true)] body: String, } - + let parent = r#" Test @@ -1155,42 +1152,47 @@ fn de_nested_element_equality() { Test "#; let child: Element = yaserde::de::from_str(child).unwrap(); - + assert_ne!(parent.elements, vec![]); assert_eq!(parent.elements[0], child); } #[test] -fn de_nested_3_levels(){ - - #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] +fn de_nested_3_levels() { + #[derive(YaSerialize, YaDeserialize, Debug, PartialEq)] struct A { id: String, #[yaserde(rename = "SAME")] - many:Vec + many: Vec, } - #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] + #[derive(YaSerialize, YaDeserialize, Debug, PartialEq)] struct B { id: Option, #[yaserde(rename = "SAME")] - many:Vec - + many: Vec, } - #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] + #[derive(YaSerialize, YaDeserialize, Debug, PartialEq)] struct C { id: Option, } - let content = r#"a1b1c1c2"#; - let model = A { + let content = + r#"a1b1c1c2"#; + let model = A { id: "a1".to_string(), - many: vec![B { id: Some("b1".to_string()), many: vec![ - C { id: Some("c1".to_string() )}, - C { id: Some("c2".to_string() )}, - ] }], + many: vec![B { + id: Some("b1".to_string()), + many: vec![ + C { + id: Some("c1".to_string()), + }, + C { + id: Some("c2".to_string()), + }, + ], + }], }; deserialize_and_validate!(content, model, A); - } From 1b281bc7e5094853226e9be233b2aec2ac7759e5 Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Mon, 30 Dec 2024 19:38:43 +0100 Subject: [PATCH 03/13] fix: keep namespace + name checking but go against root label if available --- yaserde/src/de/mod.rs | 2 + yaserde/tests/deserializer.rs | 51 ++++++++++++++++++++++++++ yaserde_derive/src/de/expand_struct.rs | 5 ++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/yaserde/src/de/mod.rs b/yaserde/src/de/mod.rs index b08b138..5840820 100644 --- a/yaserde/src/de/mod.rs +++ b/yaserde/src/de/mod.rs @@ -18,6 +18,7 @@ pub struct Deserializer { depth: usize, reader: EventReader, peeked: Option, + pub inner_struct_label: Option<&'static str>, } impl Deserializer { @@ -26,6 +27,7 @@ impl Deserializer { depth: 0, reader, peeked: None, + inner_struct_label: None, } } diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index dc15760..4466d65 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -1196,3 +1196,54 @@ fn de_nested_3_levels() { }; deserialize_and_validate!(content, model, A); } + +#[test] +fn de_nested_issue_192() { + #[derive(Clone, Default, Debug, PartialEq, YaDeserialize)] + #[yaserde( + prefix = "xs", + namespaces = { + "xs" = "http://www.w3.org/2001/XMLSchema", + } + )] + pub struct XSDGroup { + #[yaserde(rename = "ref", attribute = true)] + pub reference: String, + } + + #[derive(Clone, Default, Debug, PartialEq, YaDeserialize)] + #[yaserde( + rename = "sequence", + prefix = "xs", + namespaces = { + "xs" = "http://www.w3.org/2001/XMLSchema", + } + )] + pub struct Sequence { + #[yaserde(rename = "group", prefix = "xs")] + pub groups: Vec, + + #[yaserde(rename = "sequence", prefix = "xs")] + pub sequences: Vec, + } + + let content = r#" + + + + + "#; + let model = Sequence { + groups: vec![ + XSDGroup { + reference: "AR:AR-OBJECT".to_string(), + }, + XSDGroup { + reference: "AR:AUTOSAR".to_string(), + }, + ], + sequences: vec![], + }; + + deserialize_and_validate!(content, model, Sequence); +} diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index 9c1b30e..1fdc7a5 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -160,6 +160,7 @@ pub fn parse( } if let Ok(::yaserde::__xml::reader::XmlEvent::StartElement { .. }) = reader.peek() { // If substruct's start element found then deserialize substruct + reader.inner_struct_label = Some(#label_name); let value = <#struct_name as ::yaserde::YaDeserialize>::deserialize(reader)?; #value_label #action; // read EndElement @@ -383,7 +384,6 @@ pub fn parse( build_code_for_unused_xml_events(&call_flatten_visitors) }; - let flatten = root_attributes.flatten; let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); quote! { @@ -392,6 +392,7 @@ pub fn parse( fn deserialize( reader: &mut ::yaserde::de::Deserializer, ) -> ::std::result::Result { + let root_label= reader.inner_struct_label.take().unwrap_or(#root); let (named_element, struct_namespace) = if let ::yaserde::__xml::reader::XmlEvent::StartElement { name, .. } = reader.peek()?.to_owned() { (name.local_name.to_owned(), name.namespace.clone()) @@ -421,7 +422,7 @@ pub fn parse( match event { ::yaserde::__xml::reader::XmlEvent::StartElement{ref name, ref attributes, ..} => { let namespace = name.namespace.clone().unwrap_or_default(); - if depth == 0 { + if depth == 0 && name.local_name == root_label && namespace.as_str() == #root_namespace { // Consume root element. We must do this first. In the case it shares a name with a child element, we don't // want to prematurely match the child element below. let event = reader.next_event()?; From 70e65434c190538b3072b95492d979452d7acef9 Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Tue, 31 Dec 2024 00:04:19 +0100 Subject: [PATCH 04/13] fix: drop namespace equality check --- yaserde_derive/src/de/expand_struct.rs | 3 +-- yaserde_derive/src/de/mod.rs | 12 ------------ 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index 1fdc7a5..7a6a81b 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -7,7 +7,6 @@ use syn::{DataStruct, Generics, Ident}; pub fn parse( data_struct: &DataStruct, name: &Ident, - root_namespace: &str, root: &str, root_attributes: &YaSerdeAttribute, generics: &Generics, @@ -422,7 +421,7 @@ pub fn parse( match event { ::yaserde::__xml::reader::XmlEvent::StartElement{ref name, ref attributes, ..} => { let namespace = name.namespace.clone().unwrap_or_default(); - if depth == 0 && name.local_name == root_label && namespace.as_str() == #root_namespace { + if depth == 0 && name.local_name == root_label { // Consume root element. We must do this first. In the case it shares a name with a child element, we don't // want to prematurely match the child element below. let event = reader.next_event()?; diff --git a/yaserde_derive/src/de/mod.rs b/yaserde_derive/src/de/mod.rs index 4526209..2ed9f9c 100644 --- a/yaserde_derive/src/de/mod.rs +++ b/yaserde_derive/src/de/mod.rs @@ -15,23 +15,11 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result expand_struct::parse( data_struct, name, - &root_namespace, &root_name, &root_attributes, generics, From f1a52f218a1b7e93d8c53386e67d42b46548fa00 Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Thu, 2 Jan 2025 09:50:44 +0100 Subject: [PATCH 05/13] style: formatting --- yaserde_derive/src/de/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/yaserde_derive/src/de/mod.rs b/yaserde_derive/src/de/mod.rs index 2ed9f9c..99d243a 100644 --- a/yaserde_derive/src/de/mod.rs +++ b/yaserde_derive/src/de/mod.rs @@ -17,13 +17,9 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result expand_struct::parse( - data_struct, - name, - &root_name, - &root_attributes, - generics, - ), + syn::Data::Struct(ref data_struct) => { + expand_struct::parse(data_struct, name, &root_name, &root_attributes, generics) + } syn::Data::Enum(ref data_enum) => { expand_enum::parse(data_enum, name, &root_name, &root_attributes, generics) } From df94be609b31ef34e65c6c297740e511dceeaa0b Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Fri, 17 Jan 2025 18:34:21 +0100 Subject: [PATCH 06/13] test: fix test input after xml-rs change see: https://github.com/kornelski/xml-rs/commit/4a5ba62fb30872a2bb0648150de2c195519bedcc --- examples/tests/data/svd.xml | 2 +- yaserde/src/lib.rs | 2 +- yaserde/tests/cdata.rs | 4 ++-- yaserde/tests/deserializer.rs | 30 +++++++++++++++--------------- yaserde/tests/option.rs | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/tests/data/svd.xml b/examples/tests/data/svd.xml index 8743645..0d806ae 100644 --- a/examples/tests/data/svd.xml +++ b/examples/tests/data/svd.xml @@ -1,4 +1,4 @@ - + Renesas diff --git a/yaserde/src/lib.rs b/yaserde/src/lib.rs index 6c86ac9..669e30e 100644 --- a/yaserde/src/lib.rs +++ b/yaserde/src/lib.rs @@ -328,7 +328,7 @@ macro_rules! serialize_and_validate { log::debug!("serialize_and_validate @ {}:{}", file!(), line!()); let data: Result = yaserde::ser::to_string(&$model); - let content = &format!(r#"{}"#, $content); + let content = &format!(r#"{}"#, $content); assert_eq!( data, Ok(content.split("\n").map(|s| s.trim()).collect::()) diff --git a/yaserde/tests/cdata.rs b/yaserde/tests/cdata.rs index a73868d..45b980f 100644 --- a/yaserde/tests/cdata.rs +++ b/yaserde/tests/cdata.rs @@ -18,14 +18,14 @@ fn test_cdata_serialization() { msgdata: "Some unescaped content".to_string(), }; let xml_output = yaserde::ser::to_string(&test_data).expect("Serialization failed"); - let expected_output = r#"Some unescaped content]]>"#; + let expected_output = r#"Some unescaped content]]>"#; assert_eq!(xml_output, expected_output); } #[test] fn test_cdata_deserialization() { init(); - let xml = r#"Some unescaped content]]>"#; + let xml = r#"Some unescaped content]]>"#; let r: TestStruct = yaserde::de::from_str(xml).unwrap(); let expected_output = TestStruct { msgdata: "Some unescaped content".to_string(), diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index 4466d65..52112d7 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -583,7 +583,7 @@ fn de_complex_enum() { Dotted(u32), } - let content = r#" + let content = r#" text @@ -598,7 +598,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" text @@ -613,7 +613,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" 56 @@ -628,7 +628,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" @@ -646,7 +646,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" text @@ -661,7 +661,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" @@ -679,7 +679,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" abc @@ -695,7 +695,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" 12 @@ -711,7 +711,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" 1223 @@ -730,7 +730,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" 1223 @@ -749,7 +749,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" 54 @@ -854,7 +854,7 @@ fn de_subitem_issue_12() { } convert_and_validate!( - r#" + r#" 54 @@ -884,7 +884,7 @@ fn de_subitem_issue_12_with_sub() { } convert_and_validate!( - r#" + r#" 54 @@ -911,7 +911,7 @@ fn de_subitem_issue_12_attributes() { } convert_and_validate!( - r#" + r#" @@ -940,7 +940,7 @@ fn de_subitem_issue_12_attributes_with_sub() { } convert_and_validate!( - r#" + r#" diff --git a/yaserde/tests/option.rs b/yaserde/tests/option.rs index fac7804..1cad3e2 100644 --- a/yaserde/tests/option.rs +++ b/yaserde/tests/option.rs @@ -144,7 +144,7 @@ mod tests { #[test] fn deserialize_without_car() { - let person = r#" + let person = r#" brown 25 From b8b85b4a56d574a43bfdcd6880c0320c61816f3a Mon Sep 17 00:00:00 2001 From: John Hughes Date: Sat, 6 Sep 2025 12:54:54 +0200 Subject: [PATCH 07/13] feat: add serialization support for Vec as an attribute --- yaserde/tests/serializer.rs | 27 ++++++++++++++++++++ yaserde_derive/src/ser/expand_struct.rs | 33 ++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/yaserde/tests/serializer.rs b/yaserde/tests/serializer.rs index b575206..c48c5c1 100644 --- a/yaserde/tests/serializer.rs +++ b/yaserde/tests/serializer.rs @@ -415,3 +415,30 @@ fn ser_custom() { let content = "2020110"; serialize_and_validate!(model, content); } + +#[test] +fn ser_vec_as_attribute() { + #[derive(YaSerialize, PartialEq, Debug)] + #[yaserde(rename = "TestTag")] + pub struct VecAttributeStruct { + #[yaserde(attribute = true)] + numbers: Vec, + #[yaserde(attribute = true)] + strings: Vec, + #[yaserde(attribute = true)] + bools: Vec, + #[yaserde(attribute = true)] + floats: Vec, + } + + let model = VecAttributeStruct { + numbers: vec![1, 2, 3, 4], + strings: vec!["hello".to_string(), "world".to_string()], + bools: vec![true, false, true], + floats: vec![3.14, 2.71], + }; + + // Expected XML with space-separated attribute values + let content = r#""#; + serialize_and_validate!(model, content); +} diff --git a/yaserde_derive/src/ser/expand_struct.rs b/yaserde_derive/src/ser/expand_struct.rs index 2a4507b..1a46c08 100644 --- a/yaserde_derive/src/ser/expand_struct.rs +++ b/yaserde_derive/src/ser/expand_struct.rs @@ -115,9 +115,36 @@ pub fn serialize( struct_start_event.attr(#label_name, &yaserde_inner) }), ), - Field::FieldVec { .. } => { - // TODO - quote!() + Field::FieldVec { data_type } => match *data_type { + Field::FieldString + | Field::FieldBool + | Field::FieldI8 + | Field::FieldU8 + | Field::FieldI16 + | Field::FieldU16 + | Field::FieldI32 + | Field::FieldU32 + | Field::FieldI64 + | Field::FieldU64 + | Field::FieldF32 + | Field::FieldF64 => field.ser_wrap_default_attribute( + Some(quote! { + self.#label + .iter() + .map(|item| item.to_string()) + .collect::<::std::vec::Vec<_>>() + .join(" ") + }), + quote!({ + struct_start_event.attr(#label_name, &yaserde_inner) + }), + ), + Field::FieldOption { .. } | Field::FieldVec { .. } => { + unimplemented!("Nested Option or Vec in Vec not supported for attributes") + } + Field::FieldStruct { .. } => { + unimplemented!("Struct fields in Vec not supported for attributes") + } } } } else { From b3d4c360bc4d1c31bea4137cb42f776975c79e61 Mon Sep 17 00:00:00 2001 From: John Hughes Date: Sat, 6 Sep 2025 13:13:28 +0200 Subject: [PATCH 08/13] feat: add support for nested Vec attributes in serialization --- yaserde/tests/serializer.rs | 25 +++++++++++++++++++++++++ yaserde_derive/src/ser/expand_struct.rs | 17 +++++++++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/yaserde/tests/serializer.rs b/yaserde/tests/serializer.rs index c48c5c1..3c60d18 100644 --- a/yaserde/tests/serializer.rs +++ b/yaserde/tests/serializer.rs @@ -442,3 +442,28 @@ fn ser_vec_as_attribute() { let content = r#""#; serialize_and_validate!(model, content); } + +#[test] +fn ser_vec_as_attribute_nested() { + #[derive(YaSerialize, PartialEq, Debug)] + #[yaserde(rename = "TestTag")] + struct VecAttributeStruct { + #[yaserde(attribute = true)] + outer: Vec, + } + + #[derive(YaSerialize, PartialEq, Debug)] + #[yaserde(rename = "TestTag")] + enum Inner { + One, + Two, + } + + let model = VecAttributeStruct { + outer: vec![Inner::One, Inner::Two], + }; + + // Expected XML with space-separated attribute values + let content = r#""#; + serialize_and_validate!(model, content); +} diff --git a/yaserde_derive/src/ser/expand_struct.rs b/yaserde_derive/src/ser/expand_struct.rs index 1a46c08..0f31e31 100644 --- a/yaserde_derive/src/ser/expand_struct.rs +++ b/yaserde_derive/src/ser/expand_struct.rs @@ -142,10 +142,19 @@ pub fn serialize( Field::FieldOption { .. } | Field::FieldVec { .. } => { unimplemented!("Nested Option or Vec in Vec not supported for attributes") } - Field::FieldStruct { .. } => { - unimplemented!("Struct fields in Vec not supported for attributes") - } - } + Field::FieldStruct { .. } => field.ser_wrap_default_attribute( + Some(quote! { + self.#label + .iter() + .map(|item| ::yaserde::ser::to_string_content(item)) + .collect::<::std::result::Result<::std::vec::Vec<_>, _>>()? + .join(" ") + }), + quote!({ + struct_start_event.attr(#label_name, &yaserde_inner) + }), + ), + }, } } else { match field.get_type() { From 8b8c43b8aedfd583e99bdf61c818882f1919e724 Mon Sep 17 00:00:00 2001 From: John Hughes Date: Sat, 6 Sep 2025 16:13:05 +0200 Subject: [PATCH 09/13] fix: clippy warnings --- examples/src/bbigras_namespace.rs | 1 + examples/src/boscop.rs | 1 + examples/src/generic.rs | 1 + examples/src/ln_dom.rs | 1 + examples/src/same_element_different_namespaces.rs | 1 + yaserde/tests/deserializer.rs | 2 ++ yaserde/tests/serializer.rs | 4 ++-- yaserde_derive/src/common/field.rs | 10 +++++----- 8 files changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/src/bbigras_namespace.rs b/examples/src/bbigras_namespace.rs index 23d4ad0..e294597 100644 --- a/examples/src/bbigras_namespace.rs +++ b/examples/src/bbigras_namespace.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] // related to issue https://github.com/media-io/yaserde/issues/15 use yaserde::*; diff --git a/examples/src/boscop.rs b/examples/src/boscop.rs index 07f516d..b083344 100644 --- a/examples/src/boscop.rs +++ b/examples/src/boscop.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] // related to issue https://github.com/media-io/yaserde/issues/3 use yaserde::*; diff --git a/examples/src/generic.rs b/examples/src/generic.rs index 5c242e1..f55a53b 100644 --- a/examples/src/generic.rs +++ b/examples/src/generic.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use yaserde::*; #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] diff --git a/examples/src/ln_dom.rs b/examples/src/ln_dom.rs index 78db84e..aaae195 100644 --- a/examples/src/ln_dom.rs +++ b/examples/src/ln_dom.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] // related to issue https://github.com/media-io/yaserde/issues/11 use yaserde::*; diff --git a/examples/src/same_element_different_namespaces.rs b/examples/src/same_element_different_namespaces.rs index 31cb4e1..be3668e 100644 --- a/examples/src/same_element_different_namespaces.rs +++ b/examples/src/same_element_different_namespaces.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] // related to issue https://github.com/media-io/yaserde/issues/186 use yaserde::*; diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index 52112d7..8d92265 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -493,6 +493,7 @@ fn de_enum() { Black, } + #[allow(dead_code)] #[derive(YaDeserialize, PartialEq, Debug)] pub struct RGBColor { red: String, @@ -1108,6 +1109,7 @@ fn de_nested_macro_rules() { macro_rules! float_attrs { ($type:ty) => { #[derive(PartialEq, Debug, YaDeserialize)] + #[allow(dead_code)] pub struct Outer { #[yaserde(attribute = true)] pub inner: Option<$type>, diff --git a/yaserde/tests/serializer.rs b/yaserde/tests/serializer.rs index 3c60d18..a945fc1 100644 --- a/yaserde/tests/serializer.rs +++ b/yaserde/tests/serializer.rs @@ -435,11 +435,11 @@ fn ser_vec_as_attribute() { numbers: vec![1, 2, 3, 4], strings: vec!["hello".to_string(), "world".to_string()], bools: vec![true, false, true], - floats: vec![3.14, 2.71], + floats: vec![6.14, 2.71], }; // Expected XML with space-separated attribute values - let content = r#""#; + let content = r#""#; serialize_and_validate!(model, content); } diff --git a/yaserde_derive/src/common/field.rs b/yaserde_derive/src/common/field.rs index 06f650c..5ed8715 100644 --- a/yaserde_derive/src/common/field.rs +++ b/yaserde_derive/src/common/field.rs @@ -101,11 +101,11 @@ impl YaSerdeField { .map(|p| format!("{}_", p.to_upper_camel_case())) .unwrap_or_default(); - let attribute = self - .attributes - .attribute - .then_some("Attribute_".to_string()) - .unwrap_or_default(); + let attribute = if self.attributes.attribute { + "Attribute_" + } else { + Default::default() + }; Ident::new( &format!( From d217b707a99219be42efce93a8daeb8cf91e3425 Mon Sep 17 00:00:00 2001 From: John Hughes Date: Sun, 7 Sep 2025 11:25:43 +0200 Subject: [PATCH 10/13] feat: add support for Option as an attribute in serialization and deserialization --- yaserde/tests/deserializer.rs | 31 ++++++++++++++++++++ yaserde/tests/serializer.rs | 28 ++++++++++++++++++ yaserde_derive/src/de/expand_struct.rs | 37 ++++++++++++++++++++++-- yaserde_derive/src/ser/element.rs | 4 --- yaserde_derive/src/ser/expand_struct.rs | 38 ++++++++++++++++--------- 5 files changed, 118 insertions(+), 20 deletions(-) diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index 8d92265..1b5e528 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -1102,6 +1102,37 @@ fn de_attribute_sequence() { deserialize_and_validate!(content, model, Outer); } +#[test] +fn de_option_vec_as_attribute() { + init(); + + #[derive(YaDeserialize, PartialEq, Debug)] + #[yaserde(rename = "TestTag")] + pub struct OptionVecAttributeStruct { + #[yaserde(attribute = true)] + field: Option>, + } + + // Test case 1: Some(populated_vec) -> field="1 2 3 4" + let content = r#""#; + let model = OptionVecAttributeStruct { + field: Some(vec![1, 2, 3, 4]), + }; + convert_and_validate!(content, OptionVecAttributeStruct, model); + + // Test case 2: Some(empty_vec) -> field="" + let content = r#""#; + let model = OptionVecAttributeStruct { + field: Some(vec![]), + }; + convert_and_validate!(content, OptionVecAttributeStruct, model); + + // Test case 3: None -> no attribute + let content = r#""#; + let model = OptionVecAttributeStruct { field: None }; + convert_and_validate!(content, OptionVecAttributeStruct, model); +} + #[test] fn de_nested_macro_rules() { init(); diff --git a/yaserde/tests/serializer.rs b/yaserde/tests/serializer.rs index a945fc1..93abb57 100644 --- a/yaserde/tests/serializer.rs +++ b/yaserde/tests/serializer.rs @@ -467,3 +467,31 @@ fn ser_vec_as_attribute_nested() { let content = r#""#; serialize_and_validate!(model, content); } + +#[test] +fn ser_option_vec_as_attribute() { + #[derive(YaSerialize, PartialEq, Debug)] + #[yaserde(rename = "TestTag")] + pub struct OptionVecAttributeStruct { + #[yaserde(attribute = true)] + field: Option>, + } + + // Expected XML with space-separated attribute values + let model = OptionVecAttributeStruct { + field: Some(vec![1, 2, 3, 4]), + }; + let content = r#""#; + serialize_and_validate!(model, content); + + let model = OptionVecAttributeStruct { + field: Some(vec![]), + }; + let content = r#""#; + serialize_and_validate!(model, content); + + // Expected XML with no attributes + let model = OptionVecAttributeStruct { field: None }; + let content = r#""#; + serialize_and_validate!(model, content); +} diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index 7a6a81b..e3118c4 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -126,7 +126,12 @@ pub fn parse( Field::FieldStruct { struct_name } => struct_visitor(struct_name), Field::FieldOption { data_type } => match *data_type { Field::FieldStruct { struct_name } => struct_visitor(struct_name), - Field::FieldOption { .. } | Field::FieldVec { .. } => None, + Field::FieldOption { .. } => None, + Field::FieldVec { data_type } => match *data_type { + Field::FieldStruct { struct_name } => struct_visitor(struct_name), + Field::FieldOption { .. } | Field::FieldVec { .. } => None, + simple_type => simple_type_visitor(simple_type), + }, simple_type => simple_type_visitor(simple_type), }, Field::FieldVec { data_type } => match *data_type { @@ -259,6 +264,23 @@ pub fn parse( }) }; + let visit_option_vec = |visitor: &Ident, visitor_label: &Ident| { + Some(quote! { + for attr in attributes { + if attr.name.local_name == #label_name { + if #label.is_none() { + #label = Some(Vec::new()); + } + for value in attr.value.split_whitespace() { + let visitor = #visitor_label{}; + let value = visitor.#visitor(value)?; + #label.as_mut().unwrap().push(value); + } + } + } + }) + }; + let visit_string = || { Some(quote! { for attr in attributes { @@ -286,7 +308,18 @@ pub fn parse( }; let visit_sub = |sub_type: Box, action: TokenStream| match *sub_type { - Field::FieldOption { .. } | Field::FieldVec { .. } => unimplemented!(), + Field::FieldOption { .. } => unimplemented!(), + Field::FieldVec { data_type } => match data_type.as_ref() { + Field::FieldStruct { struct_name } => visit_option_vec( + &Ident::new("visit_str", field.get_span()), + &field.get_visitor_ident(Some(struct_name)), + ), + Field::FieldOption { .. } | Field::FieldVec { .. } => unimplemented!("Not supported"), + simple_type => visit_option_vec( + &simple_type.get_simple_type_visitor(), + &field.get_visitor_ident(None), + ), + }, Field::FieldStruct { struct_name } => visit_struct(struct_name, action), simple_type => visit_simple(simple_type, action), }; diff --git a/yaserde_derive/src/ser/element.rs b/yaserde_derive/src/ser/element.rs index 0f2fb0c..24e8cd3 100644 --- a/yaserde_derive/src/ser/element.rs +++ b/yaserde_derive/src/ser/element.rs @@ -2,10 +2,6 @@ use crate::common::YaSerdeField; use proc_macro2::{Ident, TokenStream}; use quote::quote; -pub fn enclose_formatted_characters(label: &Ident, label_name: String) -> TokenStream { - enclose_xml_event(label_name, quote!(format!("{}", &self.#label))) -} - pub fn enclose_formatted_characters_for_value(label: &Ident, label_name: String) -> TokenStream { enclose_xml_event(label_name, quote!(format!("{}", #label))) } diff --git a/yaserde_derive/src/ser/expand_struct.rs b/yaserde_derive/src/ser/expand_struct.rs index 0f31e31..15ae6e0 100644 --- a/yaserde_derive/src/ser/expand_struct.rs +++ b/yaserde_derive/src/ser/expand_struct.rs @@ -75,21 +75,31 @@ pub fn serialize( } }), ), - Field::FieldVec { .. } => { - let item_ident = Ident::new("yaserde_item", field.get_span()); - let inner = enclose_formatted_characters(&item_ident, label_name); - - field.ser_wrap_default_attribute( - None, - quote!({ - if let ::std::option::Option::Some(ref yaserde_list) = self.#label { - for yaserde_item in yaserde_list.iter() { - #inner + Field::FieldVec { .. } => field.ser_wrap_default_attribute( + Some(quote! { + self.#label + .as_ref() + .map_or_else( + || ::std::string::String::new(), + |yaserde_list| { + yaserde_list + .iter() + .map(|item| item.to_string()) + .collect::<::std::vec::Vec<_>>() + .join(" ") } - } - }), - ) - } + ) + }), + quote!({ + if self.#label.is_some() && !yaserde_inner.is_empty() { + struct_start_event.attr(#label_name, &yaserde_inner) + } else if self.#label.is_some() { + struct_start_event.attr(#label_name, "") + } else { + struct_start_event + } + }), + ), Field::FieldStruct { .. } => field.ser_wrap_default_attribute( Some(quote! { self.#label From dc1caa462b6cbb90950bb3197484b27ce8b713d2 Mon Sep 17 00:00:00 2001 From: John Hughes Date: Sun, 7 Sep 2025 13:25:56 +0200 Subject: [PATCH 11/13] feat: add serialization and deserialization support for Option> attributes --- yaserde/tests/deserializer.rs | 39 ++++++++++++ yaserde/tests/serializer.rs | 34 ++++++++++ yaserde_derive/src/ser/expand_struct.rs | 85 +++++++++++++++++-------- 3 files changed, 133 insertions(+), 25 deletions(-) diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index 1b5e528..5aeee70 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -1133,6 +1133,45 @@ fn de_option_vec_as_attribute() { convert_and_validate!(content, OptionVecAttributeStruct, model); } +#[test] +fn de_option_vec_enum_as_attribute() { + init(); + + #[derive(YaDeserialize, PartialEq, Debug, Default)] + enum MyEnum { + #[default] + One, + Two, + Three, + } + + #[derive(YaDeserialize, PartialEq, Debug)] + #[yaserde(rename = "TestTag")] + pub struct OptionVecEnumAttributeStruct { + #[yaserde(attribute = true)] + field: Option>, + } + + // Test case 1: Some(vec![MyEnum::One, MyEnum::Two, MyEnum::Three]) -> field="One Two Three" + let content = r#""#; + let model = OptionVecEnumAttributeStruct { + field: Some(vec![MyEnum::One, MyEnum::Two, MyEnum::Three]), + }; + convert_and_validate!(content, OptionVecEnumAttributeStruct, model); + + // Test case 2: Some(empty_vec) -> field="" + let content = r#""#; + let model = OptionVecEnumAttributeStruct { + field: Some(vec![]), + }; + convert_and_validate!(content, OptionVecEnumAttributeStruct, model); + + // Test case 3: None -> no attribute + let content = r#""#; + let model = OptionVecEnumAttributeStruct { field: None }; + convert_and_validate!(content, OptionVecEnumAttributeStruct, model); +} + #[test] fn de_nested_macro_rules() { init(); diff --git a/yaserde/tests/serializer.rs b/yaserde/tests/serializer.rs index 93abb57..8330fa2 100644 --- a/yaserde/tests/serializer.rs +++ b/yaserde/tests/serializer.rs @@ -495,3 +495,37 @@ fn ser_option_vec_as_attribute() { let content = r#""#; serialize_and_validate!(model, content); } + +#[test] +fn ser_option_vec_enum_as_attribute() { + #[derive(YaSerialize, PartialEq, Debug)] + enum MyEnum { + One, + Two, + Three, + } + + #[derive(YaSerialize, PartialEq, Debug)] + #[yaserde(rename = "TestTag")] + pub struct OptionVecEnumAttributeStruct { + #[yaserde(attribute = true)] + field: Option>, + } + + // Expected XML with space-separated attribute values + let model = OptionVecEnumAttributeStruct { + field: Some(vec![MyEnum::One, MyEnum::Two, MyEnum::Three]), + }; + let content = r#""#; + serialize_and_validate!(model, content); + + let model = OptionVecEnumAttributeStruct { + field: Some(vec![]), + }; + let content = r#""#; + serialize_and_validate!(model, content); + + let model = OptionVecEnumAttributeStruct { field: None }; + let content = r#""#; + serialize_and_validate!(model, content); +} diff --git a/yaserde_derive/src/ser/expand_struct.rs b/yaserde_derive/src/ser/expand_struct.rs index 15ae6e0..e0548f0 100644 --- a/yaserde_derive/src/ser/expand_struct.rs +++ b/yaserde_derive/src/ser/expand_struct.rs @@ -75,31 +75,31 @@ pub fn serialize( } }), ), - Field::FieldVec { .. } => field.ser_wrap_default_attribute( - Some(quote! { - self.#label - .as_ref() - .map_or_else( - || ::std::string::String::new(), - |yaserde_list| { - yaserde_list - .iter() - .map(|item| item.to_string()) - .collect::<::std::vec::Vec<_>>() - .join(" ") - } - ) - }), - quote!({ - if self.#label.is_some() && !yaserde_inner.is_empty() { - struct_start_event.attr(#label_name, &yaserde_inner) - } else if self.#label.is_some() { - struct_start_event.attr(#label_name, "") - } else { - struct_start_event - } - }), - ), + Field::FieldVec { data_type } => match *data_type { + Field::FieldString + | Field::FieldBool + | Field::FieldI8 + | Field::FieldU8 + | Field::FieldI16 + | Field::FieldU16 + | Field::FieldI32 + | Field::FieldU32 + | Field::FieldI64 + | Field::FieldU64 + | Field::FieldF32 + | Field::FieldF64 => { + ser_option_vec_attribute(&field, &label, &label_name, quote!(item.to_string())) + } + Field::FieldStruct { .. } => ser_option_vec_attribute( + &field, + &label, + &label_name, + quote!(::yaserde::ser::to_string_content(item).unwrap_or_default()), + ), + _ => { + unimplemented!("Complex data types in Option> attributes not yet supported") + } + }, Field::FieldStruct { .. } => field.ser_wrap_default_attribute( Some(quote! { self.#label @@ -410,3 +410,38 @@ pub fn serialize( generics, ) } + +/// Helper function to generate serialization code for Option> attributes +fn ser_option_vec_attribute( + field: &YaSerdeField, + label: &Option, + label_name: &str, + item_serializer: TokenStream, +) -> TokenStream { + let yaserde_inner_expr = quote! { + self.#label + .as_ref() + .map_or_else( + || ::std::string::String::new(), + |yaserde_list| { + yaserde_list + .iter() + .map(|item| #item_serializer) + .collect::<::std::vec::Vec<_>>() + .join(" ") + } + ) + }; + + let attribute_expr = quote!({ + if self.#label.is_some() && !yaserde_inner.is_empty() { + struct_start_event.attr(#label_name, &yaserde_inner) + } else if self.#label.is_some() { + struct_start_event.attr(#label_name, "") + } else { + struct_start_event + } + }); + + field.ser_wrap_default_attribute(Some(yaserde_inner_expr), attribute_expr) +} From 44968530ae6fbb0d468dd040d31594f100479d10 Mon Sep 17 00:00:00 2001 From: John Hughes Date: Sun, 7 Sep 2025 19:51:05 +0200 Subject: [PATCH 12/13] feat: implement serialization support for Option> attributes with attribute distinction --- yaserde/tests/serializer.rs | 69 ++++++++++++++++--------- yaserde_derive/src/de/expand_struct.rs | 43 +++++++++++++-- yaserde_derive/src/ser/expand_struct.rs | 34 ++++++++---- 3 files changed, 109 insertions(+), 37 deletions(-) diff --git a/yaserde/tests/serializer.rs b/yaserde/tests/serializer.rs index 8330fa2..f2770e6 100644 --- a/yaserde/tests/serializer.rs +++ b/yaserde/tests/serializer.rs @@ -497,35 +497,56 @@ fn ser_option_vec_as_attribute() { } #[test] -fn ser_option_vec_enum_as_attribute() { - #[derive(YaSerialize, PartialEq, Debug)] - enum MyEnum { - One, - Two, - Three, +fn ser_option_vec_complex() { + #[derive(Default, PartialEq, Debug, YaSerialize)] + pub struct Start { + #[yaserde(attribute = true, rename = "value")] + pub value: String, } - #[derive(YaSerialize, PartialEq, Debug)] - #[yaserde(rename = "TestTag")] - pub struct OptionVecEnumAttributeStruct { - #[yaserde(attribute = true)] - field: Option>, + #[derive(Default, PartialEq, Debug, YaSerialize)] + #[yaserde(rename = "String")] + pub struct StringStruct { + #[yaserde(rename = "Start")] + pub start: Option>, } - // Expected XML with space-separated attribute values - let model = OptionVecEnumAttributeStruct { - field: Some(vec![MyEnum::One, MyEnum::Two, MyEnum::Three]), + // Test serialization with Some(vec) + let model = StringStruct { + start: Some(vec![ + Start { + value: "First string".to_string(), + }, + Start { + value: "Second string".to_string(), + }, + Start { + value: "Third string".to_string(), + }, + ]), }; - let content = r#""#; - serialize_and_validate!(model, content); - let model = OptionVecEnumAttributeStruct { - field: Some(vec![]), - }; - let content = r#""#; - serialize_and_validate!(model, content); + let content = yaserde::ser::to_string(&model).unwrap(); + assert_eq!( + content, + "" + ); - let model = OptionVecEnumAttributeStruct { field: None }; - let content = r#""#; - serialize_and_validate!(model, content); + // Test serialization with None + let model_none = StringStruct { start: None }; + let content_none = yaserde::ser::to_string(&model_none).unwrap(); + assert_eq!( + content_none, + "" + ); + + // Test serialization with Some(empty_vec) + let model_empty = StringStruct { + start: Some(vec![]), + }; + let content_empty = yaserde::ser::to_string(&model_empty).unwrap(); + assert_eq!( + content_empty, + "" + ); } diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index e3118c4..266eb9b 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -24,7 +24,18 @@ pub fn parse( .map(|field| YaSerdeField::new(field.clone())) .filter_map(|field| match field.get_type() { Field::FieldStruct { struct_name } => build_default_value(&field, Some(quote!(#struct_name))), - Field::FieldOption { .. } => build_default_value(&field, None), + Field::FieldOption { data_type } => match *data_type { + Field::FieldVec { data_type: inner_data_type } => match *inner_data_type { + Field::FieldStruct { ref struct_name } => { + build_default_value(&field, Some(quote!(::std::vec::Vec<#struct_name>))) + } + simple_type => { + let type_token: TokenStream = simple_type.into(); + build_default_value(&field, Some(quote!(::std::vec::Vec<#type_token>))) + } + }, + _ => build_default_value(&field, None), + }, Field::FieldVec { data_type } => match *data_type { Field::FieldStruct { ref struct_name } => { build_default_vec_value(&field, Some(quote!(::std::vec::Vec<#struct_name>))) @@ -196,9 +207,33 @@ pub fn parse( Field::FieldStruct { struct_name } => { visit_struct(struct_name, quote! { = ::std::option::Option::Some(value) }) } - Field::FieldOption { data_type } => { - visit_sub(data_type, quote! { = ::std::option::Option::Some(value) }) - } + Field::FieldOption { data_type } => match *data_type { + Field::FieldVec { data_type: inner_data_type } => match *inner_data_type { + Field::FieldStruct { struct_name } => { + // Handle Option> + visit_struct(struct_name, quote! { + = if #value_label.is_some() { + #value_label.as_mut().unwrap().push(value); + #value_label + } else { + Some(vec![value]) + } + }) + } + simple_type => { + // Handle Option> + visit_simple(simple_type, quote! { + = if #value_label.is_some() { + #value_label.as_mut().unwrap().push(value); + #value_label + } else { + Some(vec![value]) + } + }) + } + }, + _ => visit_sub(data_type, quote! { = ::std::option::Option::Some(value) }), + }, Field::FieldVec { data_type } => visit_sub(data_type, quote! { .push(value) }), simple_type => visit_simple(simple_type, quote! { = ::std::option::Option::Some(value) }), } diff --git a/yaserde_derive/src/ser/expand_struct.rs b/yaserde_derive/src/ser/expand_struct.rs index e0548f0..9cf8b51 100644 --- a/yaserde_derive/src/ser/expand_struct.rs +++ b/yaserde_derive/src/ser/expand_struct.rs @@ -259,18 +259,34 @@ pub fn serialize( }) } Field::FieldVec { .. } => { - let item_ident = Ident::new("yaserde_item", field.get_span()); - let inner = enclose_formatted_characters_for_value(&item_ident, label_name); + // Only use attribute serialization if the field is marked as an attribute + if field.is_attribute() { + let item_ident = Ident::new("yaserde_item", field.get_span()); + let inner = enclose_formatted_characters_for_value(&item_ident, label_name); - Some(quote! { - #conditions { - if let ::std::option::Option::Some(ref yaserde_items) = &self.#label { - for yaserde_item in yaserde_items.iter() { - #inner + Some(quote! { + #conditions { + if let ::std::option::Option::Some(ref yaserde_items) = &self.#label { + for yaserde_item in yaserde_items.iter() { + #inner + } } } - } - }) + }) + } else { + // For non-attribute Option>, use standard serialization + Some(quote! { + #conditions { + if let ::std::option::Option::Some(ref items) = &self.#label { + for item in items.iter() { + writer.set_start_event_name(::std::option::Option::Some(#label_name.to_string())); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + } + }) + } } Field::FieldStruct { .. } => Some(if field.is_flatten() { quote! { From 679c1c602c6c663ab305c1a8282b855f555863cd Mon Sep 17 00:00:00 2001 From: John Hughes Date: Mon, 15 Sep 2025 16:43:52 +0200 Subject: [PATCH 13/13] fix formatting --- yaserde_derive/src/de/expand_struct.rs | 46 ++++++++++++++++---------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index 266eb9b..17dfdd4 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -25,7 +25,9 @@ pub fn parse( .filter_map(|field| match field.get_type() { Field::FieldStruct { struct_name } => build_default_value(&field, Some(quote!(#struct_name))), Field::FieldOption { data_type } => match *data_type { - Field::FieldVec { data_type: inner_data_type } => match *inner_data_type { + Field::FieldVec { + data_type: inner_data_type, + } => match *inner_data_type { Field::FieldStruct { ref struct_name } => { build_default_value(&field, Some(quote!(::std::vec::Vec<#struct_name>))) } @@ -208,28 +210,36 @@ pub fn parse( visit_struct(struct_name, quote! { = ::std::option::Option::Some(value) }) } Field::FieldOption { data_type } => match *data_type { - Field::FieldVec { data_type: inner_data_type } => match *inner_data_type { + Field::FieldVec { + data_type: inner_data_type, + } => match *inner_data_type { Field::FieldStruct { struct_name } => { // Handle Option> - visit_struct(struct_name, quote! { - = if #value_label.is_some() { - #value_label.as_mut().unwrap().push(value); - #value_label - } else { - Some(vec![value]) - } - }) + visit_struct( + struct_name, + quote! { + = if #value_label.is_some() { + #value_label.as_mut().unwrap().push(value); + #value_label + } else { + Some(vec![value]) + } + }, + ) } simple_type => { // Handle Option> - visit_simple(simple_type, quote! { - = if #value_label.is_some() { - #value_label.as_mut().unwrap().push(value); - #value_label - } else { - Some(vec![value]) - } - }) + visit_simple( + simple_type, + quote! { + = if #value_label.is_some() { + #value_label.as_mut().unwrap().push(value); + #value_label + } else { + Some(vec![value]) + } + }, + ) } }, _ => visit_sub(data_type, quote! { = ::std::option::Option::Some(value) }),