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 quote::{quote, ToTokens};
use syn::{spanned::Spanned, ImplGenerics};
use quote::quote;
use syn::spanned::Spanned;
use super::{discard_lifetimes, ContainerMeta, FieldMeta, Namespace, VariantMeta};
pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream {
let ident = &input.ident;
let meta = match ContainerMeta::from_derive(input) {
Ok(meta) => meta,
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 {
syn::Data::Struct(_) if meta.scalar => {
syn::Error::new(input.span(), "scalar structs are unsupported!").to_compile_error()
}
syn::Data::Struct(ref data) => {
deserialize_struct(input, data, meta, ident, xml_impl_generics)
}
syn::Data::Struct(ref data) => deserialize_struct(input, data, meta),
syn::Data::Enum(_) if !meta.scalar => {
syn::Error::new(input.span(), "non-scalar enums are currently unsupported!")
.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!(),
}
}
@ -40,7 +29,6 @@ fn deserialize_enum(
input: &syn::DeriveInput,
data: &syn::DataEnum,
meta: ContainerMeta,
xml_impl_generics: ImplGenerics
) -> TokenStream {
let ident = &input.ident;
let mut variants = TokenStream::new();
@ -56,10 +44,12 @@ fn deserialize_enum(
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();
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> {
match deserializer.take_str() {
#variants
@ -76,16 +66,7 @@ fn deserialize_struct(
input: &syn::DeriveInput,
data: &syn::DataStruct,
container_meta: ContainerMeta,
ident: &Ident,
xml_impl_generics: ImplGenerics,
) -> 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(););
for (k, v) in container_meta.ns.prefixes.iter() {
namespaces_map.extend(quote!(
@ -104,7 +85,7 @@ fn deserialize_struct(
match data.fields {
syn::Fields::Named(ref fields) => {
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,
Err(err) => {
return_val.extend(err.into_compile_error());
@ -144,10 +125,13 @@ fn deserialize_struct(
let attributes_names = attributes_tokens.names;
let attr_type_match = attributes_tokens.r#match;
let name = match &container_meta.rename {
Some(name) => quote!(#name),
None => ident.to_string().into_token_stream(),
};
let ident = &input.ident;
let name = container_meta.tag();
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!(
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
@ -233,22 +217,7 @@ fn process_field(
container_meta: &ContainerMeta,
) {
let field_name = field.ident.as_ref().unwrap();
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 field_tag = field_meta.tag;
let default_ns = match &field_meta.ns.uri {
None => &container_meta.ns.uri,
_ => &field_meta.ns.uri,
@ -260,6 +229,13 @@ fn process_field(
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!(
const #const_field_var_str: Id<'static> = <#no_lifetime_type as FromXml<'_>>::KIND.name(
Id { ns: #ns, name: #field_tag }

View File

@ -8,11 +8,11 @@ use std::collections::BTreeMap;
use std::fmt;
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
use quote::ToTokens;
use syn::parse_macro_input;
use quote::{quote, ToTokens};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::token::Colon2;
use syn::{parse_macro_input, DeriveInput, Generics};
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))
}
#[derive(Debug, Default)]
struct ContainerMeta {
struct ContainerMeta<'input> {
input: &'input DeriveInput,
ns: NamespaceMeta,
rename: Option<Literal>,
rename_all: RenameRule,
scalar: bool,
}
impl ContainerMeta {
fn from_derive(input: &syn::DeriveInput) -> Result<ContainerMeta, syn::Error> {
let mut meta = ContainerMeta::default();
impl<'input> ContainerMeta<'input> {
fn from_derive(input: &'input syn::DeriveInput) -> Result<Self, syn::Error> {
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) {
match item {
MetaItem::Attribute => {
@ -47,18 +52,49 @@ impl ContainerMeta {
"attribute key invalid in container xml attribute",
))
}
MetaItem::Ns(ns) => meta.ns = ns,
MetaItem::Rename(lit) => meta.rename = Some(lit),
MetaItem::Ns(namespace) => ns = namespace,
MetaItem::Rename(lit) => rename = Some(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,
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 {
attribute: bool,
ns: NamespaceMeta,
rename: Option<Literal>,
tag: TokenStream,
}
impl FieldMeta {
fn from_field(input: &syn::Field) -> Result<FieldMeta, syn::Error> {
let mut meta = FieldMeta::default();
fn from_field(input: &syn::Field, container: &ContainerMeta) -> Result<FieldMeta, syn::Error> {
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) {
match item {
MetaItem::Attribute => meta.attribute = true,
MetaItem::Ns(ns) => meta.ns = ns,
MetaItem::Rename(lit) => meta.rename = Some(lit),
MetaItem::Rename(lit) => meta.tag = quote!(#lit),
MetaItem::RenameAll(_) => {
return Err(syn::Error::new(
span,

View File

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