feat: support generic

This commit is contained in:
huangjf
2024-05-30 15:10:08 +08:00
committed by Marc-Antoine Arnaud
parent 177fa8e5a7
commit 58d81c7a87
15 changed files with 503 additions and 129 deletions

View File

@@ -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<R: ::std::io::Read>(
reader: &mut ::yaserde::de::Deserializer<R>,

View File

@@ -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<R: ::std::io::Read>(
reader: &mut ::yaserde::de::Deserializer<R>,

View File

@@ -10,6 +10,7 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result<TokenStream,
let name = &ast.ident;
let attrs = &ast.attrs;
let data = &ast.data;
let generics = &ast.generics;
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 {
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) => {
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!(),
};

View File

@@ -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)
}

View 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"))
}

View File

@@ -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,
)
}

View File

@@ -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,
)
}

View File

@@ -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<W: ::std::io::Write>(
&self,

View File

@@ -13,6 +13,7 @@ pub fn expand_derive_serialize(ast: &syn::DeriveInput) -> Result<TokenStream, St
let name = &ast.ident;
let attrs = &ast.attrs;
let data = &ast.data;
let generics = &ast.generics;
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 {
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) => {
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!(),
};