Simplify output token stream for ToXml macro
This commit is contained in:
parent
6c2dd89ef3
commit
c8cc5f5a48
|
@ -4,23 +4,15 @@ use quote::quote;
|
||||||
use crate::{ContainerMeta, FieldMeta};
|
use crate::{ContainerMeta, FieldMeta};
|
||||||
|
|
||||||
pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||||
let ident = &input.ident;
|
|
||||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
|
||||||
|
|
||||||
let root_name = ident.to_string();
|
|
||||||
let mut serializer = Serializer::new(input);
|
|
||||||
|
|
||||||
let mut header = TokenStream::new();
|
|
||||||
serializer.add_header(&mut header);
|
|
||||||
|
|
||||||
let mut body = TokenStream::new();
|
let mut body = TokenStream::new();
|
||||||
let mut attributes = TokenStream::new();
|
let mut attributes = TokenStream::new();
|
||||||
|
let meta = ContainerMeta::from_derive(input);
|
||||||
match &input.data {
|
match &input.data {
|
||||||
syn::Data::Struct(ref data) => {
|
syn::Data::Struct(ref data) => {
|
||||||
match data.fields {
|
match data.fields {
|
||||||
syn::Fields::Named(ref fields) => {
|
syn::Fields::Named(ref fields) => {
|
||||||
fields.named.iter().for_each(|field| {
|
fields.named.iter().for_each(|field| {
|
||||||
serializer.process_named_field(field, &mut body, &mut attributes);
|
process_named_field(field, &mut body, &mut attributes, &meta);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
syn::Fields::Unnamed(_) => todo!(),
|
syn::Fields::Unnamed(_) => todo!(),
|
||||||
|
@ -30,10 +22,32 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut footer = TokenStream::new();
|
let mut prefixes = TokenStream::new();
|
||||||
serializer.add_footer(&root_name, &mut footer);
|
for (key, val) in &meta.ns.prefixes {
|
||||||
|
prefixes.extend(quote!(
|
||||||
|
if serializer.parent_namespaces.get(#val).is_none() {
|
||||||
|
serializer.output.write_str(" xmlns:")?;
|
||||||
|
serializer.output.write_str(#key)?;
|
||||||
|
serializer.output.write_str("=\"")?;
|
||||||
|
serializer.output.write_str(#val)?;
|
||||||
|
serializer.output.write_char('\"')?;
|
||||||
|
}
|
||||||
|
|
||||||
let current_namespaces = serializer.namespaces_token();
|
if let ::std::collections::hash_map::Entry::Vacant(v) = serializer.parent_namespaces.entry(#val) {
|
||||||
|
v.insert(#key);
|
||||||
|
// Will remove added namespaces when going "up"
|
||||||
|
to_remove.push(#val);
|
||||||
|
};
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ident = &input.ident;
|
||||||
|
let root_name = ident.to_string();
|
||||||
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
let default_namespace = match &meta.ns.uri {
|
||||||
|
Some(ns) => quote!(#ns),
|
||||||
|
None => quote!(""),
|
||||||
|
};
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
impl #impl_generics ToXml for #ident #ty_generics #where_clause {
|
impl #impl_generics ToXml for #ident #ty_generics #where_clause {
|
||||||
|
@ -49,12 +63,37 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||||
attribute: None,
|
attribute: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
#attributes
|
// Start tag
|
||||||
|
serializer.output.write_char('<')?;
|
||||||
|
if serializer.parent_default_namespace() != #default_namespace {
|
||||||
|
if let Some(prefix) = serializer.parent_namespaces.get(#default_namespace) {
|
||||||
|
serializer.output.write_str(prefix)?;
|
||||||
|
serializer.output.write_char(':')?;
|
||||||
|
serializer.output.write_str(field_context.name)?;
|
||||||
|
} else {
|
||||||
|
serializer.output.write_str(field_context.name)?;
|
||||||
|
serializer.output.write_str(" xmlns=\"")?;
|
||||||
|
serializer.output.write_str(#default_namespace)?;
|
||||||
|
serializer.output.write_char('\"')?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serializer.output.write_str(field_context.name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
serializer.update_parent_default_namespace(#default_namespace);
|
||||||
|
let mut to_remove: Vec<&str> = Vec::new();
|
||||||
|
#prefixes
|
||||||
|
#attributes
|
||||||
|
serializer.consume_current_attributes()?;
|
||||||
|
serializer.output.write_char('>')?;
|
||||||
|
|
||||||
#header
|
|
||||||
#current_namespaces
|
|
||||||
#body
|
#body
|
||||||
#footer
|
|
||||||
|
// Close tag
|
||||||
|
serializer.output.write_str("</")?;
|
||||||
|
serializer.output.write_str(#root_name)?;
|
||||||
|
serializer.output.write_char('>')?;
|
||||||
|
serializer.retrieve_parent_default_namespace();
|
||||||
|
|
||||||
// Removing current namespaces
|
// Removing current namespaces
|
||||||
for it in to_remove {
|
for it in to_remove {
|
||||||
|
@ -67,140 +106,50 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Serializer {
|
fn process_named_field(
|
||||||
meta: ContainerMeta,
|
field: &syn::Field,
|
||||||
}
|
body: &mut TokenStream,
|
||||||
|
attributes: &mut TokenStream,
|
||||||
|
meta: &ContainerMeta,
|
||||||
|
) {
|
||||||
|
let name = field.ident.as_ref().unwrap().to_string();
|
||||||
|
let field_value = field.ident.as_ref().unwrap();
|
||||||
|
|
||||||
impl<'a> Serializer {
|
let declaration = quote!(
|
||||||
fn new(input: &syn::DeriveInput) -> Self {
|
let mut field = FieldContext {
|
||||||
Self {
|
name: #name,
|
||||||
meta: ContainerMeta::from_derive(input),
|
attribute: None,
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_header(&mut self, output: &'a mut TokenStream) {
|
|
||||||
output.extend(quote!(
|
|
||||||
serializer.output.write_char('<')?;
|
|
||||||
|
|
||||||
));
|
|
||||||
|
|
||||||
let default_namespace = match &self.meta.ns.uri {
|
|
||||||
Some(ns) => quote!(#ns),
|
|
||||||
None => quote!(""),
|
|
||||||
};
|
};
|
||||||
|
);
|
||||||
|
|
||||||
output.extend(quote!(
|
let field_meta = FieldMeta::from_field(field);
|
||||||
// Check if parent default namespace equals
|
if field_meta.attribute {
|
||||||
if serializer.parent_default_namespace() != #default_namespace {
|
attributes.extend(quote!(
|
||||||
if let Some(prefix) = serializer.parent_namespaces.get(#default_namespace) {
|
|
||||||
serializer.output.write_str(prefix)?;
|
|
||||||
serializer.output.write_char(':')?;
|
|
||||||
serializer.output.write_str(field_context.name)?;
|
|
||||||
} else {
|
|
||||||
serializer.output.write_str(field_context.name)?;
|
|
||||||
serializer.output.write_str(" xmlns=\"")?;
|
|
||||||
serializer.output.write_str(#default_namespace)?;
|
|
||||||
serializer.output.write_char('\"')?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
serializer.output.write_str(field_context.name)?;
|
|
||||||
}
|
|
||||||
serializer.update_parent_default_namespace(#default_namespace);
|
|
||||||
));
|
|
||||||
|
|
||||||
for (key, val) in &self.meta.ns.prefixes {
|
|
||||||
output.extend(quote!(
|
|
||||||
if serializer.parent_namespaces.get(#val).is_none() {
|
|
||||||
serializer.output.write_str(" xmlns:")?;
|
|
||||||
serializer.output.write_str(#key)?;
|
|
||||||
serializer.output.write_str("=\"")?;
|
|
||||||
serializer.output.write_str(#val)?;
|
|
||||||
serializer.output.write_char('\"')?;
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attributes
|
|
||||||
output.extend(quote!(
|
|
||||||
serializer.consume_current_attributes()?;
|
|
||||||
));
|
|
||||||
|
|
||||||
output.extend(quote!(
|
|
||||||
serializer.output.write_char('>')?;
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_footer(&mut self, root_name: &str, output: &'a mut TokenStream) {
|
|
||||||
output.extend(quote!(
|
|
||||||
serializer.output.write_str("</")?;
|
|
||||||
serializer.output.write_str(#root_name)?;
|
|
||||||
serializer.output.write_char('>')?;
|
|
||||||
serializer.retrieve_parent_default_namespace();
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_named_field(
|
|
||||||
&mut self,
|
|
||||||
field: &syn::Field,
|
|
||||||
body: &mut TokenStream,
|
|
||||||
attributes: &mut TokenStream,
|
|
||||||
) {
|
|
||||||
let name = field.ident.as_ref().unwrap().to_string();
|
|
||||||
let field_value = field.ident.as_ref().unwrap();
|
|
||||||
|
|
||||||
let declaration = quote!(
|
|
||||||
let mut field = FieldContext {
|
|
||||||
name: #name,
|
|
||||||
attribute: None,
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
let field_meta = FieldMeta::from_field(field);
|
|
||||||
if field_meta.attribute {
|
|
||||||
attributes.extend(quote!(
|
|
||||||
#declaration
|
|
||||||
|
|
||||||
serializer.add_attribute_key(&#name)?;
|
|
||||||
field.attribute = Some(FieldAttribute::Attribute);
|
|
||||||
serializer.set_field_context(field)?;
|
|
||||||
self.#field_value.serialize(serializer)?;
|
|
||||||
));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ns = match field_meta.ns.uri {
|
|
||||||
Some(ns) => quote!(#ns),
|
|
||||||
None => match &self.meta.ns.uri {
|
|
||||||
Some(ns) => quote!(#ns),
|
|
||||||
None => quote!(""),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
body.extend(quote!(
|
|
||||||
#declaration
|
#declaration
|
||||||
match serializer.parent_namespaces.get(#ns) {
|
|
||||||
Some(prefix) => field.attribute = Some(FieldAttribute::Prefix(prefix)),
|
serializer.add_attribute_key(&#name)?;
|
||||||
None => field.attribute = Some(FieldAttribute::Namespace(#ns)),
|
field.attribute = Some(FieldAttribute::Attribute);
|
||||||
}
|
|
||||||
serializer.set_field_context(field)?;
|
serializer.set_field_context(field)?;
|
||||||
self.#field_value.serialize(serializer)?;
|
self.#field_value.serialize(serializer)?;
|
||||||
));
|
));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn namespaces_token(&self) -> TokenStream {
|
let ns = match field_meta.ns.uri {
|
||||||
let mut namespaces = quote!(
|
Some(ns) => quote!(#ns),
|
||||||
let mut to_remove: Vec<&str> = Vec::new();
|
None => match &meta.ns.uri {
|
||||||
);
|
Some(ns) => quote!(#ns),
|
||||||
for (k, v) in self.meta.ns.prefixes.iter() {
|
None => quote!(""),
|
||||||
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) {
|
|
||||||
v.insert(#k);
|
body.extend(quote!(
|
||||||
// Will remove added namespaces when going "up"
|
#declaration
|
||||||
to_remove.push(#v);
|
match serializer.parent_namespaces.get(#ns) {
|
||||||
};
|
Some(prefix) => field.attribute = Some(FieldAttribute::Prefix(prefix)),
|
||||||
))
|
None => field.attribute = Some(FieldAttribute::Namespace(#ns)),
|
||||||
}
|
}
|
||||||
namespaces
|
serializer.set_field_context(field)?;
|
||||||
}
|
self.#field_value.serialize(serializer)?;
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue