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(reader: &mut yaserde::de::Deserializer) -> Result { 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 { 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 { #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, "e! { 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( "e! { visit_str }, "e! { #struct_name }, "e! { let content = "<".to_string() + #struct_id + ">" + v + ""; 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() }