yaserde/yaserde_derive/src/de/expand_enum.rs
2020-04-19 10:57:40 +02:00

299 lines
8.5 KiB
Rust

use crate::attribute::*;
use crate::field_type::*;
use proc_macro2::TokenStream;
use syn::spanned::Spanned;
use syn::DataEnum;
use syn::Fields;
use syn::Ident;
pub fn parse(
data_enum: &DataEnum,
name: &Ident,
root: &str,
root_attributes: &YaSerdeAttribute,
) -> TokenStream {
let match_to_enum: TokenStream = data_enum
.variants
.iter()
.map(|variant| parse_variant(variant, name))
.filter_map(|f| f)
.collect();
let flatten = root_attributes.flatten;
quote! {
use xml::reader::XmlEvent;
use yaserde::Visitor;
#[allow(unknown_lints, unused_imports)]
use std::str::FromStr;
use log::debug;
impl YaDeserialize for #name {
#[allow(unused_variables)]
fn deserialize<R: Read>(reader: &mut yaserde::de::Deserializer<R>) -> Result<Self, String> {
let named_element =
if let XmlEvent::StartElement{name, ..} = reader.peek()?.to_owned() {
name.local_name.to_owned()
} else {
String::from(#root)
};
debug!("Enum: start to parse {:?}", named_element);
#[allow(unused_assignments, unused_mut)]
let mut enum_value = None;
loop {
match reader.peek()?.to_owned() {
XmlEvent::StartElement{ref name, ref attributes, ..} => {
match name.local_name.as_str() {
#match_to_enum
_named_element => {
let _root = reader.next_event();
}
}
if let XmlEvent::Characters(content) = reader.peek()?.to_owned() {
match content.as_str() {
#match_to_enum
_ => {}
}
}
}
XmlEvent::EndElement{ref name} => {
if name.local_name == named_element {
break;
}
let _root = reader.next_event();
}
XmlEvent::Characters(ref text_content) => {
let _root = reader.next_event();
}
XmlEvent::EndDocument => {
if #flatten {
break;
}
return Err(format!("End of document, missing some content ?"))
}
event => {
return Err(format!("unknown event {:?}", event))
}
}
}
match enum_value {
Some(value) => Ok(value),
None => {
Ok(#name::default())
},
}
}
}
}
}
fn parse_variant(variant: &syn::Variant, name: &Ident) -> Option<TokenStream> {
let xml_element_name = YaSerdeAttribute::parse(&variant.attrs)
.rename
.unwrap_or_else(|| variant.ident.to_string());
let variant_name = {
let label = &variant.ident;
quote! { #name::#label }
};
match variant.fields {
Fields::Unit => Some(quote! {
#xml_element_name => {
enum_value = Some(#variant_name);
}
}),
Fields::Unnamed(ref fields) => {
let field_visitors = build_unnamed_field_visitors(fields);
let call_visitors = build_unnamed_visitor_calls(fields, &variant_name);
if fields.unnamed.len() > 1 {
unimplemented!("enum variant with multiple fields")
}
Some(
fields
.unnamed
.iter()
.take(1)
.map(|_field| {
quote! {
#xml_element_name => {
#field_visitors
#call_visitors
}
}
})
.collect(),
)
}
_ => None,
}
}
fn build_unnamed_field_visitors(fields: &syn::FieldsUnnamed) -> TokenStream {
fields
.unnamed
.iter()
.enumerate()
.map(|(idx, field)| {
let visitor_label = Ident::new(&format!("__Visitor_{}", idx), field.span());
let make_visitor =
|visitor: &TokenStream, field_type: &TokenStream, fn_body: &TokenStream| {
Some(quote! {
#[allow(non_snake_case, non_camel_case_types)]
struct #visitor_label;
impl<'de> Visitor<'de> for #visitor_label {
type Value = #field_type;
fn #visitor(self, v: &str) -> Result<Self::Value, String> {
#fn_body
}
}
})
};
let simple_type_visitor = |simple_type| {
let field_type = get_simple_type_token(&simple_type);
let visitor = get_simple_type_visitor(&simple_type);
make_visitor(
&visitor,
&field_type,
&quote! { Ok(#field_type::from_str(v).unwrap()) },
)
};
get_field_type(field).and_then(|f| match f {
FieldType::FieldTypeStruct { struct_name } => {
let struct_id: String = struct_name
.segments
.iter()
.map(|s| s.ident.to_string())
.collect();
make_visitor(
&quote! { visit_str },
&quote! { #struct_name },
&quote! {
let content = "<".to_string() + #struct_id + ">" + v + "</" + #struct_id + ">";
let value : Result<#struct_name, String> = yaserde::de::from_str(&content);
value
},
)
}
FieldType::FieldTypeOption { data_type } | FieldType::FieldTypeVec { data_type } => {
match *data_type {
FieldType::FieldTypeStruct { .. } => None,
simple_type => simple_type_visitor(simple_type),
}
}
simple_type => simple_type_visitor(simple_type),
})
})
.filter_map(|f| f)
.collect()
}
fn build_unnamed_visitor_calls(
fields: &syn::FieldsUnnamed,
variant_name: &TokenStream,
) -> TokenStream {
fields
.unnamed
.iter()
.enumerate()
.map(|(idx, field)| {
let visitor_label = Ident::new(&format!("__Visitor_{}", idx), field.span());
let call_simple_type_visitor = |simple_type, action| {
let field_type = get_simple_type_token(&simple_type);
let visitor = get_simple_type_visitor(&simple_type);
let label_name = format!("field_{}", idx);
Some(quote! {
let visitor = #visitor_label{};
if let Some(namespace) = name.namespace.as_ref() {
match namespace.as_str() {
bad_ns => {
let msg = format!("bad field namespace for {}, found {}",
name.local_name.as_str(),
bad_ns);
return Err(msg);
}
}
}
let result = reader.read_inner_value::<#field_type, _>(|reader| {
if let XmlEvent::EndElement { .. } = *reader.peek()? {
return visitor.#visitor("");
}
if let Ok(XmlEvent::Characters(s)) = reader.next_event() {
visitor.#visitor(&s)
} else {
Err(format!("unable to parse content for {}", #label_name))
}
});
if let Ok(value) = result {
#action
}
})
};
let call_struct_visitor = |struct_name, action| {
Some(quote! {
match #struct_name::deserialize(reader) {
Ok(value) => {
#action;
let _root = reader.next_event();
},
Err(msg) => {
return Err(msg);
},
}
})
};
let set_val = quote! { enum_value = Some(#variant_name(value)) };
let set_opt = quote! { enum_value = Some(#variant_name(Some(value))) };
let set_vec = quote! {
match enum_value {
Some(ref mut v) => match v {
#variant_name(ref mut v) => v.push(value),
_ => return Err(String::from("Got sequence of different types"))
}
None => {
enum_value = Some(#variant_name(vec![value]));
}
}
};
get_field_type(field).and_then(|f| match f {
FieldType::FieldTypeStruct { struct_name } => call_struct_visitor(struct_name, set_val),
FieldType::FieldTypeOption { data_type } => match *data_type {
FieldType::FieldTypeStruct { struct_name } => call_struct_visitor(struct_name, set_opt),
simple_type => call_simple_type_visitor(simple_type, set_opt),
},
FieldType::FieldTypeVec { data_type } => match *data_type {
FieldType::FieldTypeStruct { struct_name } => call_struct_visitor(struct_name, set_vec),
simple_type => call_simple_type_visitor(simple_type, set_vec),
},
simple_type => call_simple_type_visitor(simple_type, set_val),
})
})
.filter_map(|f| f)
.collect()
}