serializer changes extractet from simple-deserializer branch

This commit is contained in:
Bartlomiej Choinski 2022-08-11 12:26:45 +02:00 committed by Dirkjan Ochtman
parent 3e9f978846
commit b0e09962bd
2 changed files with 175 additions and 160 deletions

View File

@ -1,10 +1,15 @@
extern crate proc_macro; extern crate proc_macro;
mod se;
use std::collections::{BTreeSet, HashMap};
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
use std::collections::{BTreeSet, HashMap};
use syn::{parse_macro_input, Lit, Meta, NestedMeta}; use syn::{parse_macro_input, Lit, Meta, NestedMeta};
use crate::se::Serializer;
const XML: &str = "xml"; const XML: &str = "xml";
enum FieldAttribute { enum FieldAttribute {
@ -12,160 +17,40 @@ enum FieldAttribute {
PrefixIdentifier(String), PrefixIdentifier(String),
} }
struct Serializer { pub(crate) fn get_namespaces(
default_namespace: Option<String>, attributes: &Vec<syn::Attribute>,
other_namespaces: HashMap<String, String>, ) -> (Option<String>, HashMap<String, String>) {
}
impl<'a> Serializer {
pub fn new(attributes: &'a Vec<syn::Attribute>) -> Serializer {
let mut default_namespace = None; let mut default_namespace = None;
let mut other_namespaces = HashMap::default(); let mut other_namespaces = HashMap::default();
if let Some(list) = Self::retrieve_namespace_list(attributes) { let list = match retrieve_attr_list("namespace", attributes) {
match list.path.get_ident() { Some(v) => v,
Some(ident) if ident == "namespace" => { None => return (default_namespace, other_namespaces),
};
if list.path.get_ident().unwrap() == "namespace" {
let mut iter = list.nested.iter(); let mut iter = list.nested.iter();
if let Some(NestedMeta::Lit(Lit::Str(v))) = iter.next() { if let Some(NestedMeta::Lit(Lit::Str(v))) = iter.next() {
default_namespace = Some(v.value()); default_namespace = Some(v.value());
} }
for item in iter { for item in iter {
match item { if let NestedMeta::Meta(Meta::NameValue(key)) = item {
NestedMeta::Meta(Meta::NameValue(key)) => {
if let Lit::Str(value) = &key.lit { if let Lit::Str(value) = &key.lit {
other_namespaces.insert( other_namespaces
key.path.get_ident().unwrap().to_string(), .insert(key.path.get_ident().unwrap().to_string(), value.value());
value.value(),
);
}
}
_ => todo!(),
}
}
}
_ => (),
}
}
Serializer {
default_namespace,
other_namespaces,
}
}
fn keys_set(&self) -> BTreeSet<&str> {
self.other_namespaces
.iter()
.map(|(k, _)| k.as_str())
.collect()
}
fn add_header(&mut self, root_name: &str, output: &'a mut TokenStream) {
output.extend(quote!(
serializer.output.write_char('<')?;
serializer.output.write_str(#root_name)?;
));
if let Some(default_namespace) = self.default_namespace.as_ref() {
output.extend(quote!(
serializer.output.write_str(" xmlns=\"")?;
serializer.output.write_str(#default_namespace)?;
serializer.output.write_char('\"')?;
));
}
let mut sorted_values: Vec<_> = self.other_namespaces.iter().collect();
sorted_values.sort();
for (key, val) in sorted_values {
output.extend(quote!(
serializer.output.write_str(" xmlns:")?;
serializer.output.write_str(#key)?;
serializer.output.write_str("=\"")?;
serializer.output.write_str(#val)?;
serializer.output.write_char('\"')?;
));
}
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('>')?;
));
}
fn process_named_field(
&mut self,
field: &syn::Field,
output: &'a mut TokenStream,
missing_prefixes: &'a mut BTreeSet<String>,
) {
let name = field.ident.as_ref().unwrap().to_string();
let field_value = field.ident.as_ref().unwrap();
output.extend(quote!(
let mut field = instant_xml::FieldContext {
name: #name,
attribute: None,
};
));
match Self::retrieve_field_attribute(field) {
Some(FieldAttribute::Namespace(namespace_key)) => {
output.extend(quote!(
field.attribute = Some(instant_xml::FieldAttribute::Namespace(#namespace_key));
));
}
Some(FieldAttribute::PrefixIdentifier(prefix_key)) => {
output.extend(quote!(
field.attribute = Some(instant_xml::FieldAttribute::Prefix(#prefix_key));
));
if self.other_namespaces.get(&prefix_key).is_none() {
missing_prefixes.insert(prefix_key);
};
}
_ => {}
};
output.extend(quote!(
self.#field_value.serialize(serializer, Some(&field))?;
));
}
fn retrieve_namespace_list(attributes: &Vec<syn::Attribute>) -> Option<syn::MetaList> {
for attr in attributes {
if !attr.path.is_ident(XML) {
continue; continue;
} }
}
let nested = match attr.parse_meta() { panic!("Wrong data");
Ok(Meta::List(meta)) => meta.nested,
Ok(_) => todo!(),
_ => todo!(),
};
let list = match nested.first() {
Some(NestedMeta::Meta(Meta::List(list))) => list,
_ => todo!(),
};
if list.path.get_ident()? == "namespace" {
return Some(list.to_owned());
} }
} }
None (default_namespace, other_namespaces)
} }
fn retrieve_field_attribute(input: &syn::Field) -> Option<FieldAttribute> { pub(crate) fn retrieve_field_attribute(name: &str, input: &syn::Field) -> Option<FieldAttribute> {
if let Some(list) = Self::retrieve_namespace_list(&input.attrs) { if let Some(list) = retrieve_attr_list(name, &input.attrs) {
match list.nested.first() { match list.nested.first() {
Some(NestedMeta::Lit(Lit::Str(v))) => { Some(NestedMeta::Lit(Lit::Str(v))) => {
return Some(FieldAttribute::Namespace(v.value())); return Some(FieldAttribute::Namespace(v.value()));
@ -180,6 +65,30 @@ impl<'a> Serializer {
} }
None None
} }
fn retrieve_attr_list(name: &str, attributes: &Vec<syn::Attribute>) -> Option<syn::MetaList> {
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,
_ => return None,
};
if list.path.get_ident()? == name {
return Some(list.to_owned());
}
}
None
} }
#[proc_macro_derive(ToXml, attributes(xml))] #[proc_macro_derive(ToXml, attributes(xml))]
@ -189,10 +98,8 @@ pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let root_name = ident.to_string(); let root_name = ident.to_string();
let mut missing_prefixes = BTreeSet::new(); let mut missing_prefixes = BTreeSet::new();
let mut serializer = Serializer::new(&ast.attrs); let mut serializer = Serializer::new(&ast.attrs);
let mut output = TokenStream::new(); let mut output = TokenStream::new();
serializer.add_header(&mut output);
serializer.add_header(&root_name, &mut output);
match &ast.data { match &ast.data {
syn::Data::Struct(ref data) => { syn::Data::Struct(ref data) => {
@ -212,6 +119,7 @@ pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
serializer.add_footer(&root_name, &mut output); serializer.add_footer(&root_name, &mut output);
let current_prefixes = serializer.keys_set(); let current_prefixes = serializer.keys_set();
proc_macro::TokenStream::from(quote!( proc_macro::TokenStream::from(quote!(
impl ToXml for #ident { impl ToXml for #ident {
fn serialize<W>(&self, serializer: &mut instant_xml::Serializer<W>, _field_data: Option<&instant_xml::FieldContext>) -> Result<(), instant_xml::Error> fn serialize<W>(&self, serializer: &mut instant_xml::Serializer<W>, _field_data: Option<&instant_xml::FieldContext>) -> Result<(), instant_xml::Error>

View File

@ -0,0 +1,107 @@
use std::collections::{BTreeSet, HashMap};
use proc_macro2::TokenStream;
use quote::quote;
use crate::{get_namespaces, retrieve_field_attribute, FieldAttribute};
pub struct Serializer {
default_namespace: Option<String>,
other_namespaces: HashMap<String, String>,
}
impl<'a> Serializer {
pub fn new(attributes: &'a Vec<syn::Attribute>) -> Serializer {
let (default_namespace, other_namespaces) = get_namespaces(attributes);
Serializer {
default_namespace,
other_namespaces,
}
}
pub fn keys_set(&self) -> BTreeSet<&str> {
self.other_namespaces
.iter()
.map(|(k, _)| k.as_str())
.collect()
}
pub fn add_header(&mut self, output: &'a mut TokenStream) {
output.extend(quote!(
serializer.output.write_char('<')?;
serializer.output.write_str(field_context.name)?;
));
if let Some(default_namespace) = self.default_namespace.as_ref() {
output.extend(quote!(
serializer.output.write_str(" xmlns=\"")?;
serializer.output.write_str(#default_namespace)?;
serializer.output.write_char('\"')?;
));
}
let mut sorted_values: Vec<_> = self.other_namespaces.iter().collect();
sorted_values.sort();
for (key, val) in sorted_values {
output.extend(quote!(
serializer.output.write_str(" xmlns:")?;
serializer.output.write_str(#key)?;
serializer.output.write_str("=\"")?;
serializer.output.write_str(#val)?;
serializer.output.write_char('\"')?;
));
}
output.extend(quote!(
serializer.output.write_char('>')?;
));
}
pub 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('>')?;
));
}
pub fn process_named_field(
&mut self,
field: &syn::Field,
output: &'a mut TokenStream,
missing_prefixes: &'a mut BTreeSet<String>,
) {
let name = field.ident.as_ref().unwrap().to_string();
let field_value = field.ident.as_ref().unwrap();
output.extend(quote!(
let mut field = instant_xml::FieldContext {
name: #name,
attribute: None,
};
));
match retrieve_field_attribute("namespace", field) {
Some(FieldAttribute::Namespace(namespace_key)) => {
output.extend(quote!(
field.attribute = Some(instant_xml::FieldAttribute::Namespace(#namespace_key));
));
}
Some(FieldAttribute::PrefixIdentifier(prefix_key)) => {
output.extend(quote!(
field.attribute = Some(instant_xml::FieldAttribute::Prefix(#prefix_key));
));
if self.other_namespaces.get(&prefix_key).is_none() {
missing_prefixes.insert(prefix_key);
};
}
_ => {}
};
output.extend(quote!(
self.#field_value.serialize(serializer, Some(&field))?;
));
}
}