feat: support generic
This commit is contained in:
		
							parent
							
								
									177fa8e5a7
								
							
						
					
					
						commit
						58d81c7a87
					
				
							
								
								
									
										100
									
								
								examples/src/generic.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								examples/src/generic.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | |||||||
|  | use yaserde::*; | ||||||
|  | 
 | ||||||
|  | #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] | ||||||
|  | pub struct Header {} | ||||||
|  | 
 | ||||||
|  | #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] | ||||||
|  | #[yaserde(
 | ||||||
|  |   rename = "Envelope", | ||||||
|  |   namespace = "s: http://schemas.xmlsoap.org/soap/envelope/", | ||||||
|  |   prefix = "s" | ||||||
|  | )] | ||||||
|  | pub struct SoapEnvelope<BODY> | ||||||
|  | where | ||||||
|  |   BODY: YaSerialize + YaDeserialize + Default, | ||||||
|  | { | ||||||
|  |   #[yaserde(rename = "encodingStyle", prefix = "s", attribute)] | ||||||
|  |   pub encoding_style: String, | ||||||
|  |   #[yaserde(rename = "u", prefix = "xmlns", attribute)] | ||||||
|  |   pub tnsattr: Option<String>, | ||||||
|  |   #[yaserde(rename = "urn", prefix = "xmlns", attribute)] | ||||||
|  |   pub urnattr: Option<String>, | ||||||
|  |   #[yaserde(rename = "xsi", prefix = "xmlns", attribute)] | ||||||
|  |   pub xsiattr: Option<String>, | ||||||
|  |   #[yaserde(rename = "Header", prefix = "s")] | ||||||
|  |   pub header: Option<Header>, | ||||||
|  |   #[yaserde(rename = "Body", prefix = "s")] | ||||||
|  |   pub body: BODY, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] | ||||||
|  | pub struct SoapPlay { | ||||||
|  |   #[yaserde(rename = "Play", prefix = "u", default)] | ||||||
|  |   pub body: Play, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] | ||||||
|  | #[yaserde(rename = "Play", prefix = "u")] | ||||||
|  | pub struct Play { | ||||||
|  |   #[yaserde(flatten, default)] | ||||||
|  |   pub parameters: Play2, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] | ||||||
|  | #[yaserde(
 | ||||||
|  |   rename = "Play", | ||||||
|  |   namespace = "u: urn:schemas-upnp-org:service:AVTransport:1", | ||||||
|  |   prefix = "u" | ||||||
|  | )] | ||||||
|  | pub struct Play2 { | ||||||
|  |   #[yaserde(rename = "InstanceID", default)] | ||||||
|  |   pub instance_id: i32, | ||||||
|  |   #[yaserde(rename = "Speed", default)] | ||||||
|  |   pub speed: i32, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(PrimitiveYaSerde, Debug, Default, Eq, PartialEq)] | ||||||
|  | struct Meters(i32); | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn test_for_generic_newtype() { | ||||||
|  |   let a = SoapEnvelope { | ||||||
|  |     encoding_style: "".to_string(), | ||||||
|  |     tnsattr: None, | ||||||
|  |     urnattr: None, | ||||||
|  |     xsiattr: None, | ||||||
|  |     header: None, | ||||||
|  |     body: Meters(10), | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   let s = ser::to_string(&a).unwrap(); | ||||||
|  |   let b: SoapEnvelope<Meters> = de::from_str(&s).unwrap(); | ||||||
|  | 
 | ||||||
|  |   assert_eq!(a, b); | ||||||
|  |   println!("{:#?}", b); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn test_for_generic_nested_struct() { | ||||||
|  |   let a = SoapEnvelope { | ||||||
|  |     encoding_style: "".to_string(), | ||||||
|  |     tnsattr: None, | ||||||
|  |     urnattr: None, | ||||||
|  |     xsiattr: None, | ||||||
|  |     header: None, | ||||||
|  |     body: SoapPlay { | ||||||
|  |       body: Play { | ||||||
|  |         parameters: Play2 { | ||||||
|  |           instance_id: 20, | ||||||
|  |           speed: 1, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   let s = ser::to_string(&a).unwrap(); | ||||||
|  |   let b: SoapEnvelope<SoapPlay> = de::from_str(&s).unwrap(); | ||||||
|  | 
 | ||||||
|  |   assert_eq!(a, b); | ||||||
|  |   println!("{:#?}", b); | ||||||
|  | } | ||||||
| @ -1,4 +1,5 @@ | |||||||
| mod bbigras_namespace; | mod bbigras_namespace; | ||||||
| mod boscop; | mod boscop; | ||||||
|  | mod generic; | ||||||
| mod ln_dom; | mod ln_dom; | ||||||
| mod svd; | mod svd; | ||||||
|  | |||||||
| @ -1,3 +1,6 @@ | |||||||
|  | #[test] | ||||||
|  | fn parsing_svd() { | ||||||
|  |   use std::fs; | ||||||
|   use yaserde::YaSerialize; |   use yaserde::YaSerialize; | ||||||
| 
 | 
 | ||||||
|   #[derive(PartialEq, Debug, YaSerialize)] |   #[derive(PartialEq, Debug, YaSerialize)] | ||||||
| @ -116,10 +119,6 @@ struct Device { | |||||||
|     devattributes: DevAttrs, |     devattributes: DevAttrs, | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| #[test] |  | ||||||
| fn parsing_svd() { |  | ||||||
|   use std::fs; |  | ||||||
| 
 |  | ||||||
|   let register = Register { |   let register = Register { | ||||||
|         name: "PRCMD".to_string(), |         name: "PRCMD".to_string(), | ||||||
|         description: "This command register (PRCMD) is to protect the registers that may have a significant influence on the application system (PSC, PSM) from an inadvertent write access, so that the system does not stop in case of a program hang-up.".to_string(), |         description: "This command register (PRCMD) is to protect the registers that may have a significant influence on the application system (PSC, PSM) from an inadvertent write access, so that the system does not stop in case of a program hang-up.".to_string(), | ||||||
|  | |||||||
| @ -94,6 +94,7 @@ use std::io::{Read, Write}; | |||||||
| use xml::writer::XmlEvent; | use xml::writer::XmlEvent; | ||||||
| 
 | 
 | ||||||
| pub mod de; | pub mod de; | ||||||
|  | pub mod primitives; | ||||||
| pub mod ser; | pub mod ser; | ||||||
| 
 | 
 | ||||||
| /// A **data structure** that can be deserialized from any data format supported by YaSerDe.
 | /// A **data structure** that can be deserialized from any data format supported by YaSerDe.
 | ||||||
|  | |||||||
							
								
								
									
										51
									
								
								yaserde/src/primitives.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								yaserde/src/primitives.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | use std::{io::Read, io::Write}; | ||||||
|  | 
 | ||||||
|  | use crate::{de, ser}; | ||||||
|  | 
 | ||||||
|  | pub fn serialize_primitives<S, W: Write>( | ||||||
|  |   self_bypass: &S, | ||||||
|  |   default_name: &str, | ||||||
|  |   writer: &mut ser::Serializer<W>, | ||||||
|  |   serialize_function: impl FnOnce(&S) -> String, | ||||||
|  | ) -> Result<(), String> { | ||||||
|  |   let name = writer | ||||||
|  |     .get_start_event_name() | ||||||
|  |     .unwrap_or_else(|| default_name.to_string()); | ||||||
|  | 
 | ||||||
|  |   if !writer.skip_start_end() { | ||||||
|  |     writer | ||||||
|  |       .write(xml::writer::XmlEvent::start_element(name.as_str())) | ||||||
|  |       .map_err(|_e| format!("Start element {name:?} write failed"))?; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   writer | ||||||
|  |     .write(xml::writer::XmlEvent::characters( | ||||||
|  |       serialize_function(self_bypass).as_str(), | ||||||
|  |     )) | ||||||
|  |     .map_err(|_e| format!("Element value {name:?} write failed"))?; | ||||||
|  | 
 | ||||||
|  |   if !writer.skip_start_end() { | ||||||
|  |     writer | ||||||
|  |       .write(xml::writer::XmlEvent::end_element()) | ||||||
|  |       .map_err(|_e| format!("End element {name:?} write failed"))?; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn deserialize_primitives<S, R: Read>( | ||||||
|  |   reader: &mut de::Deserializer<R>, | ||||||
|  |   deserialize_function: impl FnOnce(&str) -> Result<S, String>, | ||||||
|  | ) -> Result<S, String> { | ||||||
|  |   if let Ok(xml::reader::XmlEvent::StartElement { .. }) = reader.peek() { | ||||||
|  |     reader.next_event()?; | ||||||
|  |   } else { | ||||||
|  |     return Err("Start element not found".to_string()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if let Ok(xml::reader::XmlEvent::Characters(ref text)) = reader.peek() { | ||||||
|  |     deserialize_function(text) | ||||||
|  |   } else { | ||||||
|  |     deserialize_function("") | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								yaserde/tests/generic.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								yaserde/tests/generic.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | #[macro_use] | ||||||
|  | extern crate yaserde; | ||||||
|  | 
 | ||||||
|  | use yaserde::{YaDeserialize, YaSerialize}; | ||||||
|  | 
 | ||||||
|  | fn init() { | ||||||
|  |   let _ = env_logger::builder().is_test(true).try_init(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn generic() { | ||||||
|  |   init(); | ||||||
|  | 
 | ||||||
|  |   #[derive(Debug, PartialEq, YaDeserialize, YaSerialize)] | ||||||
|  |   #[yaserde(rename = "base")] | ||||||
|  |   pub struct Base<G> | ||||||
|  |   where | ||||||
|  |     G: YaSerialize + YaDeserialize + Default, | ||||||
|  |   { | ||||||
|  |     background: G, | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   #[derive(Debug, Default, PartialEq, YaDeserialize, YaSerialize)] | ||||||
|  |   pub struct Generic { | ||||||
|  |     #[yaserde(attribute)] | ||||||
|  |     color: String, | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   let content = r#"<base><background color="blue" /></base>"#; | ||||||
|  |   let model = Base { | ||||||
|  |     background: Generic { | ||||||
|  |       color: "blue".to_string(), | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   serialize_and_validate!(model, content); | ||||||
|  | 
 | ||||||
|  |   log::debug!("deserialize_and_validate @ {}:{}", file!(), line!()); | ||||||
|  |   let loaded: Result<Base<Generic>, String> = yaserde::de::from_str(content); | ||||||
|  |   assert_eq!(loaded, Ok(model)); | ||||||
|  | } | ||||||
| @ -1,13 +1,14 @@ | |||||||
| use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | ||||||
| use proc_macro2::{Span, TokenStream}; | use proc_macro2::{Span, TokenStream}; | ||||||
| use quote::quote; | use quote::quote; | ||||||
| use syn::{DataEnum, Fields, Ident}; | use syn::{DataEnum, Fields, Generics, Ident}; | ||||||
| 
 | 
 | ||||||
| pub fn parse( | pub fn parse( | ||||||
|   data_enum: &DataEnum, |   data_enum: &DataEnum, | ||||||
|   name: &Ident, |   name: &Ident, | ||||||
|   root: &str, |   root: &str, | ||||||
|   root_attributes: &YaSerdeAttribute, |   root_attributes: &YaSerdeAttribute, | ||||||
|  |   generics: &Generics, | ||||||
| ) -> TokenStream { | ) -> TokenStream { | ||||||
|   let namespaces_matching = root_attributes.get_namespace_matching( |   let namespaces_matching = root_attributes.get_namespace_matching( | ||||||
|     &None, |     &None, | ||||||
| @ -23,6 +24,7 @@ pub fn parse( | |||||||
|     .collect(); |     .collect(); | ||||||
| 
 | 
 | ||||||
|   let flatten = root_attributes.flatten; |   let flatten = root_attributes.flatten; | ||||||
|  |   let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); | ||||||
| 
 | 
 | ||||||
|   let element_name = if let Some(tag) = &root_attributes.tag { |   let element_name = if let Some(tag) = &root_attributes.tag { | ||||||
|     quote! { |     quote! { | ||||||
| @ -39,7 +41,7 @@ pub fn parse( | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   quote! { |   quote! { | ||||||
|     impl ::yaserde::YaDeserialize for #name { |     impl #impl_generics ::yaserde::YaDeserialize for #name #ty_generics #where_clause { | ||||||
|       #[allow(unused_variables)] |       #[allow(unused_variables)] | ||||||
|       fn deserialize<R: ::std::io::Read>( |       fn deserialize<R: ::std::io::Read>( | ||||||
|         reader: &mut ::yaserde::de::Deserializer<R>, |         reader: &mut ::yaserde::de::Deserializer<R>, | ||||||
|  | |||||||
| @ -2,13 +2,14 @@ use super::build_default_value::{build_default_value, build_default_vec_value}; | |||||||
| use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | ||||||
| use proc_macro2::{Span, TokenStream}; | use proc_macro2::{Span, TokenStream}; | ||||||
| use quote::quote; | use quote::quote; | ||||||
| use syn::{DataStruct, Ident}; | use syn::{DataStruct, Generics, Ident}; | ||||||
| 
 | 
 | ||||||
| pub fn parse( | pub fn parse( | ||||||
|   data_struct: &DataStruct, |   data_struct: &DataStruct, | ||||||
|   name: &Ident, |   name: &Ident, | ||||||
|   root: &str, |   root: &str, | ||||||
|   root_attributes: &YaSerdeAttribute, |   root_attributes: &YaSerdeAttribute, | ||||||
|  |   generics: &Generics, | ||||||
| ) -> TokenStream { | ) -> TokenStream { | ||||||
|   let namespaces_matching = root_attributes.get_namespace_matching( |   let namespaces_matching = root_attributes.get_namespace_matching( | ||||||
|     &None, |     &None, | ||||||
| @ -49,6 +50,17 @@ pub fn parse( | |||||||
|     .fields |     .fields | ||||||
|     .iter() |     .iter() | ||||||
|     .map(|field| YaSerdeField::new(field.clone())) |     .map(|field| YaSerdeField::new(field.clone())) | ||||||
|  |     .filter(|field| { | ||||||
|  |       if field.is_attribute() { | ||||||
|  |         return true; | ||||||
|  |       }; | ||||||
|  |       match field.get_type() { | ||||||
|  |         Field::FieldVec { data_type } => !matches!(*data_type, Field::FieldStruct { .. }), | ||||||
|  |         Field::FieldOption { data_type } => !matches!(*data_type, Field::FieldStruct { .. }), | ||||||
|  |         Field::FieldStruct { .. } => false, | ||||||
|  |         _ => true, | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|     .filter_map(|field| { |     .filter_map(|field| { | ||||||
|       let struct_visitor = |struct_name: syn::Path| { |       let struct_visitor = |struct_name: syn::Path| { | ||||||
|         let struct_id: String = struct_name |         let struct_id: String = struct_name | ||||||
| @ -369,9 +381,10 @@ pub fn parse( | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   let flatten = root_attributes.flatten; |   let flatten = root_attributes.flatten; | ||||||
|  |   let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); | ||||||
| 
 | 
 | ||||||
|   quote! { |   quote! { | ||||||
|     impl ::yaserde::YaDeserialize for #name { |     impl #impl_generics ::yaserde::YaDeserialize for #name #ty_generics #where_clause { | ||||||
|       #[allow(unused_variables)] |       #[allow(unused_variables)] | ||||||
|       fn deserialize<R: ::std::io::Read>( |       fn deserialize<R: ::std::io::Read>( | ||||||
|         reader: &mut ::yaserde::de::Deserializer<R>, |         reader: &mut ::yaserde::de::Deserializer<R>, | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result<TokenStream, | |||||||
|   let name = &ast.ident; |   let name = &ast.ident; | ||||||
|   let attrs = &ast.attrs; |   let attrs = &ast.attrs; | ||||||
|   let data = &ast.data; |   let data = &ast.data; | ||||||
|  |   let generics = &ast.generics; | ||||||
| 
 | 
 | ||||||
|   let root_attributes = YaSerdeAttribute::parse(attrs); |   let root_attributes = YaSerdeAttribute::parse(attrs); | ||||||
| 
 | 
 | ||||||
| @ -21,10 +22,10 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result<TokenStream, | |||||||
| 
 | 
 | ||||||
|   let impl_block = match *data { |   let impl_block = match *data { | ||||||
|     syn::Data::Struct(ref data_struct) => { |     syn::Data::Struct(ref data_struct) => { | ||||||
|       expand_struct::parse(data_struct, name, &root_name, &root_attributes) |       expand_struct::parse(data_struct, name, &root_name, &root_attributes, generics) | ||||||
|     } |     } | ||||||
|     syn::Data::Enum(ref data_enum) => { |     syn::Data::Enum(ref data_enum) => { | ||||||
|       expand_enum::parse(data_enum, name, &root_name, &root_attributes) |       expand_enum::parse(data_enum, name, &root_name, &root_attributes, generics) | ||||||
|     } |     } | ||||||
|     syn::Data::Union(ref _data_union) => unimplemented!(), |     syn::Data::Union(ref _data_union) => unimplemented!(), | ||||||
|   }; |   }; | ||||||
|  | |||||||
| @ -5,9 +5,13 @@ extern crate proc_macro; | |||||||
| 
 | 
 | ||||||
| mod common; | mod common; | ||||||
| mod de; | mod de; | ||||||
|  | mod primitives; | ||||||
| mod ser; | mod ser; | ||||||
| 
 | 
 | ||||||
|  | use primitives::{hexbinary_serde, primitive_serde, primitive_yaserde}; | ||||||
| use proc_macro::TokenStream; | use proc_macro::TokenStream; | ||||||
|  | use proc_macro2::TokenStream as TokenStream2; | ||||||
|  | use quote::quote; | ||||||
| 
 | 
 | ||||||
| #[proc_macro_derive(YaDeserialize, attributes(yaserde))] | #[proc_macro_derive(YaDeserialize, attributes(yaserde))] | ||||||
| pub fn derive_deserialize(input: TokenStream) -> TokenStream { | pub fn derive_deserialize(input: TokenStream) -> TokenStream { | ||||||
| @ -26,3 +30,37 @@ pub fn derive_serialize(input: TokenStream) -> TokenStream { | |||||||
|     Err(msg) => panic!("{}", msg), |     Err(msg) => panic!("{}", msg), | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // Serialize & Deserialize a struct using it's UpperHex implementation
 | ||||||
|  | #[proc_macro_derive(HexBinaryYaSerde)] | ||||||
|  | pub fn derive_hexbinary(input: TokenStream) -> TokenStream { | ||||||
|  |   let serde: TokenStream2 = hexbinary_serde(input.clone()).into(); | ||||||
|  |   let yaserde: TokenStream2 = primitive_yaserde(input).into(); | ||||||
|  | 
 | ||||||
|  |   quote! { | ||||||
|  |       use ::std::str::FromStr as _; | ||||||
|  |       #serde | ||||||
|  |       #yaserde | ||||||
|  |   } | ||||||
|  |   .into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Serialize & Deserialize a primitive newtype by generating a FromStr & Display implementation
 | ||||||
|  | #[proc_macro_derive(PrimitiveYaSerde)] | ||||||
|  | pub fn derive_primitive(input: TokenStream) -> TokenStream { | ||||||
|  |   let serde: TokenStream2 = primitive_serde(input.clone()).into(); | ||||||
|  |   let yaserde: TokenStream2 = primitive_yaserde(input).into(); | ||||||
|  | 
 | ||||||
|  |   quote! { | ||||||
|  |       use ::std::str::FromStr as _; | ||||||
|  |       #serde | ||||||
|  |       #yaserde | ||||||
|  |   } | ||||||
|  |   .into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Serialize & Deserialize a type using it's existing FromStr & Display implementation
 | ||||||
|  | #[proc_macro_derive(DefaultYaSerde)] | ||||||
|  | pub fn derive_default(input: TokenStream) -> TokenStream { | ||||||
|  |   primitive_yaserde(input) | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										118
									
								
								yaserde_derive/src/primitives.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								yaserde_derive/src/primitives.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,118 @@ | |||||||
|  | // Adds YaSerialize and YaDeserialize implementations for types that support FromStr and Display traits.
 | ||||||
|  | // Code originally from `xsd-parser-rs`
 | ||||||
|  | 
 | ||||||
|  | use proc_macro::TokenStream; | ||||||
|  | use proc_macro2::TokenStream as TokenStream2; | ||||||
|  | use quote::quote; | ||||||
|  | use syn::{parse_macro_input, DeriveInput}; | ||||||
|  | 
 | ||||||
|  | pub fn primitive_yaserde(input: TokenStream) -> TokenStream { | ||||||
|  |   let ast = parse_macro_input!(input as DeriveInput); | ||||||
|  | 
 | ||||||
|  |   let struct_name = &ast.ident; | ||||||
|  |   let struct_name_literal = &ast.ident.to_string(); | ||||||
|  | 
 | ||||||
|  |   let serde = quote! { | ||||||
|  |       impl ::yaserde::YaSerialize for #struct_name { | ||||||
|  |           fn serialize<W: ::std::io::Write>( | ||||||
|  |               &self, | ||||||
|  |               writer: &mut ::yaserde::ser::Serializer<W>, | ||||||
|  |           ) -> ::std::result::Result<(), ::std::string::String> { | ||||||
|  |             ::yaserde::primitives::serialize_primitives( | ||||||
|  |                   self, | ||||||
|  |                   #struct_name_literal, | ||||||
|  |                   writer, |s| s.to_string(), | ||||||
|  |               ) | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           fn serialize_attributes( | ||||||
|  |               &self, | ||||||
|  |               attributes: ::std::vec::Vec<::yaserde::__xml::attribute::OwnedAttribute>, | ||||||
|  |               namespace: ::yaserde::__xml::namespace::Namespace, | ||||||
|  |           ) -> ::std::result::Result< | ||||||
|  |               ( | ||||||
|  |                   ::std::vec::Vec<::yaserde::__xml::attribute::OwnedAttribute>, | ||||||
|  |                   ::yaserde::__xml::namespace::Namespace, | ||||||
|  |               ), | ||||||
|  |               ::std::string::String, | ||||||
|  |           > { | ||||||
|  |               Ok((attributes, namespace)) | ||||||
|  |           } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       impl ::yaserde::YaDeserialize for #struct_name { | ||||||
|  |           fn deserialize<R: ::std::io::Read>( | ||||||
|  |               reader: &mut ::yaserde::de::Deserializer<R>, | ||||||
|  |           ) -> ::std::result::Result<Self, ::std::string::String> { | ||||||
|  |               ::yaserde::primitives::deserialize_primitives( | ||||||
|  |                   reader, | ||||||
|  |                   |s| #struct_name::from_str(s).map_err(|e| e.to_string()), | ||||||
|  |               ) | ||||||
|  |           } | ||||||
|  |       } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   serde.into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn hexbinary_serde(input: TokenStream) -> TokenStream { | ||||||
|  |   let first = input.clone(); | ||||||
|  |   let DeriveInput { ident, .. } = parse_macro_input!(first); | ||||||
|  |   // Calculate number digits to determine whether leading zero should be added
 | ||||||
|  |   quote! { | ||||||
|  |     impl std::fmt::Display for #ident { | ||||||
|  |       fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "{:02X}", self.0) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     impl ::std::str::FromStr for #ident { | ||||||
|  |       type Err = ::std::string::String; | ||||||
|  | 
 | ||||||
|  |       fn from_str(s: &::std::primitive::str) -> ::std::result::Result<Self, Self::Err> { | ||||||
|  |         Self::from_bits( | ||||||
|  |           s.parse() | ||||||
|  |               .map_err(|_| String::from("Failed to parse Bitflag integer"))?, | ||||||
|  |       ) | ||||||
|  |       .ok_or(String::from("Unknown bits were set in Bitflag")) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn primitive_serde(input: TokenStream) -> TokenStream { | ||||||
|  |   let first = input.clone(); | ||||||
|  |   let ref di @ DeriveInput { ref ident, .. } = parse_macro_input!(first); | ||||||
|  |   let fromstr = extract_full_path(di).unwrap(); | ||||||
|  |   quote! { | ||||||
|  |     impl std::fmt::Display for #ident { | ||||||
|  |       fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "{}", self.0) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     impl ::std::str::FromStr for #ident { | ||||||
|  |       type Err = ::std::string::String; | ||||||
|  | 
 | ||||||
|  |       fn from_str(s: &::std::primitive::str) -> ::std::result::Result<Self, Self::Err> { | ||||||
|  |         Ok(#ident(#fromstr)) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn extract_full_path(ast: &syn::DeriveInput) -> Result<TokenStream2, syn::Error> { | ||||||
|  |   if let syn::Data::Struct(data_struct) = &ast.data { | ||||||
|  |     if let syn::Fields::Unnamed(fields) = &data_struct.fields { | ||||||
|  |       if let Some(syn::Type::Path(path)) = &fields.unnamed.first().map(|f| &f.ty) { | ||||||
|  |         return Ok( | ||||||
|  |           quote! { <#path as ::std::str::FromStr>::from_str(s).map_err(|e| e.to_string())? }, | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Err(syn::Error::new_spanned(ast, "Unable to extract full path")) | ||||||
|  | } | ||||||
| @ -2,15 +2,16 @@ use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | |||||||
| use crate::ser::{implement_serializer::implement_serializer, label::build_label_name}; | use crate::ser::{implement_serializer::implement_serializer, label::build_label_name}; | ||||||
| use proc_macro2::TokenStream; | use proc_macro2::TokenStream; | ||||||
| use quote::quote; | use quote::quote; | ||||||
| use syn::DataEnum; |  | ||||||
| use syn::Fields; | use syn::Fields; | ||||||
| use syn::Ident; | use syn::Ident; | ||||||
|  | use syn::{DataEnum, Generics}; | ||||||
| 
 | 
 | ||||||
| pub fn serialize( | pub fn serialize( | ||||||
|   data_enum: &DataEnum, |   data_enum: &DataEnum, | ||||||
|   name: &Ident, |   name: &Ident, | ||||||
|   root: &str, |   root: &str, | ||||||
|   root_attributes: &YaSerdeAttribute, |   root_attributes: &YaSerdeAttribute, | ||||||
|  |   generics: &Generics, | ||||||
| ) -> TokenStream { | ) -> TokenStream { | ||||||
|   let inner_enum_inspector = inner_enum_inspector(data_enum, name, root_attributes); |   let inner_enum_inspector = inner_enum_inspector(data_enum, name, root_attributes); | ||||||
| 
 | 
 | ||||||
| @ -108,6 +109,7 @@ pub fn serialize( | |||||||
|     quote!(match self { |     quote!(match self { | ||||||
|       #inner_enum_inspector |       #inner_enum_inspector | ||||||
|     }), |     }), | ||||||
|  |     generics, | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,14 +3,15 @@ use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | |||||||
| use crate::ser::{element::*, implement_serializer::implement_serializer}; | use crate::ser::{element::*, implement_serializer::implement_serializer}; | ||||||
| use proc_macro2::TokenStream; | use proc_macro2::TokenStream; | ||||||
| use quote::quote; | use quote::quote; | ||||||
| use syn::DataStruct; |  | ||||||
| use syn::Ident; | use syn::Ident; | ||||||
|  | use syn::{DataStruct, Generics}; | ||||||
| 
 | 
 | ||||||
| pub fn serialize( | pub fn serialize( | ||||||
|   data_struct: &DataStruct, |   data_struct: &DataStruct, | ||||||
|   name: &Ident, |   name: &Ident, | ||||||
|   root: &str, |   root: &str, | ||||||
|   root_attributes: &YaSerdeAttribute, |   root_attributes: &YaSerdeAttribute, | ||||||
|  |   generics: &Generics, | ||||||
| ) -> TokenStream { | ) -> TokenStream { | ||||||
|   let append_attributes: TokenStream = data_struct |   let append_attributes: TokenStream = data_struct | ||||||
|     .fields |     .fields | ||||||
| @ -348,5 +349,6 @@ pub fn serialize( | |||||||
|     root_attributes, |     root_attributes, | ||||||
|     append_attributes, |     append_attributes, | ||||||
|     struct_inspector, |     struct_inspector, | ||||||
|  |     generics, | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ use crate::ser::namespace::generate_namespaces_definition; | |||||||
| use proc_macro2::Ident; | use proc_macro2::Ident; | ||||||
| use proc_macro2::TokenStream; | use proc_macro2::TokenStream; | ||||||
| use quote::quote; | use quote::quote; | ||||||
|  | use syn::Generics; | ||||||
| 
 | 
 | ||||||
| pub fn implement_serializer( | pub fn implement_serializer( | ||||||
|   name: &Ident, |   name: &Ident, | ||||||
| @ -10,12 +11,15 @@ pub fn implement_serializer( | |||||||
|   attributes: &YaSerdeAttribute, |   attributes: &YaSerdeAttribute, | ||||||
|   append_attributes: TokenStream, |   append_attributes: TokenStream, | ||||||
|   inner_inspector: TokenStream, |   inner_inspector: TokenStream, | ||||||
|  |   generics: &Generics, | ||||||
| ) -> TokenStream { | ) -> TokenStream { | ||||||
|   let namespaces_definition = generate_namespaces_definition(attributes); |   let namespaces_definition = generate_namespaces_definition(attributes); | ||||||
|   let flatten = attributes.flatten; |   let flatten = attributes.flatten; | ||||||
| 
 | 
 | ||||||
|  |   let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); | ||||||
|  | 
 | ||||||
|   quote! { |   quote! { | ||||||
|     impl ::yaserde::YaSerialize for #name { |     impl #impl_generics ::yaserde::YaSerialize for #name #ty_generics #where_clause { | ||||||
|       #[allow(unused_variables)] |       #[allow(unused_variables)] | ||||||
|       fn serialize<W: ::std::io::Write>( |       fn serialize<W: ::std::io::Write>( | ||||||
|         &self, |         &self, | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ pub fn expand_derive_serialize(ast: &syn::DeriveInput) -> Result<TokenStream, St | |||||||
|   let name = &ast.ident; |   let name = &ast.ident; | ||||||
|   let attrs = &ast.attrs; |   let attrs = &ast.attrs; | ||||||
|   let data = &ast.data; |   let data = &ast.data; | ||||||
|  |   let generics = &ast.generics; | ||||||
| 
 | 
 | ||||||
|   let root_attributes = YaSerdeAttribute::parse(attrs); |   let root_attributes = YaSerdeAttribute::parse(attrs); | ||||||
| 
 | 
 | ||||||
| @ -24,10 +25,10 @@ pub fn expand_derive_serialize(ast: &syn::DeriveInput) -> Result<TokenStream, St | |||||||
| 
 | 
 | ||||||
|   let impl_block = match *data { |   let impl_block = match *data { | ||||||
|     syn::Data::Struct(ref data_struct) => { |     syn::Data::Struct(ref data_struct) => { | ||||||
|       expand_struct::serialize(data_struct, name, &root_name, &root_attributes) |       expand_struct::serialize(data_struct, name, &root_name, &root_attributes, generics) | ||||||
|     } |     } | ||||||
|     syn::Data::Enum(ref data_enum) => { |     syn::Data::Enum(ref data_enum) => { | ||||||
|       expand_enum::serialize(data_enum, name, &root_name, &root_attributes) |       expand_enum::serialize(data_enum, name, &root_name, &root_attributes, generics) | ||||||
|     } |     } | ||||||
|     syn::Data::Union(ref _data_union) => unimplemented!(), |     syn::Data::Union(ref _data_union) => unimplemented!(), | ||||||
|   }; |   }; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user