diff --git a/yaserde/tests/se_namespace.rs b/yaserde/tests/se_namespace.rs index 981307c..0314d56 100644 --- a/yaserde/tests/se_namespace.rs +++ b/yaserde/tests/se_namespace.rs @@ -183,7 +183,7 @@ fn ser_struct_default_namespace_via_attribute() { } #[test] -fn de_struct_namespace_nested() { +fn ser_struct_namespace_nested() { #[derive(YaSerialize, Default, PartialEq, Debug)] #[yaserde(prefix = "nsa", namespace = "nsa: http://www.sample.com/ns/a")] struct A { diff --git a/yaserde/tests/ser_skip.rs b/yaserde/tests/ser_skip.rs new file mode 100644 index 0000000..fef63a8 --- /dev/null +++ b/yaserde/tests/ser_skip.rs @@ -0,0 +1,68 @@ +extern crate log; +extern crate xml; +extern crate yaserde; +#[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_skip_serializing_if_for_struct() { + #[derive(YaSerialize, PartialEq, Debug)] + #[yaserde(root = "base")] + pub struct XmlStruct { + #[yaserde(skip_serializing_if = "check_string_function")] + string_item: String, + #[yaserde(skip_serializing_if = "check_bool_function")] + bool_item: bool, + #[yaserde(skip_serializing_if = "check_f32_function")] + f32_item: f32, + #[yaserde(skip_serializing_if = "check_option_string_function")] + option_string_item: Option, + } + + impl XmlStruct { + fn check_string_function(&self, value: &String) -> bool { + value == "something" + } + + fn check_option_string_function(&self, value: &Option) -> bool { + value == &Some("something".to_string()) + } + + fn check_bool_function(&self, value: &bool) -> bool { + value == &true + } + + fn check_f32_function(&self, value: &f32) -> bool { + value == &0.0 + } + } + + let model = XmlStruct { + string_item: "something".to_string(), + bool_item: true, + f32_item: 0.0, + option_string_item: Some("something".to_string()), + }; + + let content = ""; + convert_and_validate!(model, content); +} diff --git a/yaserde_derive/src/attribute.rs b/yaserde_derive/src/attribute.rs index f5177a4..6504dad 100644 --- a/yaserde_derive/src/attribute.rs +++ b/yaserde_derive/src/attribute.rs @@ -14,6 +14,7 @@ pub struct YaSerdeAttribute { pub prefix: Option, pub root: Option, pub rename: Option, + pub skip_serializing_if: Option, pub text: bool, } @@ -41,6 +42,7 @@ impl YaSerdeAttribute { let mut prefix = None; let mut rename = None; let mut root = None; + let mut skip_serializing_if = None; let mut text = false; for attr in attrs.iter() { @@ -85,6 +87,9 @@ impl YaSerdeAttribute { "root" => { root = get_value(&mut attr_iter); } + "skip_serializing_if" => { + skip_serializing_if = get_value(&mut attr_iter); + } "text" => { text = true; } @@ -106,6 +111,7 @@ impl YaSerdeAttribute { prefix, rename, root, + skip_serializing_if, text, } } @@ -126,6 +132,7 @@ fn parse_empty_attributes() { prefix: None, root: None, rename: None, + skip_serializing_if: None, text: false, }, attrs @@ -175,6 +182,7 @@ fn parse_attributes() { prefix: None, root: None, rename: None, + skip_serializing_if: None, text: false, }, attrs @@ -228,6 +236,7 @@ fn parse_attributes_with_values() { prefix: None, root: None, rename: None, + skip_serializing_if: None, text: false, }, attrs diff --git a/yaserde_derive/src/ser/element.rs b/yaserde_derive/src/ser/element.rs index 69a77c1..5a18601 100644 --- a/yaserde_derive/src/ser/element.rs +++ b/yaserde_derive/src/ser/element.rs @@ -1,55 +1,50 @@ +use attribute::*; use proc_macro2::{Ident, Span, TokenStream}; pub fn enclose_formatted_characters(label: &Ident, label_name: String) -> TokenStream { - quote! { - let start_event = XmlEvent::start_element(#label_name); - let _ret = writer.write(start_event); - - let yas_value = format!("{}", &self.#label); - let data_event = XmlEvent::characters(&yas_value); - let _ret = writer.write(data_event); - - let end_event = XmlEvent::end_element(); - let _ret = writer.write(end_event); - } + enclose_xml_event(label_name, quote!(format!("{}", &self.#label))) } pub fn enclose_formatted_characters_for_value(label: &Ident, label_name: String) -> TokenStream { - quote! { - let start_event = XmlEvent::start_element(#label_name); - let _ret = writer.write(start_event); - - let value = format!("{}", #label); - let data_event = XmlEvent::characters(&value); - let _ret = writer.write(data_event); - - let end_event = XmlEvent::end_element(); - let _ret = writer.write(end_event); - } + enclose_xml_event(label_name, quote!(format!("{}", #label))) } pub fn enclose_characters(label: &Option, label_name: String) -> TokenStream { + enclose_xml_event(label_name, quote!(format!("{}", self.#label))) +} + +pub fn enclose_xml_event(label_name: String, yaserde_format: TokenStream) -> TokenStream { quote! { let start_event = XmlEvent::start_element(#label_name); - let _ret = writer.write(start_event); + writer.write(start_event).map_err(|e| e.to_string())?; - let value = format!("{}", self.#label); - let data_event = XmlEvent::characters(&value); - let _ret = writer.write(data_event); + let yaserde_value = #yaserde_format; + let data_event = XmlEvent::characters(&yaserde_value); + writer.write(data_event).map_err(|e| e.to_string())?; let end_event = XmlEvent::end_element(); - let _ret = writer.write(end_event); + writer.write(end_event).map_err(|e| e.to_string())?; } } pub fn serialize_element( label: &Option, label_name: String, - default: &Option, + conditions: &TokenStream, ) -> Option { let inner = enclose_characters(label, label_name); - if let Some(ref d) = default { + Some(quote! { + #conditions { + #inner + } + }) +} + +pub fn condition_generator(label: &Option, attributes: &YaSerdeAttribute) -> TokenStream { + let mut conditions = None; + + if let Some(ref d) = attributes.default { let default_function = Ident::new( &d, label @@ -57,14 +52,23 @@ pub fn serialize_element( .map_or(Span::call_site(), |ident| ident.span()), ); - Some(quote! { - if self.#label != #default_function() { - #inner - } - }) - } else { - Some(quote! { - #inner - }) + conditions = Some(quote!(self.#label != #default_function())) } + + if let Some(ref s) = attributes.skip_serializing_if { + let skip_if_function = Ident::new( + &s, + label + .as_ref() + .map_or(Span::call_site(), |ident| ident.span()), + ); + + conditions = if let Some(prev_conditions) = conditions { + Some(quote!(!#skip_if_function() && #prev_conditions)) + } else { + Some(quote!(!self.#skip_if_function(&self.#label))) + }; + } + + conditions.map(|c| quote!(if #c)).unwrap_or(quote!()) } diff --git a/yaserde_derive/src/ser/expand_enum.rs b/yaserde_derive/src/ser/expand_enum.rs index 713eba5..32f50ed 100644 --- a/yaserde_derive/src/ser/expand_enum.rs +++ b/yaserde_derive/src/ser/expand_enum.rs @@ -33,7 +33,7 @@ pub fn serialize( Fields::Unit => Some(quote! { &#name::#label => { let data_event = XmlEvent::characters(#label_name); - let _ret = writer.write(data_event); + writer.write(data_event).map_err(|e| e.to_string())?; } }), Fields::Named(ref fields) => { @@ -50,7 +50,7 @@ pub fn serialize( if field_attrs.text { return Some(quote!( let data_event = XmlEvent::characters(&self.#field_label); - let _ret = writer.write(data_event); + writer.write(data_event).map_err(|e| e.to_string())?; )); } @@ -61,19 +61,21 @@ pub fn serialize( let field_label_name = renamed_field_label.unwrap().to_string(); match get_field_type(field) { - Some(FieldType::FieldTypeString) => Some(quote! { - match self { - &#name::#label{ref #field_label, ..} => { - let struct_start_event = XmlEvent::start_element(#field_label_name); - let _ret = writer.write(struct_start_event); + Some(FieldType::FieldTypeString) => Some({ + quote! { + match self { + &#name::#label{ref #field_label, ..} => { + let struct_start_event = XmlEvent::start_element(#field_label_name); + writer.write(struct_start_event).map_err(|e| e.to_string())?; - let data_event = XmlEvent::characters(#field_label); - let _ret = writer.write(data_event); + let data_event = XmlEvent::characters(#field_label); + writer.write(data_event).map_err(|e| e.to_string())?; - let struct_end_event = XmlEvent::end_element(); - let _ret = writer.write(struct_end_event); - }, - _ => {}, + let struct_end_event = XmlEvent::end_element(); + writer.write(struct_end_event).map_err(|e| e.to_string())?; + }, + _ => {}, + } } }), Some(FieldType::FieldTypeStruct { .. }) => Some(quote! { @@ -123,24 +125,24 @@ pub fn serialize( let write_element = |action: &TokenStream| { quote! { let struct_start_event = XmlEvent::start_element(#label_name); - let _ret = writer.write(struct_start_event); + writer.write(struct_start_event).map_err(|e| e.to_string())?; #action let struct_end_event = XmlEvent::end_element(); - let _ret = writer.write(struct_end_event); + writer.write(struct_end_event).map_err(|e| e.to_string())?; } }; let write_string_chars = quote! { let data_event = XmlEvent::characters(item); - let _ret = writer.write(data_event); + writer.write(data_event).map_err(|e| e.to_string())?; }; let write_simple_type = write_element("e! { let s = item.to_string(); let data_event = XmlEvent::characters(&s); - let _ret = writer.write(data_event); + writer.write(data_event).map_err(|e| e.to_string())?; }); let serialize = quote! { @@ -239,10 +241,10 @@ pub fn serialize( if !skip { if let Some(label) = writer.get_start_event_name() { let struct_start_event = XmlEvent::start_element(label.as_ref()); - let _ret = writer.write(struct_start_event); + writer.write(struct_start_event).map_err(|e| e.to_string())?; } else { let struct_start_event = XmlEvent::start_element(#root)#add_namespaces; - let _ret = writer.write(struct_start_event); + writer.write(struct_start_event).map_err(|e| e.to_string())?; } } @@ -252,7 +254,7 @@ pub fn serialize( if !skip { let struct_end_event = XmlEvent::end_element(); - let _ret = writer.write(struct_end_event); + writer.write(struct_end_event).map_err(|e| e.to_string())?; } Ok(()) diff --git a/yaserde_derive/src/ser/expand_struct.rs b/yaserde_derive/src/ser/expand_struct.rs index 6b5b704..3c94d4b 100644 --- a/yaserde_derive/src/ser/expand_struct.rs +++ b/yaserde_derive/src/ser/expand_struct.rs @@ -126,7 +126,7 @@ pub fn serialize( } } FieldType::FieldTypeVec { .. } => { - let item_ident = Ident::new("yas_item", field.span()); + let item_ident = Ident::new("yaserde_item", field.span()); let inner = enclose_formatted_characters(&item_ident, label_name); if let Some(ref d) = field_attrs.default { @@ -134,8 +134,8 @@ pub fn serialize( Some(quote! { if self.#label != #default_function() { - if let Some(ref yas_list) = self.#label { - for yas_item in yas_list.iter() { + if let Some(ref yaserde_list) = self.#label { + for yaserde_item in yaserde_list.iter() { #inner } } @@ -143,7 +143,7 @@ pub fn serialize( }) } else { Some(quote! { - for yas_item in &self.#label { + for yaserde_item in &self.#label { #inner } }) @@ -236,11 +236,12 @@ pub fn serialize( if field_attrs.text { return Some(quote!( let data_event = XmlEvent::characters(&self.#label); - let _ret = writer.write(data_event); + writer.write(data_event).map_err(|e| e.to_string())?; )); } let label_name = build_label_name(&field, &field_attrs); + let conditions = condition_generator(label, &field_attrs); get_field_type(field).and_then(|f| match f { FieldType::FieldTypeString @@ -254,7 +255,7 @@ pub fn serialize( | FieldType::FieldTypeI64 | FieldType::FieldTypeU64 | FieldType::FieldTypeF32 - | FieldType::FieldTypeF64 => serialize_element(label, label_name, &field_attrs.default), + | FieldType::FieldTypeF64 => serialize_element(label, label_name, &conditions), FieldType::FieldTypeOption { data_type } => match *data_type { FieldType::FieldTypeString | FieldType::FieldTypeBool @@ -268,52 +269,30 @@ pub fn serialize( | FieldType::FieldTypeU64 | FieldType::FieldTypeF32 | FieldType::FieldTypeF64 => { - let item_ident = Ident::new("yas_item", field.span()); + let item_ident = Ident::new("yaserde_item", field.span()); let inner = enclose_formatted_characters_for_value(&item_ident, label_name); - if let Some(ref d) = field_attrs.default { - let default_function = Ident::new(&d, field.span()); - - Some(quote! { - if self.#label != #default_function() { - if let Some(ref yas_item) = self.#label { - #inner - } - } - }) - } else { - Some(quote! { - if let Some(ref yas_item) = self.#label { + Some(quote! { + #conditions { + if let Some(ref yaserde_item) = self.#label { #inner } - }) - } + } + }) } FieldType::FieldTypeVec { .. } => { - let item_ident = Ident::new("yas_item", field.span()); + let item_ident = Ident::new("yaserde_item", field.span()); let inner = enclose_formatted_characters_for_value(&item_ident, label_name); - if let Some(ref d) = field_attrs.default { - let default_function = Ident::new(&d, field.span()); - - Some(quote! { - if self.#label != #default_function() { - if let Some(ref yas_items) = &self.#label { - for yas_item in yas_items.iter() { - #inner - } - } - } - }) - } else { - Some(quote! { - if let Some(ref yas_items) = &self.#label { - for yas_item in yas_items.iter() { + Some(quote! { + #conditions { + if let Some(ref yaserde_items) = &self.#label { + for yaserde_item in yaserde_items.iter() { #inner } } - }) - } + } + }) } FieldType::FieldTypeStruct { .. } => Some(if field_attrs.flatten { quote! { @@ -334,26 +313,26 @@ pub fn serialize( }), _ => unimplemented!(), }, - FieldType::FieldTypeStruct { .. } => Some(if field_attrs.flatten { - quote! { - writer.set_start_event_name(None); - writer.set_skip_start_end(true); + FieldType::FieldTypeStruct { .. } => { + let (start_event, skip_start) = if field_attrs.flatten { + (quote!(None), true) + } else { + (quote!(Some(#label_name.to_string())), false) + }; + + Some(quote! { + writer.set_start_event_name(#start_event); + writer.set_skip_start_end(#skip_start); self.#label.serialize(writer)?; - } - } else { - quote! { - writer.set_start_event_name(Some(#label_name.to_string())); - writer.set_skip_start_end(false); - self.#label.serialize(writer)?; - } - }), + }) + } FieldType::FieldTypeVec { data_type } => match *data_type { FieldType::FieldTypeString => { - let item_ident = Ident::new("yas_item", field.span()); + let item_ident = Ident::new("yaserde_item", field.span()); let inner = enclose_formatted_characters_for_value(&item_ident, label_name); Some(quote! { - for yas_item in &self.#label { + for yaserde_item in &self.#label { #inner } }) @@ -369,11 +348,11 @@ pub fn serialize( | FieldType::FieldTypeU64 | FieldType::FieldTypeF32 | FieldType::FieldTypeF64 => { - let item_ident = Ident::new("yas_item", field.span()); + let item_ident = Ident::new("yaserde_item", field.span()); let inner = enclose_formatted_characters_for_value(&item_ident, label_name); Some(quote! { - for yas_item in &self.#label { + for yaserde_item in &self.#label { #inner } }) @@ -413,17 +392,17 @@ pub fn serialize( let skip = writer.skip_start_end(); if !skip { - let label = writer.get_start_event_name().unwrap_or_else(|| #root.to_string()); - let struct_start_event = XmlEvent::start_element(label.as_ref())#add_namespaces; + 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 - let _ret = writer.write(struct_start_event); + writer.write(struct_start_event).map_err(|e| e.to_string())?; } #struct_inspector if !skip { let struct_end_event = XmlEvent::end_element(); - let _ret = writer.write(struct_end_event); + writer.write(struct_end_event).map_err(|e| e.to_string())?; } Ok(())