Reduce code redundancy (#28)

This commit is contained in:
rsdy 2022-09-27 17:23:28 +01:00 committed by GitHub
parent 4a61982792
commit 44f8a2cc6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 96 additions and 91 deletions

View File

@ -1,36 +1,25 @@
use proc_macro2::{Ident, Span, TokenStream}; use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens}; use quote::quote;
use syn::{spanned::Spanned, ImplGenerics}; use syn::spanned::Spanned;
use super::{discard_lifetimes, ContainerMeta, FieldMeta, Namespace, VariantMeta}; use super::{discard_lifetimes, ContainerMeta, FieldMeta, Namespace, VariantMeta};
pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream { pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream {
let ident = &input.ident;
let meta = match ContainerMeta::from_derive(input) { let meta = match ContainerMeta::from_derive(input) {
Ok(meta) => meta, Ok(meta) => meta,
Err(e) => return e.to_compile_error(), Err(e) => return e.to_compile_error(),
}; };
let mut xml_generics = input.generics.clone();
let mut xml = syn::LifetimeDef::new(syn::Lifetime::new("'xml", Span::call_site()));
xml.bounds
.extend(xml_generics.lifetimes().map(|lt| lt.lifetime.clone()));
xml_generics.params.push(xml.into());
let (xml_impl_generics, _, _) = xml_generics.split_for_impl();
match &input.data { match &input.data {
syn::Data::Struct(_) if meta.scalar => { syn::Data::Struct(_) if meta.scalar => {
syn::Error::new(input.span(), "scalar structs are unsupported!").to_compile_error() syn::Error::new(input.span(), "scalar structs are unsupported!").to_compile_error()
} }
syn::Data::Struct(ref data) => { syn::Data::Struct(ref data) => deserialize_struct(input, data, meta),
deserialize_struct(input, data, meta, ident, xml_impl_generics)
}
syn::Data::Enum(_) if !meta.scalar => { syn::Data::Enum(_) if !meta.scalar => {
syn::Error::new(input.span(), "non-scalar enums are currently unsupported!") syn::Error::new(input.span(), "non-scalar enums are currently unsupported!")
.to_compile_error() .to_compile_error()
} }
syn::Data::Enum(ref data) => deserialize_enum(input, data, meta, xml_impl_generics), syn::Data::Enum(ref data) => deserialize_enum(input, data, meta),
_ => todo!(), _ => todo!(),
} }
} }
@ -40,7 +29,6 @@ fn deserialize_enum(
input: &syn::DeriveInput, input: &syn::DeriveInput,
data: &syn::DataEnum, data: &syn::DataEnum,
meta: ContainerMeta, meta: ContainerMeta,
xml_impl_generics: ImplGenerics
) -> TokenStream { ) -> TokenStream {
let ident = &input.ident; let ident = &input.ident;
let mut variants = TokenStream::new(); let mut variants = TokenStream::new();
@ -56,10 +44,12 @@ fn deserialize_enum(
variants.extend(quote!(Ok(#serialize_as) => Ok(#ident::#v_ident),)); variants.extend(quote!(Ok(#serialize_as) => Ok(#ident::#v_ident),));
} }
let generics = meta.xml_generics();
let (impl_generics, _, _) = generics.split_for_impl();
let (_, ty_generics, where_clause) = input.generics.split_for_impl(); let (_, ty_generics, where_clause) = input.generics.split_for_impl();
quote!( quote!(
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause { impl #impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
fn deserialize<'cx>(deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>) -> Result<Self, ::instant_xml::Error> { fn deserialize<'cx>(deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>) -> Result<Self, ::instant_xml::Error> {
match deserializer.take_str() { match deserializer.take_str() {
#variants #variants
@ -76,16 +66,7 @@ fn deserialize_struct(
input: &syn::DeriveInput, input: &syn::DeriveInput,
data: &syn::DataStruct, data: &syn::DataStruct,
container_meta: ContainerMeta, container_meta: ContainerMeta,
ident: &Ident,
xml_impl_generics: ImplGenerics,
) -> TokenStream { ) -> TokenStream {
let default_namespace = match &container_meta.ns.uri {
Some(ns) => quote!(#ns),
None => quote!(""),
};
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
let mut namespaces_map = quote!(let mut namespaces_map = std::collections::HashMap::new();); let mut namespaces_map = quote!(let mut namespaces_map = std::collections::HashMap::new(););
for (k, v) in container_meta.ns.prefixes.iter() { for (k, v) in container_meta.ns.prefixes.iter() {
namespaces_map.extend(quote!( namespaces_map.extend(quote!(
@ -104,7 +85,7 @@ fn deserialize_struct(
match data.fields { match data.fields {
syn::Fields::Named(ref fields) => { syn::Fields::Named(ref fields) => {
fields.named.iter().enumerate().for_each(|(index, field)| { fields.named.iter().enumerate().for_each(|(index, field)| {
let field_meta = match FieldMeta::from_field(field) { let field_meta = match FieldMeta::from_field(field, &container_meta) {
Ok(meta) => meta, Ok(meta) => meta,
Err(err) => { Err(err) => {
return_val.extend(err.into_compile_error()); return_val.extend(err.into_compile_error());
@ -144,10 +125,13 @@ fn deserialize_struct(
let attributes_names = attributes_tokens.names; let attributes_names = attributes_tokens.names;
let attr_type_match = attributes_tokens.r#match; let attr_type_match = attributes_tokens.r#match;
let name = match &container_meta.rename { let ident = &input.ident;
Some(name) => quote!(#name), let name = container_meta.tag();
None => ident.to_string().into_token_stream(), let default_namespace = container_meta.default_namespace();
}; let generics = container_meta.xml_generics();
let (xml_impl_generics, _, _) = generics.split_for_impl();
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
quote!( quote!(
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause { impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
@ -233,22 +217,7 @@ fn process_field(
container_meta: &ContainerMeta, container_meta: &ContainerMeta,
) { ) {
let field_name = field.ident.as_ref().unwrap(); let field_name = field.ident.as_ref().unwrap();
let field_tag = field_meta.tag;
let field_tag = match &field_meta.rename {
Some(rename) => quote!(#rename),
None => container_meta
.rename_all
.apply_to_field(field_name)
.into_token_stream(),
};
let const_field_var_str = Ident::new(&field_name.to_string().to_uppercase(), Span::call_site());
let mut no_lifetime_type = field.ty.clone();
discard_lifetimes(&mut no_lifetime_type);
let enum_name = Ident::new(&format!("__Value{index}"), Span::call_site());
tokens.r#enum.extend(quote!(#enum_name,));
let default_ns = match &field_meta.ns.uri { let default_ns = match &field_meta.ns.uri {
None => &container_meta.ns.uri, None => &container_meta.ns.uri,
_ => &field_meta.ns.uri, _ => &field_meta.ns.uri,
@ -260,6 +229,13 @@ fn process_field(
None => quote!(""), None => quote!(""),
}; };
let const_field_var_str = Ident::new(&field_name.to_string().to_uppercase(), Span::call_site());
let mut no_lifetime_type = field.ty.clone();
discard_lifetimes(&mut no_lifetime_type);
let enum_name = Ident::new(&format!("__Value{index}"), Span::call_site());
tokens.r#enum.extend(quote!(#enum_name,));
tokens.consts.extend(quote!( tokens.consts.extend(quote!(
const #const_field_var_str: Id<'static> = <#no_lifetime_type as FromXml<'_>>::KIND.name( const #const_field_var_str: Id<'static> = <#no_lifetime_type as FromXml<'_>>::KIND.name(
Id { ns: #ns, name: #field_tag } Id { ns: #ns, name: #field_tag }

View File

@ -8,11 +8,11 @@ use std::collections::BTreeMap;
use std::fmt; use std::fmt;
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Span, TokenStream, TokenTree}; use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
use quote::ToTokens; use quote::{quote, ToTokens};
use syn::parse_macro_input;
use syn::punctuated::Punctuated; use syn::punctuated::Punctuated;
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::token::Colon2; use syn::token::Colon2;
use syn::{parse_macro_input, DeriveInput, Generics};
use case::RenameRule; use case::RenameRule;
@ -28,17 +28,22 @@ pub fn from_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
proc_macro::TokenStream::from(de::from_xml(&ast)) proc_macro::TokenStream::from(de::from_xml(&ast))
} }
#[derive(Debug, Default)] struct ContainerMeta<'input> {
struct ContainerMeta { input: &'input DeriveInput,
ns: NamespaceMeta, ns: NamespaceMeta,
rename: Option<Literal>, rename: Option<Literal>,
rename_all: RenameRule, rename_all: RenameRule,
scalar: bool, scalar: bool,
} }
impl ContainerMeta { impl<'input> ContainerMeta<'input> {
fn from_derive(input: &syn::DeriveInput) -> Result<ContainerMeta, syn::Error> { fn from_derive(input: &'input syn::DeriveInput) -> Result<Self, syn::Error> {
let mut meta = ContainerMeta::default(); let mut ns = NamespaceMeta::default();
let mut rename = Default::default();
let mut rename_all = Default::default();
let mut scalar = Default::default();
for (item, span) in meta_items(&input.attrs) { for (item, span) in meta_items(&input.attrs) {
match item { match item {
MetaItem::Attribute => { MetaItem::Attribute => {
@ -47,18 +52,49 @@ impl ContainerMeta {
"attribute key invalid in container xml attribute", "attribute key invalid in container xml attribute",
)) ))
} }
MetaItem::Ns(ns) => meta.ns = ns, MetaItem::Ns(namespace) => ns = namespace,
MetaItem::Rename(lit) => meta.rename = Some(lit), MetaItem::Rename(lit) => rename = Some(lit),
MetaItem::RenameAll(lit) => { MetaItem::RenameAll(lit) => {
meta.rename_all = match RenameRule::from_str(&lit.to_string()) { rename_all = match RenameRule::from_str(&lit.to_string()) {
Ok(rule) => rule, Ok(rule) => rule,
Err(err) => return Err(syn::Error::new(span, err)), Err(err) => return Err(syn::Error::new(span, err)),
}; };
} }
MetaItem::Scalar => meta.scalar = true, MetaItem::Scalar => scalar = true,
} }
} }
Ok(meta)
Ok(Self {
input,
ns,
rename,
rename_all,
scalar,
})
}
fn xml_generics(&self) -> Generics {
let mut xml_generics = self.input.generics.clone();
let mut xml = syn::LifetimeDef::new(syn::Lifetime::new("'xml", Span::call_site()));
xml.bounds
.extend(xml_generics.lifetimes().map(|lt| lt.lifetime.clone()));
xml_generics.params.push(xml.into());
xml_generics
}
fn tag(&self) -> TokenStream {
match &self.rename {
Some(name) => quote!(#name),
None => self.input.ident.to_string().into_token_stream(),
}
}
fn default_namespace(&self) -> TokenStream {
match &self.ns.uri {
Some(ns) => quote!(#ns),
None => quote!(""),
}
} }
} }
@ -66,17 +102,25 @@ impl ContainerMeta {
struct FieldMeta { struct FieldMeta {
attribute: bool, attribute: bool,
ns: NamespaceMeta, ns: NamespaceMeta,
rename: Option<Literal>, tag: TokenStream,
} }
impl FieldMeta { impl FieldMeta {
fn from_field(input: &syn::Field) -> Result<FieldMeta, syn::Error> { fn from_field(input: &syn::Field, container: &ContainerMeta) -> Result<FieldMeta, syn::Error> {
let mut meta = FieldMeta::default(); let field_name = input.ident.as_ref().unwrap();
let mut meta = FieldMeta {
tag: container
.rename_all
.apply_to_field(field_name)
.into_token_stream(),
..Default::default()
};
for (item, span) in meta_items(&input.attrs) { for (item, span) in meta_items(&input.attrs) {
match item { match item {
MetaItem::Attribute => meta.attribute = true, MetaItem::Attribute => meta.attribute = true,
MetaItem::Ns(ns) => meta.ns = ns, MetaItem::Ns(ns) => meta.ns = ns,
MetaItem::Rename(lit) => meta.rename = Some(lit), MetaItem::Rename(lit) => meta.tag = quote!(#lit),
MetaItem::RenameAll(_) => { MetaItem::RenameAll(_) => {
return Err(syn::Error::new( return Err(syn::Error::new(
span, span,

View File

@ -1,5 +1,5 @@
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{quote, ToTokens}; use quote::quote;
use syn::spanned::Spanned; use syn::spanned::Spanned;
use crate::Namespace; use crate::Namespace;
@ -80,28 +80,22 @@ fn serialize_struct(
syn::Fields::Unit => {} syn::Fields::Unit => {}
}; };
let default_namespace = match &meta.ns.uri { let default_namespace = meta.default_namespace();
Some(ns) => quote!(#ns),
None => quote!(""),
};
let cx_len = meta.ns.prefixes.len(); let cx_len = meta.ns.prefixes.len();
let mut context = quote!( let mut context = quote!(
let mut new = ::instant_xml::ser::Context::<#cx_len>::default(); let mut new = ::instant_xml::ser::Context::<#cx_len>::default();
new.default_ns = #default_namespace; new.default_ns = #default_namespace;
); );
for (i, (prefix, ns)) in meta.ns.prefixes.iter().enumerate() { for (i, (prefix, ns)) in meta.ns.prefixes.iter().enumerate() {
context.extend(quote!( context.extend(quote!(
new.prefixes[#i] = ::instant_xml::ser::Prefix { ns: #ns, prefix: #prefix }; new.prefixes[#i] = ::instant_xml::ser::Prefix { ns: #ns, prefix: #prefix };
)); ));
} }
let ident = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let tag = match &meta.rename { let tag = meta.tag();
Some(rename) => quote!(#rename), let ident = &input.ident;
None => ident.to_string().into_token_stream(),
};
quote!( quote!(
impl #impl_generics ToXml for #ident #ty_generics #where_clause { impl #impl_generics ToXml for #ident #ty_generics #where_clause {
@ -145,7 +139,7 @@ fn process_named_field(
meta: &ContainerMeta, meta: &ContainerMeta,
) { ) {
let field_name = field.ident.as_ref().unwrap(); let field_name = field.ident.as_ref().unwrap();
let field_meta = match FieldMeta::from_field(field) { let field_meta = match FieldMeta::from_field(field, meta) {
Ok(meta) => meta, Ok(meta) => meta,
Err(err) => { Err(err) => {
body.extend(err.into_compile_error()); body.extend(err.into_compile_error());
@ -153,15 +147,12 @@ fn process_named_field(
} }
}; };
let tag = match &field_meta.rename { let tag = field_meta.tag;
Some(rename) => quote!(#rename), let default_ns = match &meta.ns.uri {
None => meta Some(ns) => quote!(#ns),
.rename_all None => quote!(""),
.apply_to_field(field_name)
.into_token_stream(),
}; };
let default_ns = &meta.ns.uri;
if field_meta.attribute { if field_meta.attribute {
let (ns, error) = match &field_meta.ns.uri { let (ns, error) = match &field_meta.ns.uri {
Some(Namespace::Path(path)) => match path.get_ident() { Some(Namespace::Path(path)) => match path.get_ident() {
@ -193,10 +184,7 @@ fn process_named_field(
) )
.into_compile_error(), .into_compile_error(),
), ),
None => (match default_ns { None => (default_ns, quote!()),
Some(ns) => quote!(#ns),
None => quote!(""),
}, quote!()),
}; };
attributes.extend(quote!( attributes.extend(quote!(
@ -207,11 +195,8 @@ fn process_named_field(
} }
let ns = match field_meta.ns.uri { let ns = match field_meta.ns.uri {
Some(ns) => quote!(#ns), Some(ref ns) => quote!(#ns),
None => match &meta.ns.uri { None => default_ns,
Some(ns) => quote!(#ns),
None => quote!(""),
},
}; };
let mut no_lifetime_type = field.ty.clone(); let mut no_lifetime_type = field.ty.clone();