Reduce code redundancy (#28)
This commit is contained in:
parent
4a61982792
commit
44f8a2cc6d
|
@ -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 }
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue