Refactor how attributes are parsed
This commit is contained in:
parent
bebeba8d72
commit
dee065cd9a
|
@ -1,7 +1,7 @@
|
|||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
use crate::{namespaces, retrieve_field_attribute, FieldAttribute};
|
||||
use crate::{ContainerMeta, FieldMeta, Namespace};
|
||||
|
||||
struct Tokens {
|
||||
enum_: TokenStream,
|
||||
|
@ -34,6 +34,13 @@ impl quote::ToTokens for Deserializer {
|
|||
impl Deserializer {
|
||||
pub fn new(input: &syn::DeriveInput) -> Deserializer {
|
||||
let ident = &input.ident;
|
||||
let container_meta = ContainerMeta::from_derive(input);
|
||||
let default_namespace = match container_meta.ns.default {
|
||||
Namespace::Default => String::new(),
|
||||
Namespace::Prefix(_) => panic!("container namespace cannot be prefix"),
|
||||
Namespace::Literal(ns) => ns,
|
||||
};
|
||||
|
||||
let generics = (&input.generics).into_token_stream();
|
||||
let lifetimes = (&input.generics.params).into_token_stream();
|
||||
|
||||
|
@ -56,10 +63,9 @@ impl Deserializer {
|
|||
let name = ident.to_string();
|
||||
let mut out = TokenStream::new();
|
||||
|
||||
let (default_namespace, other_namespaces) = namespaces(&input.attrs);
|
||||
let mut namespaces_map = quote!(let mut namespaces_map = std::collections::HashMap::new(););
|
||||
|
||||
for (k, v) in other_namespaces.iter() {
|
||||
for (k, v) in container_meta.ns.prefixes.iter() {
|
||||
namespaces_map.extend(quote!(
|
||||
namespaces_map.insert(#k, #v);
|
||||
))
|
||||
|
@ -78,26 +84,16 @@ impl Deserializer {
|
|||
match data.fields {
|
||||
syn::Fields::Named(ref fields) => {
|
||||
fields.named.iter().enumerate().for_each(|(index, field)| {
|
||||
let mut field_namespace = None;
|
||||
let (tokens, def_prefix, is_element) = match retrieve_field_attribute(field) {
|
||||
Some(FieldAttribute::Namespace(value)) => {
|
||||
field_namespace = Some(value);
|
||||
(&mut elements_tokens, None, true)
|
||||
let field_meta = FieldMeta::from_field(field);
|
||||
if let Namespace::Prefix(prefix) = &field_meta.ns.default {
|
||||
if !container_meta.ns.prefixes.contains_key(prefix) {
|
||||
panic!("unknown prefix for this type");
|
||||
}
|
||||
Some(FieldAttribute::PrefixIdentifier(def_prefix)) => {
|
||||
if other_namespaces.get(&def_prefix).is_none() {
|
||||
panic!("Namespace with such prefix do not exist for this struct");
|
||||
}
|
||||
|
||||
(&mut elements_tokens, Some(def_prefix), true)
|
||||
},
|
||||
Some(FieldAttribute::Attribute) => {
|
||||
(&mut attributes_tokens, None, false)
|
||||
}
|
||||
None => {
|
||||
(&mut elements_tokens, None, true)
|
||||
},
|
||||
}
|
||||
|
||||
let tokens = match field_meta.attribute {
|
||||
true => &mut attributes_tokens,
|
||||
false => &mut elements_tokens,
|
||||
};
|
||||
|
||||
Self::process_field(
|
||||
|
@ -106,9 +102,7 @@ impl Deserializer {
|
|||
&mut declare_values,
|
||||
&mut return_val,
|
||||
tokens,
|
||||
is_element,
|
||||
def_prefix,
|
||||
field_namespace,
|
||||
field_meta,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -237,9 +231,7 @@ impl Deserializer {
|
|||
declare_values: &mut TokenStream,
|
||||
return_val: &mut TokenStream,
|
||||
tokens: &mut Tokens,
|
||||
is_element: bool,
|
||||
def_prefix: Option<String>,
|
||||
field_namespace: Option<String>,
|
||||
field_meta: FieldMeta,
|
||||
) {
|
||||
let field_var = field.ident.as_ref().unwrap();
|
||||
let field_var_str = field_var.to_string();
|
||||
|
@ -254,7 +246,7 @@ impl Deserializer {
|
|||
const #const_field_var_str: &str = <#no_lifetime_type>::KIND.name(#field_var_str);
|
||||
));
|
||||
|
||||
if is_element {
|
||||
if !field_meta.attribute {
|
||||
tokens.names.extend(quote!(
|
||||
#const_field_var_str => __Elements::#enum_name,
|
||||
));
|
||||
|
@ -268,28 +260,21 @@ impl Deserializer {
|
|||
let mut #enum_name: Option<#no_lifetime_type> = None;
|
||||
));
|
||||
|
||||
let def_prefix = match def_prefix {
|
||||
Some(def_prefix) => quote!(Some(#def_prefix)),
|
||||
None => quote!(None::<&str>),
|
||||
let (field_prefix, new_default_ns) = match field_meta.ns.default {
|
||||
Namespace::Default => (quote!(None::<&str>), quote!(None::<&str>)),
|
||||
Namespace::Prefix(prefix) => (quote!(Some(#prefix)), quote!(None)),
|
||||
Namespace::Literal(ns) => (quote!(None::<&str>), quote!(Some(#ns))),
|
||||
};
|
||||
|
||||
let field_namespace = match field_namespace {
|
||||
Some(field_namespace) => {
|
||||
quote!(let field_namespace: Option<&str> = Some(#field_namespace);)
|
||||
}
|
||||
None => quote!(let field_namespace: Option<&str> = None;),
|
||||
};
|
||||
|
||||
if is_element {
|
||||
if !field_meta.attribute {
|
||||
tokens.match_.extend(quote!(
|
||||
__Elements::#enum_name => {
|
||||
if #enum_name.is_some() {
|
||||
panic!("duplicated value");
|
||||
}
|
||||
|
||||
deserializer.compare_namespace(&item.prefix, #def_prefix)?;
|
||||
#field_namespace
|
||||
deserializer.set_next_def_namespace(field_namespace)?;
|
||||
deserializer.compare_namespace(&item.prefix, #field_prefix)?;
|
||||
deserializer.set_next_def_namespace(#new_default_ns)?;
|
||||
#enum_name = Some(<#no_lifetime_type>::deserialize(deserializer)?);
|
||||
},
|
||||
));
|
||||
|
|
|
@ -7,105 +7,20 @@ use std::collections::HashMap;
|
|||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{parse_macro_input, Lit, Meta, NestedMeta};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{parse_macro_input, Meta, NestedMeta};
|
||||
|
||||
use crate::ser::Serializer;
|
||||
|
||||
const XML: &str = "xml";
|
||||
|
||||
pub(crate) enum FieldAttribute {
|
||||
Namespace(String),
|
||||
PrefixIdentifier(String),
|
||||
Attribute,
|
||||
}
|
||||
|
||||
pub(crate) fn namespaces(attributes: &Vec<syn::Attribute>) -> (String, HashMap<String, String>) {
|
||||
let mut default_namespace = String::new();
|
||||
let mut other_namespaces = HashMap::default();
|
||||
|
||||
let (list, name) = match retrieve_attr_list(attributes) {
|
||||
Some((Some(list), name)) => (list, name),
|
||||
None => return (default_namespace, other_namespaces),
|
||||
_ => panic!("wrong parameters"),
|
||||
};
|
||||
|
||||
if name == "namespace" {
|
||||
let mut iter = list.nested.iter();
|
||||
let mut next = iter.next();
|
||||
if let Some(NestedMeta::Lit(Lit::Str(v))) = next {
|
||||
default_namespace = v.value();
|
||||
next = iter.next();
|
||||
}
|
||||
|
||||
while let Some(value) = next {
|
||||
if let NestedMeta::Meta(Meta::NameValue(key)) = value {
|
||||
if let Lit::Str(value) = &key.lit {
|
||||
other_namespaces
|
||||
.insert(key.path.get_ident().unwrap().to_string(), value.value());
|
||||
next = iter.next();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
panic!("Wrong data")
|
||||
}
|
||||
}
|
||||
|
||||
(default_namespace, other_namespaces)
|
||||
}
|
||||
|
||||
pub(crate) fn retrieve_field_attribute(input: &syn::Field) -> Option<FieldAttribute> {
|
||||
match retrieve_attr_list(&input.attrs) {
|
||||
Some((Some(list), name)) if name.as_str() == "namespace" => match list.nested.first() {
|
||||
Some(NestedMeta::Lit(Lit::Str(v))) => Some(FieldAttribute::Namespace(v.value())),
|
||||
Some(NestedMeta::Meta(Meta::Path(v))) => {
|
||||
if let Some(ident) = v.get_ident() {
|
||||
Some(FieldAttribute::PrefixIdentifier(ident.to_string()))
|
||||
} else {
|
||||
panic!("unexpected parameter");
|
||||
}
|
||||
}
|
||||
_ => panic!("unexpected parameter"),
|
||||
},
|
||||
Some((None, name)) if name.as_str() == "attribute" => Some(FieldAttribute::Attribute),
|
||||
None => None,
|
||||
_ => panic!("unexpected parameter"),
|
||||
}
|
||||
}
|
||||
|
||||
fn retrieve_attr_list(attributes: &Vec<syn::Attribute>) -> Option<(Option<syn::MetaList>, String)> {
|
||||
for attr in attributes {
|
||||
if !attr.path.is_ident(XML) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let nested = match attr.parse_meta() {
|
||||
Ok(Meta::List(meta)) => meta.nested,
|
||||
Ok(_) => todo!(),
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
let list = match nested.first() {
|
||||
Some(NestedMeta::Meta(Meta::List(list))) => list,
|
||||
Some(NestedMeta::Meta(Meta::Path(path))) => {
|
||||
return Some((None, path.get_ident()?.to_string()))
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
return Some((Some(list.to_owned()), list.path.get_ident()?.to_string()));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[proc_macro_derive(ToXml, attributes(xml))]
|
||||
pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let ident = &ast.ident;
|
||||
let generics = (&ast.generics).into_token_stream();
|
||||
|
||||
let root_name = ident.to_string();
|
||||
let mut serializer = Serializer::new(&ast.attrs);
|
||||
let mut serializer = Serializer::new(&ast);
|
||||
|
||||
let mut header = TokenStream::new();
|
||||
serializer.add_header(&mut header);
|
||||
|
@ -162,9 +77,118 @@ pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||
#[proc_macro_derive(FromXml, attributes(xml))]
|
||||
pub fn from_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let deserializer = de::Deserializer::new(&ast);
|
||||
|
||||
proc_macro::TokenStream::from(quote!(
|
||||
#deserializer
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ContainerMeta {
|
||||
ns: NamespaceMeta,
|
||||
}
|
||||
|
||||
impl ContainerMeta {
|
||||
fn from_derive(input: &syn::DeriveInput) -> ContainerMeta {
|
||||
let mut meta = ContainerMeta::default();
|
||||
for item in meta_items(&input.attrs) {
|
||||
match item {
|
||||
Meta::List(list) if list.path.is_ident("namespace") => {
|
||||
meta.ns = NamespaceMeta::from_list(&list.nested)
|
||||
}
|
||||
_ => panic!("invalid xml attribute syntax"),
|
||||
}
|
||||
}
|
||||
meta
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FieldMeta {
|
||||
attribute: bool,
|
||||
ns: NamespaceMeta,
|
||||
}
|
||||
|
||||
impl FieldMeta {
|
||||
fn from_field(input: &syn::Field) -> FieldMeta {
|
||||
let mut meta = FieldMeta::default();
|
||||
for item in meta_items(&input.attrs) {
|
||||
match item {
|
||||
Meta::Path(path) if path.is_ident("attribute") => meta.attribute = true,
|
||||
Meta::List(list) if list.path.is_ident("namespace") => {
|
||||
meta.ns = NamespaceMeta::from_list(&list.nested)
|
||||
}
|
||||
_ => panic!("invalid xml attribute syntax"),
|
||||
}
|
||||
}
|
||||
meta
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct NamespaceMeta {
|
||||
default: Namespace,
|
||||
prefixes: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl NamespaceMeta {
|
||||
fn from_list(list: &Punctuated<NestedMeta, syn::token::Comma>) -> NamespaceMeta {
|
||||
let mut meta = NamespaceMeta::default();
|
||||
for (i, item) in list.iter().enumerate() {
|
||||
match item {
|
||||
NestedMeta::Meta(inner) => match inner {
|
||||
Meta::Path(path) => match path.get_ident() {
|
||||
Some(id) => meta.default = Namespace::Prefix(id.to_string()),
|
||||
None => panic!("invalid xml attribute syntax"),
|
||||
},
|
||||
Meta::NameValue(nv) => match (nv.path.get_ident(), &nv.lit) {
|
||||
(Some(id), syn::Lit::Str(lit)) => {
|
||||
meta.prefixes.insert(id.to_string(), lit.value());
|
||||
}
|
||||
_ => panic!("invalid xml attribute syntax"),
|
||||
},
|
||||
_ => panic!("invalid xml attribute syntax"),
|
||||
},
|
||||
NestedMeta::Lit(syn::Lit::Str(lit)) if i == 0 => {
|
||||
meta.default = Namespace::Literal(lit.value())
|
||||
}
|
||||
_ => panic!("invalid xml attribute syntax"),
|
||||
}
|
||||
}
|
||||
meta
|
||||
}
|
||||
}
|
||||
|
||||
fn meta_items(attrs: &[syn::Attribute]) -> impl Iterator<Item = Meta> + '_ {
|
||||
attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
if !attr.path.is_ident("xml") {
|
||||
return None;
|
||||
}
|
||||
|
||||
match attr.parse_meta() {
|
||||
Ok(Meta::List(meta)) => Some(meta.nested.into_iter()),
|
||||
_ => panic!("unexpected xml attribute syntax"),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.map(|item| match item {
|
||||
NestedMeta::Meta(item) => item,
|
||||
NestedMeta::Lit(_) => panic!("unexpected xml attribute syntax"),
|
||||
})
|
||||
}
|
||||
|
||||
enum Namespace {
|
||||
Default,
|
||||
Prefix(String),
|
||||
Literal(String),
|
||||
}
|
||||
|
||||
impl Default for Namespace {
|
||||
fn default() -> Self {
|
||||
Namespace::Default
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
use crate::{namespaces, retrieve_field_attribute, FieldAttribute};
|
||||
use crate::{ContainerMeta, FieldMeta, Namespace};
|
||||
|
||||
pub struct Serializer {
|
||||
default_namespace: String,
|
||||
other_namespaces: HashMap<String, String>,
|
||||
meta: ContainerMeta,
|
||||
}
|
||||
|
||||
impl<'a> Serializer {
|
||||
pub fn new(attributes: &'a Vec<syn::Attribute>) -> Serializer {
|
||||
let (default_namespace, other_namespaces) = namespaces(attributes);
|
||||
|
||||
Serializer {
|
||||
default_namespace,
|
||||
other_namespaces,
|
||||
pub fn new(input: &syn::DeriveInput) -> Self {
|
||||
Self {
|
||||
meta: ContainerMeta::from_derive(input),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +20,11 @@ impl<'a> Serializer {
|
|||
serializer.output.write_str(field_context.name)?;
|
||||
));
|
||||
|
||||
let default_namespace = &self.default_namespace;
|
||||
let default_namespace = match &self.meta.ns.default {
|
||||
Namespace::Default => "",
|
||||
Namespace::Prefix(_) => panic!("type cannot have prefix as namespace"),
|
||||
Namespace::Literal(ns) => ns,
|
||||
};
|
||||
output.extend(quote!(
|
||||
// Check if parent default namespace equals
|
||||
if serializer.parent_default_namespace() != #default_namespace {
|
||||
|
@ -37,7 +35,7 @@ impl<'a> Serializer {
|
|||
serializer.update_parent_default_namespace(#default_namespace);
|
||||
));
|
||||
|
||||
let mut sorted_values: Vec<_> = self.other_namespaces.iter().collect();
|
||||
let mut sorted_values: Vec<_> = self.meta.ns.prefixes.iter().collect();
|
||||
sorted_values.sort();
|
||||
|
||||
for (key, val) in sorted_values {
|
||||
|
@ -87,53 +85,50 @@ impl<'a> Serializer {
|
|||
};
|
||||
);
|
||||
|
||||
let stream_ref = match retrieve_field_attribute(field) {
|
||||
Some(FieldAttribute::Namespace(namespace)) => {
|
||||
body.extend(quote!(
|
||||
#declaration
|
||||
field.attribute = Some(instant_xml::FieldAttribute::Namespace(#namespace));
|
||||
));
|
||||
body
|
||||
}
|
||||
Some(FieldAttribute::PrefixIdentifier(prefix_key)) => {
|
||||
match self.other_namespaces.get(&prefix_key) {
|
||||
Some(val) => {
|
||||
body.extend(quote!(
|
||||
#declaration
|
||||
let field_meta = FieldMeta::from_field(field);
|
||||
if field_meta.attribute {
|
||||
attributes.extend(quote!(
|
||||
#declaration
|
||||
|
||||
// Check if such namespace already exist, if so change its prefix to parent prefix
|
||||
let prefix_key = match serializer.parent_namespaces.get(#val) {
|
||||
Some(key) => key,
|
||||
None => #prefix_key,
|
||||
};
|
||||
));
|
||||
}
|
||||
None => panic!("Prefix not defined: {}", prefix_key),
|
||||
};
|
||||
serializer.add_attribute_key(&#name)?;
|
||||
field.attribute = Some(instant_xml::FieldAttribute::Attribute);
|
||||
serializer.set_field_context(field)?;
|
||||
self.#field_value.serialize(serializer)?;
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
body.extend(quote!(
|
||||
field.attribute = Some(instant_xml::FieldAttribute::Prefix(prefix_key));
|
||||
));
|
||||
body
|
||||
}
|
||||
Some(FieldAttribute::Attribute) => {
|
||||
attributes.extend(quote!(
|
||||
#declaration
|
||||
if let Namespace::Literal(ns) = &field_meta.ns.default {
|
||||
body.extend(quote!(
|
||||
#declaration
|
||||
field.attribute = Some(instant_xml::FieldAttribute::Namespace(#ns));
|
||||
));
|
||||
} else if let Namespace::Prefix(prefix) = &field_meta.ns.default {
|
||||
match self.meta.ns.prefixes.get(prefix) {
|
||||
Some(val) => {
|
||||
body.extend(quote!(
|
||||
#declaration
|
||||
|
||||
serializer.add_attribute_key(&#name)?;
|
||||
field.attribute = Some(instant_xml::FieldAttribute::Attribute);
|
||||
));
|
||||
attributes
|
||||
}
|
||||
_ => {
|
||||
body.extend(quote!(
|
||||
#declaration
|
||||
));
|
||||
body
|
||||
}
|
||||
// Check if such namespace already exist, if so change its prefix to parent prefix
|
||||
let prefix_key = match serializer.parent_namespaces.get(#val) {
|
||||
Some(key) => key,
|
||||
None => #prefix,
|
||||
};
|
||||
));
|
||||
}
|
||||
None => panic!("Prefix not defined: {}", prefix),
|
||||
};
|
||||
|
||||
body.extend(quote!(
|
||||
field.attribute = Some(instant_xml::FieldAttribute::Prefix(prefix_key));
|
||||
));
|
||||
} else {
|
||||
body.extend(quote!(
|
||||
#declaration
|
||||
));
|
||||
};
|
||||
|
||||
stream_ref.extend(quote!(
|
||||
body.extend(quote!(
|
||||
serializer.set_field_context(field)?;
|
||||
self.#field_value.serialize(serializer)?;
|
||||
));
|
||||
|
@ -143,7 +138,7 @@ impl<'a> Serializer {
|
|||
let mut namespaces = quote!(
|
||||
let mut to_remove: Vec<&str> = Vec::new();
|
||||
);
|
||||
for (k, v) in self.other_namespaces.iter() {
|
||||
for (k, v) in self.meta.ns.prefixes.iter() {
|
||||
namespaces.extend(quote!(
|
||||
// Only adding to HashMap if namespace do not exist, if it exist it will use the parent defined prefix
|
||||
if let std::collections::hash_map::Entry::Vacant(v) = serializer.parent_namespaces.entry(#v) {
|
||||
|
|
Loading…
Reference in New Issue