diff --git a/examples/src/generic.rs b/examples/src/generic.rs new file mode 100644 index 0000000..0358382 --- /dev/null +++ b/examples/src/generic.rs @@ -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 +where + BODY: YaSerialize + YaDeserialize + Default, +{ + #[yaserde(rename = "encodingStyle", prefix = "s", attribute)] + pub encoding_style: String, + #[yaserde(rename = "u", prefix = "xmlns", attribute)] + pub tnsattr: Option, + #[yaserde(rename = "urn", prefix = "xmlns", attribute)] + pub urnattr: Option, + #[yaserde(rename = "xsi", prefix = "xmlns", attribute)] + pub xsiattr: Option, + #[yaserde(rename = "Header", prefix = "s")] + pub header: Option
, + #[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 = 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 = de::from_str(&s).unwrap(); + + assert_eq!(a, b); + println!("{:#?}", b); +} diff --git a/examples/src/lib.rs b/examples/src/lib.rs index 439aac9..8f83b28 100644 --- a/examples/src/lib.rs +++ b/examples/src/lib.rs @@ -1,4 +1,5 @@ mod bbigras_namespace; mod boscop; +mod generic; mod ln_dom; mod svd; diff --git a/examples/src/svd.rs b/examples/src/svd.rs index 2a8d72b..f41b8b5 100644 --- a/examples/src/svd.rs +++ b/examples/src/svd.rs @@ -1,124 +1,123 @@ -use yaserde::YaSerialize; - -#[derive(PartialEq, Debug, YaSerialize)] -struct CpuDef { - #[yaserde(child)] - name: String, - #[yaserde(child)] - revision: String, - #[yaserde(child)] - endian: String, // enum {LE, BE, ME} - #[yaserde(child)] - mpupresent: bool, - #[yaserde(child)] - fpupresent: bool, - //#[yaserde(child)] - //nvicpriobits: enum {8, 16, 32, 64, 128}, - #[yaserde(child)] - vendorsystickconfig: bool, -} - -#[derive(PartialEq, Debug, YaSerialize)] -struct Field { - name: String, - #[yaserde(child)] - description: String, - #[yaserde(child)] - bitrange: String, - #[yaserde(child)] - access: String, -} - -#[derive(PartialEq, Debug, YaSerialize)] -struct Register { - #[yaserde(child)] - name: String, - #[yaserde(child)] - description: String, - #[yaserde(child)] - addressoffset: String, - #[yaserde(child)] - size: u8, - #[yaserde(child)] - access: String, - #[yaserde(child)] - resetvalue: String, - #[yaserde(child)] - resetmask: String, - #[yaserde(child)] - fields: Vec, -} - -#[derive(PartialEq, Debug, YaSerialize)] -struct Peripheral { - #[yaserde(child)] - name: String, - #[yaserde(child)] - version: String, - #[yaserde(child)] - description: String, - #[yaserde(child)] - groupname: String, - #[yaserde(child)] - baseaddress: String, - #[yaserde(child)] - size: u8, - #[yaserde(child)] - access: String, - #[yaserde(child)] - registers: Vec, -} - -#[derive(PartialEq, Debug, YaSerialize)] -struct DevAttrs { - #[yaserde(child)] - vendor: String, - #[yaserde(child)] - vendorid: String, - #[yaserde(child)] - name: String, - #[yaserde(child)] - series: String, - #[yaserde(child)] - version: String, - #[yaserde(child)] - description: String, - #[yaserde(child)] - licensetext: String, - #[yaserde(child)] - cpu: CpuDef, - #[yaserde(child)] - addressunitbits: u8, - #[yaserde(child)] - width: u8, - #[yaserde(child)] - size: u8, - #[yaserde(child)] - access: String, - #[yaserde(child)] - resetvalue: String, - #[yaserde(child)] - resetmask: String, - #[yaserde(child)] - peripherals: Vec, -} - -#[derive(PartialEq, Debug, YaSerialize)] -#[yaserde(rename = "device")] -struct Device { - #[yaserde(attribute)] - schemaversion: String, - #[yaserde(attribute)] - xmlns: String, - #[yaserde(attribute)] - xsnonamespaceschemalocation: String, - #[yaserde(child)] - devattributes: DevAttrs, -} - #[test] fn parsing_svd() { use std::fs; + use yaserde::YaSerialize; + + #[derive(PartialEq, Debug, YaSerialize)] + struct CpuDef { + #[yaserde(child)] + name: String, + #[yaserde(child)] + revision: String, + #[yaserde(child)] + endian: String, // enum {LE, BE, ME} + #[yaserde(child)] + mpupresent: bool, + #[yaserde(child)] + fpupresent: bool, + //#[yaserde(child)] + //nvicpriobits: enum {8, 16, 32, 64, 128}, + #[yaserde(child)] + vendorsystickconfig: bool, + } + + #[derive(PartialEq, Debug, YaSerialize)] + struct Field { + name: String, + #[yaserde(child)] + description: String, + #[yaserde(child)] + bitrange: String, + #[yaserde(child)] + access: String, + } + + #[derive(PartialEq, Debug, YaSerialize)] + struct Register { + #[yaserde(child)] + name: String, + #[yaserde(child)] + description: String, + #[yaserde(child)] + addressoffset: String, + #[yaserde(child)] + size: u8, + #[yaserde(child)] + access: String, + #[yaserde(child)] + resetvalue: String, + #[yaserde(child)] + resetmask: String, + #[yaserde(child)] + fields: Vec, + } + + #[derive(PartialEq, Debug, YaSerialize)] + struct Peripheral { + #[yaserde(child)] + name: String, + #[yaserde(child)] + version: String, + #[yaserde(child)] + description: String, + #[yaserde(child)] + groupname: String, + #[yaserde(child)] + baseaddress: String, + #[yaserde(child)] + size: u8, + #[yaserde(child)] + access: String, + #[yaserde(child)] + registers: Vec, + } + + #[derive(PartialEq, Debug, YaSerialize)] + struct DevAttrs { + #[yaserde(child)] + vendor: String, + #[yaserde(child)] + vendorid: String, + #[yaserde(child)] + name: String, + #[yaserde(child)] + series: String, + #[yaserde(child)] + version: String, + #[yaserde(child)] + description: String, + #[yaserde(child)] + licensetext: String, + #[yaserde(child)] + cpu: CpuDef, + #[yaserde(child)] + addressunitbits: u8, + #[yaserde(child)] + width: u8, + #[yaserde(child)] + size: u8, + #[yaserde(child)] + access: String, + #[yaserde(child)] + resetvalue: String, + #[yaserde(child)] + resetmask: String, + #[yaserde(child)] + peripherals: Vec, + } + + #[derive(PartialEq, Debug, YaSerialize)] + #[yaserde(rename = "device")] + struct Device { + #[yaserde(attribute)] + schemaversion: String, + #[yaserde(attribute)] + xmlns: String, + #[yaserde(attribute)] + xsnonamespaceschemalocation: String, + #[yaserde(child)] + devattributes: DevAttrs, + } let register = Register { name: "PRCMD".to_string(), diff --git a/yaserde/src/lib.rs b/yaserde/src/lib.rs index 971ce48..720782e 100644 --- a/yaserde/src/lib.rs +++ b/yaserde/src/lib.rs @@ -94,6 +94,7 @@ use std::io::{Read, Write}; use xml::writer::XmlEvent; pub mod de; +pub mod primitives; pub mod ser; /// A **data structure** that can be deserialized from any data format supported by YaSerDe. diff --git a/yaserde/src/primitives.rs b/yaserde/src/primitives.rs new file mode 100644 index 0000000..0d4107f --- /dev/null +++ b/yaserde/src/primitives.rs @@ -0,0 +1,51 @@ +use std::{io::Read, io::Write}; + +use crate::{de, ser}; + +pub fn serialize_primitives( + self_bypass: &S, + default_name: &str, + writer: &mut ser::Serializer, + 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( + reader: &mut de::Deserializer, + deserialize_function: impl FnOnce(&str) -> Result, +) -> Result { + 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("") + } +} diff --git a/yaserde/tests/generic.rs b/yaserde/tests/generic.rs new file mode 100644 index 0000000..5aba46c --- /dev/null +++ b/yaserde/tests/generic.rs @@ -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 + where + G: YaSerialize + YaDeserialize + Default, + { + background: G, + } + + #[derive(Debug, Default, PartialEq, YaDeserialize, YaSerialize)] + pub struct Generic { + #[yaserde(attribute)] + color: String, + } + + let content = r#""#; + let model = Base { + background: Generic { + color: "blue".to_string(), + }, + }; + + serialize_and_validate!(model, content); + + log::debug!("deserialize_and_validate @ {}:{}", file!(), line!()); + let loaded: Result, String> = yaserde::de::from_str(content); + assert_eq!(loaded, Ok(model)); +} diff --git a/yaserde_derive/src/de/expand_enum.rs b/yaserde_derive/src/de/expand_enum.rs index 5a41c57..b025ab9 100644 --- a/yaserde_derive/src/de/expand_enum.rs +++ b/yaserde_derive/src/de/expand_enum.rs @@ -1,13 +1,14 @@ use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; use proc_macro2::{Span, TokenStream}; use quote::quote; -use syn::{DataEnum, Fields, Ident}; +use syn::{DataEnum, Fields, Generics, Ident}; pub fn parse( data_enum: &DataEnum, name: &Ident, root: &str, root_attributes: &YaSerdeAttribute, + generics: &Generics, ) -> TokenStream { let namespaces_matching = root_attributes.get_namespace_matching( &None, @@ -23,6 +24,7 @@ pub fn parse( .collect(); 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 { quote! { @@ -39,7 +41,7 @@ pub fn parse( }; quote! { - impl ::yaserde::YaDeserialize for #name { + impl #impl_generics ::yaserde::YaDeserialize for #name #ty_generics #where_clause { #[allow(unused_variables)] fn deserialize( reader: &mut ::yaserde::de::Deserializer, diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index 5ca527a..782aa08 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -2,13 +2,14 @@ use super::build_default_value::{build_default_value, build_default_vec_value}; use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; use proc_macro2::{Span, TokenStream}; use quote::quote; -use syn::{DataStruct, Ident}; +use syn::{DataStruct, Generics, Ident}; pub fn parse( data_struct: &DataStruct, name: &Ident, root: &str, root_attributes: &YaSerdeAttribute, + generics: &Generics, ) -> TokenStream { let namespaces_matching = root_attributes.get_namespace_matching( &None, @@ -49,6 +50,17 @@ pub fn parse( .fields .iter() .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| { let struct_visitor = |struct_name: syn::Path| { let struct_id: String = struct_name @@ -369,9 +381,10 @@ pub fn parse( }; let flatten = root_attributes.flatten; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); quote! { - impl ::yaserde::YaDeserialize for #name { + impl #impl_generics ::yaserde::YaDeserialize for #name #ty_generics #where_clause { #[allow(unused_variables)] fn deserialize( reader: &mut ::yaserde::de::Deserializer, diff --git a/yaserde_derive/src/de/mod.rs b/yaserde_derive/src/de/mod.rs index 043a3a0..31453f7 100644 --- a/yaserde_derive/src/de/mod.rs +++ b/yaserde_derive/src/de/mod.rs @@ -10,6 +10,7 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result Result { - 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) => { - 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!(), }; diff --git a/yaserde_derive/src/lib.rs b/yaserde_derive/src/lib.rs index 9d881a1..914cc30 100644 --- a/yaserde_derive/src/lib.rs +++ b/yaserde_derive/src/lib.rs @@ -5,9 +5,13 @@ extern crate proc_macro; mod common; mod de; +mod primitives; mod ser; +use primitives::{hexbinary_serde, primitive_serde, primitive_yaserde}; use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; #[proc_macro_derive(YaDeserialize, attributes(yaserde))] pub fn derive_deserialize(input: TokenStream) -> TokenStream { @@ -26,3 +30,37 @@ pub fn derive_serialize(input: TokenStream) -> TokenStream { 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) +} diff --git a/yaserde_derive/src/primitives.rs b/yaserde_derive/src/primitives.rs new file mode 100644 index 0000000..8847521 --- /dev/null +++ b/yaserde_derive/src/primitives.rs @@ -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( + &self, + writer: &mut ::yaserde::ser::Serializer, + ) -> ::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( + reader: &mut ::yaserde::de::Deserializer, + ) -> ::std::result::Result { + ::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::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 { + Ok(#ident(#fromstr)) + } + } + } + .into() +} + +fn extract_full_path(ast: &syn::DeriveInput) -> Result { + 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")) +} diff --git a/yaserde_derive/src/ser/expand_enum.rs b/yaserde_derive/src/ser/expand_enum.rs index 3ef0bed..4d13abb 100644 --- a/yaserde_derive/src/ser/expand_enum.rs +++ b/yaserde_derive/src/ser/expand_enum.rs @@ -2,15 +2,16 @@ use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; use crate::ser::{implement_serializer::implement_serializer, label::build_label_name}; use proc_macro2::TokenStream; use quote::quote; -use syn::DataEnum; use syn::Fields; use syn::Ident; +use syn::{DataEnum, Generics}; pub fn serialize( data_enum: &DataEnum, name: &Ident, root: &str, root_attributes: &YaSerdeAttribute, + generics: &Generics, ) -> TokenStream { let inner_enum_inspector = inner_enum_inspector(data_enum, name, root_attributes); @@ -108,6 +109,7 @@ pub fn serialize( quote!(match self { #inner_enum_inspector }), + generics, ) } diff --git a/yaserde_derive/src/ser/expand_struct.rs b/yaserde_derive/src/ser/expand_struct.rs index 6bb5c49..b9e836e 100644 --- a/yaserde_derive/src/ser/expand_struct.rs +++ b/yaserde_derive/src/ser/expand_struct.rs @@ -3,14 +3,15 @@ use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; use crate::ser::{element::*, implement_serializer::implement_serializer}; use proc_macro2::TokenStream; use quote::quote; -use syn::DataStruct; use syn::Ident; +use syn::{DataStruct, Generics}; pub fn serialize( data_struct: &DataStruct, name: &Ident, root: &str, root_attributes: &YaSerdeAttribute, + generics: &Generics, ) -> TokenStream { let append_attributes: TokenStream = data_struct .fields @@ -348,5 +349,6 @@ pub fn serialize( root_attributes, append_attributes, struct_inspector, + generics, ) } diff --git a/yaserde_derive/src/ser/implement_serializer.rs b/yaserde_derive/src/ser/implement_serializer.rs index da3d9ab..82c3759 100644 --- a/yaserde_derive/src/ser/implement_serializer.rs +++ b/yaserde_derive/src/ser/implement_serializer.rs @@ -3,6 +3,7 @@ use crate::ser::namespace::generate_namespaces_definition; use proc_macro2::Ident; use proc_macro2::TokenStream; use quote::quote; +use syn::Generics; pub fn implement_serializer( name: &Ident, @@ -10,12 +11,15 @@ pub fn implement_serializer( attributes: &YaSerdeAttribute, append_attributes: TokenStream, inner_inspector: TokenStream, + generics: &Generics, ) -> TokenStream { let namespaces_definition = generate_namespaces_definition(attributes); let flatten = attributes.flatten; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + quote! { - impl ::yaserde::YaSerialize for #name { + impl #impl_generics ::yaserde::YaSerialize for #name #ty_generics #where_clause { #[allow(unused_variables)] fn serialize( &self, diff --git a/yaserde_derive/src/ser/mod.rs b/yaserde_derive/src/ser/mod.rs index 98548ab..a09e181 100644 --- a/yaserde_derive/src/ser/mod.rs +++ b/yaserde_derive/src/ser/mod.rs @@ -13,6 +13,7 @@ pub fn expand_derive_serialize(ast: &syn::DeriveInput) -> Result Result { - 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) => { - 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!(), };