use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{DataEnum, Fields, Ident}; pub fn parse( data_enum: &DataEnum, name: &Ident, root: &str, root_attributes: &YaSerdeAttribute, ) -> TokenStream { let namespaces_matching = root_attributes.get_namespace_matching( &None, quote!(enum_namespace), quote!(named_element), true, ); 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! { impl ::yaserde::YaDeserialize for #name { #[allow(unused_variables)] fn deserialize( reader: &mut ::yaserde::de::Deserializer, ) -> ::std::result::Result { let (named_element, enum_namespace) = if let ::xml::reader::XmlEvent::StartElement{ name, .. } = reader.peek()?.to_owned() { (name.local_name.to_owned(), name.namespace.clone()) } else { (::std::string::String::from(#root), ::std::option::Option::None) }; let start_depth = reader.depth(); ::log::debug!("Enum {} @ {}: start to parse {:?}", stringify!(#name), start_depth, named_element); #namespaces_matching #[allow(unused_assignments, unused_mut)] let mut enum_value = ::std::option::Option::None; loop { let event = reader.peek()?.to_owned(); ::log::trace!("Enum {} @ {}: matching {:?}", stringify!(#name), start_depth, event); match event { ::xml::reader::XmlEvent::StartElement { ref name, ref attributes, .. } => { match name.local_name.as_str() { #match_to_enum _named_element => { let _root = reader.next_event(); } } if let ::xml::reader::XmlEvent::Characters(content) = reader.peek()?.to_owned() { match content.as_str() { #match_to_enum _ => {} } } } ::xml::reader::XmlEvent::EndElement { ref name } => { if name.local_name == named_element { break; } let _root = reader.next_event(); } ::xml::reader::XmlEvent::Characters(ref text_content) => { let _root = reader.next_event(); } ::xml::reader::XmlEvent::EndDocument => { if #flatten { break; } return ::std::result::Result::Err( ::std::format!("End of document, missing some content ?"), ); } event => { return ::std::result::Result::Err(::std::format!("unknown event {:?}", event)) } } } ::log::debug!("Enum {} @ {}: success", stringify!(#name), start_depth); match enum_value { ::std::option::Option::Some(value) => ::std::result::Result::Ok(value), ::std::option::Option::None => { ::std::result::Result::Ok(<#name as ::std::default::Default>::default()) }, } } } } } fn parse_variant(variant: &syn::Variant, name: &Ident) -> Option { let xml_element_name = YaSerdeAttribute::parse(&variant.attrs).xml_element_name(&variant.ident); let variant_name = { let label = &variant.ident; quote! { #name::#label } }; match variant.fields { Fields::Unit => Some(quote! { #xml_element_name => { enum_value = ::std::option::Option::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() .map(|field| YaSerdeField::new(field.clone())) .enumerate() .filter_map(|(idx, field)| { let visitor_label = Ident::new(&format!("__Visitor_{}", idx), field.get_span()); let make_visitor = |visitor: &Ident, field_type: &TokenStream, fn_body: &TokenStream| { quote! { #[allow(non_snake_case, non_camel_case_types)] struct #visitor_label; impl<'de> ::yaserde::Visitor<'de> for #visitor_label { type Value = #field_type; fn #visitor( self, v: &::std::primitive::str, ) -> ::std::result::Result { #fn_body } } } }; let simple_type_visitor = |simple_type: Field| { let visitor = simple_type.get_simple_type_visitor(); let field_type = simple_type.into(); make_visitor( &visitor, &field_type, "e! { ::std::result::Result::Ok(#field_type::from_str(v).unwrap()) }, ) }; match field.get_type() { Field::FieldStruct { struct_name } => { let struct_id: std::string::String = struct_name .segments .iter() .map(|s| s.ident.to_string()) .collect(); Some(make_visitor( &Ident::new("visit_str", Span::call_site()), "e! { #struct_name }, "e! { let content = "<".to_string() + #struct_id + ">" + v + ""; let value: ::std::result::Result<#struct_name, ::std::string::String> = ::yaserde::de::from_str(&content); value }, )) } Field::FieldOption { data_type } | Field::FieldVec { data_type } => match *data_type { Field::FieldStruct { .. } => None, simple_type => Some(simple_type_visitor(simple_type)), }, simple_type => Some(simple_type_visitor(simple_type)), } }) .collect() } fn build_unnamed_visitor_calls( fields: &syn::FieldsUnnamed, variant_name: &TokenStream, ) -> TokenStream { fields .unnamed .iter() .map(|field| YaSerdeField::new(field.clone())) .enumerate() .map(|(idx, field)| { let visitor_label = Ident::new(&format!("__Visitor_{}", idx), field.get_span()); let call_simple_type_visitor = |simple_type: Field, action| { let visitor = simple_type.get_simple_type_visitor(); let field_type: TokenStream = simple_type.into(); let label_name = format!("field_{}", idx); Some(quote! { let visitor = #visitor_label{}; let result = reader.read_inner_value::<#field_type, _>(|reader| { if let ::xml::reader::XmlEvent::EndElement { .. } = *reader.peek()? { return visitor.#visitor(""); } if let ::std::result::Result::Ok(::xml::reader::XmlEvent::Characters(s)) = reader.next_event() { visitor.#visitor(&s) } else { ::std::result::Result::Err( ::std::format!("unable to parse content for {}", #label_name), ) } }); if let ::std::result::Result::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 = ::std::option::Option::Some(#variant_name(value)) }; let set_opt = quote! { enum_value = ::std::option::Option::Some(#variant_name(::std::option::Option::Some(value))) }; let set_vec = quote! { match enum_value { Some(ref mut v) => match v { #variant_name(ref mut v) => v.push(value), _ => { return ::std::result::Result::Err( ::std::string::String::from("Got sequence of different types"), ); } } None => { enum_value = ::std::option::Option::Some(#variant_name(vec![value])); } } }; match field.get_type() { Field::FieldStruct { struct_name } => call_struct_visitor(struct_name, set_val), Field::FieldOption { data_type } => match *data_type { Field::FieldStruct { struct_name } => call_struct_visitor(struct_name, set_opt), simple_type => call_simple_type_visitor(simple_type, set_opt), }, Field::FieldVec { data_type } => match *data_type { Field::FieldStruct { 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() }