From 5c3910c4202537aad036c2bde3e2c69cd5beeb13 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Arnaud Date: Tue, 10 Apr 2018 15:06:50 +0200 Subject: [PATCH] Serialize Enum --- yaserde/tests/serializer.rs | 65 +++++++++++ yaserde_derive/src/ser/expand_enum.rs | 150 ++++++++++++++++++++++++++ yaserde_derive/src/ser/mod.rs | 5 +- 3 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 yaserde_derive/src/ser/expand_enum.rs diff --git a/yaserde/tests/serializer.rs b/yaserde/tests/serializer.rs index e1f9090..39dbe07 100644 --- a/yaserde/tests/serializer.rs +++ b/yaserde/tests/serializer.rs @@ -200,3 +200,68 @@ fn ser_text_content_with_attributes() { let content = "text_content"; convert_and_validate!(model, content); } + +#[test] +fn ser_enum() { + #[derive(YaSerialize, PartialEq, Debug)] + #[yaserde(root="base")] + pub struct XmlStruct { + color: Color + } + + #[derive(YaSerialize, PartialEq, Debug)] + #[yaserde(root="color")] + pub enum Color { + White, + Black, + #[yaserde(rename="custom")] + Custom{ + enabled: String, + color: RGBColor, + alpha: Alpha, + alphas: Vec, + } + } + + impl Default for Color { + fn default() -> Color { + Color::White + } + } + + #[derive(YaSerialize, PartialEq, Debug)] + pub struct RGBColor { + red: String, + green: String, + blue: String, + } + + #[derive(YaSerialize, PartialEq, Debug)] + pub enum Alpha { + Transparent, + Opaque, + } + + let model = XmlStruct{ + color: Color::Black + }; + + let content = "Black"; + convert_and_validate!(model, content); + + let model = XmlStruct{ + color: Color::Custom{ + enabled: "true".to_string(), + color: RGBColor{ + red: "0".to_string(), + green: "128".to_string(), + blue: "255".to_string(), + }, + alpha: Alpha::Opaque, + alphas: vec![Alpha::Opaque, Alpha::Transparent] + } + }; + + let content = "true0128255OpaqueOpaqueTransparent"; + convert_and_validate!(model, content); +} diff --git a/yaserde_derive/src/ser/expand_enum.rs b/yaserde_derive/src/ser/expand_enum.rs new file mode 100644 index 0000000..6985c14 --- /dev/null +++ b/yaserde_derive/src/ser/expand_enum.rs @@ -0,0 +1,150 @@ + +use attribute::*; +use field_type::*; +use quote::Tokens; +use syn::Fields; +use syn::Ident; +use syn::DataEnum; +use proc_macro2::Span; + +pub fn serialize(data_enum: &DataEnum, name: &Ident, root: &String) -> Tokens { + let write_enum_content : Tokens = data_enum.variants.iter().map(|ref variant| + { + let field_attrs = YaSerdeAttribute::parse(&variant.attrs); + let renamed_label = + match field_attrs.rename { + Some(value) => Ident::new(&format!("{}", value), Span::call_site()), + None => variant.ident + }; + let label = variant.ident; + let label_name = renamed_label.to_string(); + + match variant.fields { + Fields::Unit => { + Some(quote!{ + &#name::#label => { + let data_event = XmlEvent::characters(#label_name); + let _ret = writer.write(data_event); + } + }) + }, + Fields::Named(ref fields) => { + let enum_fields = fields.named.iter().map(|ref field| { + + let field_attrs = YaSerdeAttribute::parse(&field.attrs); + if field_attrs.attribute == true { + return None; + } + + let field_label = field.ident; + if field_attrs.text == true { + return Some(quote!( + let data_event = XmlEvent::characters(&self.#field_label); + let _ret = writer.write(data_event); + )) + } + + let renamed_field_label = + match field_attrs.rename { + Some(value) => Some(Ident::new(&format!("{}", value), Span::call_site())), + None => field.ident + }; + 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); + + let data_event = XmlEvent::characters(#field_label); + let _ret = writer.write(data_event); + + let struct_end_event = XmlEvent::end_element(); + let _ret = writer.write(struct_end_event); + }, + _ => {}, + } + }), + Some(FieldType::FieldTypeStruct{..}) => + Some(quote!{ + match self { + &#name::#label{ref #field_label, ..} => { + match #field_label.derive_serialize(writer) { + Ok(()) => {}, + Err(msg) => { + return Err(msg); + }, + }; + }, + _ => {} + } + }), + Some(FieldType::FieldTypeVec{..}) => + Some(quote!{ + match self { + &#name::#label{ref #field_label, ..} => { + for item in #field_label { + match item.derive_serialize(writer) { + Ok(()) => {}, + Err(msg) => { + return Err(msg); + }, + }; + } + }, + _ => {} + } + }), + _ => None + } + }) + .filter(|x| x.is_some()) + .map(|x| x.unwrap()) + .fold(Tokens::new(), |mut tokens, token| {tokens.append_all(token); tokens}); + + Some(quote!{ + &#name::#label{..} => { + let struct_start_event = XmlEvent::start_element(#label_name); + let _ret = writer.write(struct_start_event); + + #enum_fields + + let struct_end_event = XmlEvent::end_element(); + let _ret = writer.write(struct_end_event); + } + }) + }, + Fields::Unnamed(ref _fields) => { + unimplemented!() + }, + } + }) + .filter(|x| x.is_some()) + .map(|x| x.unwrap()) + .fold(Tokens::new(), |mut tokens, token| {tokens.append_all(token); tokens}); + + // println!("{:?}", write_enum_content); + + quote! { + use xml::writer::XmlEvent; + + impl YaSerialize for #name { + #[allow(unused_variables)] + fn derive_serialize(&self, writer: &mut xml::EventWriter) -> Result<(), String> { + let struct_start_event = XmlEvent::start_element(#root); + let _ret = writer.write(struct_start_event); + match self { + #write_enum_content + } + + let struct_end_event = XmlEvent::end_element(); + let _ret = writer.write(struct_end_event); + Ok(()) + } + } + } +} diff --git a/yaserde_derive/src/ser/mod.rs b/yaserde_derive/src/ser/mod.rs index c565613..8d4e9b8 100644 --- a/yaserde_derive/src/ser/mod.rs +++ b/yaserde_derive/src/ser/mod.rs @@ -1,4 +1,5 @@ +pub mod expand_enum; pub mod expand_struct; use attribute; @@ -21,8 +22,8 @@ pub fn expand_derive_serialize(ast: &syn::DeriveInput) -> Result { expand_struct::serialize(data_struct, &name, &root) }, - &syn::Data::Enum(ref _data_enum) => { - unimplemented!() + &syn::Data::Enum(ref data_enum) => { + expand_enum::serialize(data_enum, &name, &root) }, &syn::Data::Union(ref _data_union) => { unimplemented!()