use crate::common::{Field, YaSerdeAttribute}; use crate::de::build_default_value::build_default_value; use proc_macro2::{Span, TokenStream}; use std::collections::BTreeMap; use syn::{ spanned::Spanned, DataStruct, Ident, }; pub fn parse( data_struct: &DataStruct, name: &Ident, root: &str, root_attributes: &YaSerdeAttribute, ) -> TokenStream { let namespaces_matches: TokenStream = root_attributes .namespaces .iter() .map(|(p, ns)| { if root_attributes.prefix.as_ref() == Some(p) { Some(quote!(#ns => {})) } else { None } }) .filter_map(|x| x) .collect(); let variables: TokenStream = data_struct .fields .iter() .map(|field| { let label = &get_value_label(&field.ident); let field_attrs = YaSerdeAttribute::parse(&field.attrs); match Field::from(field) { Field::FieldStruct { struct_name } => build_default_value( label, "e! {#struct_name}, "e! {#struct_name::default()}, &field_attrs.default, ), Field::FieldOption { .. } => { if let Some(d) = &field_attrs.default { let default_function = Ident::new(&d, field.span()); Some(quote! { #[allow(unused_mut, non_snake_case, non_camel_case_types)] let mut #label = #default_function(); }) } else { Some(quote! { #[allow(unused_mut, non_snake_case, non_camel_case_types)] let mut #label = None; }) } } Field::FieldVec { data_type } => match *data_type { Field::FieldStruct { ref struct_name } => build_default_value( label, "e! {Vec<#struct_name>}, "e! {vec![]}, &field_attrs.default, ), Field::FieldOption { .. } | Field::FieldVec { .. } => { unimplemented!(); } simple_type => { let type_token: TokenStream = simple_type.into(); build_default_value( label, "e! {Vec<#type_token>}, "e! {vec![]}, &field_attrs.default, ) } }, simple_type => { let type_token: TokenStream = simple_type.into(); build_default_value( label, &type_token, "e! {#type_token::default()}, &field_attrs.default, ) } } }) .filter_map(|x| x) .collect(); let field_visitors: TokenStream = data_struct .fields .iter() .map(|field| { let field_attrs = YaSerdeAttribute::parse(&field.attrs); let label_name = field_attrs .rename .unwrap_or_else(|| field.ident.as_ref().unwrap().to_string()); let struct_visitor = |struct_name: syn::Path| { let struct_id: String = struct_name .segments .iter() .map(|s| s.ident.to_string()) .collect(); let visitor_label = build_visitor_ident(&label_name, field.span(), Some(&struct_name)); Some(quote! { #[allow(non_snake_case, non_camel_case_types)] struct #visitor_label; impl<'de> Visitor<'de> for #visitor_label { type Value = #struct_name; fn visit_str(self, v: &str) -> Result { let content = "<".to_string() + #struct_id + ">" + v + ""; let value : Result<#struct_name, String> = yaserde::de::from_str(&content); value } } }) }; let simple_type_visitor = |simple_type: Field| { let visitor = simple_type.get_simple_type_visitor(); let visitor_label = build_visitor_ident(&label_name, field.span(), None); let field_type: TokenStream = simple_type.into(); 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 { Ok(#field_type::from_str(v).unwrap()) } } }) }; match Field::from(field) { Field::FieldStruct { struct_name } => struct_visitor(struct_name), Field::FieldOption { data_type } => match *data_type { Field::FieldStruct { struct_name } => struct_visitor(struct_name), Field::FieldOption { .. } | Field::FieldVec { .. } => None, simple_type => simple_type_visitor(simple_type), }, Field::FieldVec { data_type } => match *data_type { Field::FieldStruct { struct_name } => struct_visitor(struct_name), Field::FieldOption { .. } | Field::FieldVec { .. } => None, simple_type => simple_type_visitor(simple_type), }, simple_type => simple_type_visitor(simple_type), } }) .filter_map(|x| x) .collect(); let call_visitors: TokenStream = data_struct .fields .iter() .map(|field| { let field_attrs = YaSerdeAttribute::parse(&field.attrs); let label = &field.ident; let value_label = &get_value_label(&field.ident); if field_attrs.attribute || field_attrs.flatten { return None; } let label_name = field_attrs .rename .clone() .unwrap_or_else(|| label.as_ref().unwrap().to_string()); let visit_struct = |struct_name: syn::Path, action: TokenStream| { Some(quote! { #label_name => { if depth == 0 { // Don't count current struct's StartElement as substruct's StartElement let _root = reader.next_event(); } if let Ok(XmlEvent::StartElement { .. }) = reader.peek() { // If substruct's start element found then deserialize substruct let value = #struct_name::deserialize(reader)?; #value_label #action; } } }) }; let visit_simple = |simple_type: Field, action: TokenStream| { let field_visitor = simple_type.get_simple_type_visitor(); let field_type: TokenStream = simple_type.into(); build_call_visitor( &field_type, &field_visitor, &action, &field_attrs, label, &root_attributes.namespaces, field.span(), ) }; let visit_sub = |sub_type: Box, action: TokenStream| match *sub_type { Field::FieldOption { .. } | Field::FieldVec { .. } => unimplemented!(), Field::FieldStruct { struct_name } => visit_struct(struct_name, action), simple_type => visit_simple(simple_type, action), }; match Field::from(field) { Field::FieldStruct { struct_name } => visit_struct(struct_name, quote! {= value}), Field::FieldOption { data_type } => visit_sub(data_type, quote! {= Some(value)}), Field::FieldVec { data_type } => visit_sub(data_type, quote! {.push(value)}), simple_type => visit_simple(simple_type, quote! {= value}), } }) .filter_map(|x| x) .collect(); let call_flatten_visitors: TokenStream = data_struct .fields .iter() .map(|field| { let field_attrs = YaSerdeAttribute::parse(&field.attrs); let value_label = &get_value_label(&field.ident); if field_attrs.attribute || !field_attrs.flatten { return None; } match Field::from(field) { Field::FieldStruct { .. } => Some(quote! { #value_label = yaserde::de::from_str(&unused_xml_elements)?; }), Field::FieldOption { data_type } => match *data_type { Field::FieldStruct { .. } => Some(quote! { #value_label = yaserde::de::from_str(&unused_xml_elements).ok(); }), field_type => unimplemented!("\"flatten\" is not implemented for {:?}", field_type), }, field_type => unimplemented!("\"flatten\" is not implemented for {:?}", field_type), } }) .filter_map(|x| x) .collect(); let attributes_loading: TokenStream = data_struct .fields .iter() .map(|field| { let field_attrs = YaSerdeAttribute::parse(&field.attrs); if !field_attrs.attribute { return None; } let label = &get_value_label(&field.ident); let label_name = field_attrs .rename .unwrap_or_else(|| field.ident.as_ref().unwrap().to_string()); let visitor_label = build_visitor_ident(&label_name, field.span(), None); let visit = |action: &TokenStream, visitor: &TokenStream, visitor_label: &Ident| { Some(quote! { for attr in attributes { if attr.name.local_name == #label_name { let visitor = #visitor_label{}; let value = visitor.#visitor(&attr.value)?; #label #action; } } }) }; let visit_string = || { Some(quote! { for attr in attributes { if attr.name.local_name == #label_name { #label = attr.value.to_owned(); } } }) }; let visit_struct = |struct_name: syn::Path, action: TokenStream| { visit( &action, "e! {visit_str}, &build_visitor_ident(&label_name, field.span(), Some(&struct_name)), ) }; let visit_simple = |simple_type: Field, action: TokenStream| { visit( &action, &simple_type.get_simple_type_visitor(), &visitor_label, ) }; let visit_sub = |sub_type: Box, action: TokenStream| match *sub_type { Field::FieldOption { .. } | Field::FieldVec { .. } => unimplemented!(), Field::FieldStruct { struct_name } => visit_struct(struct_name, action), simple_type => visit_simple(simple_type, action), }; match Field::from(field) { Field::FieldString => visit_string(), Field::FieldOption { data_type } => visit_sub(data_type, quote! {= Some(value)}), Field::FieldVec { .. } => unimplemented!(), Field::FieldStruct { struct_name } => visit_struct(struct_name, quote! {= value}), simple_type => visit_simple(simple_type, quote! {= value}), } }) .filter_map(|x| x) .collect(); let set_text: TokenStream = data_struct .fields .iter() .map(|field| { let label = &get_value_label(&field.ident); let field_attrs = YaSerdeAttribute::parse(&field.attrs); let set_text = |action: &TokenStream| { if field_attrs.text { Some(quote! {#label = #action;}) } else { None } }; match Field::from(field) { Field::FieldString => set_text("e! {text_content.to_owned()}), Field::FieldStruct { .. } | Field::FieldOption { .. } | Field::FieldVec { .. } => None, simple_type => { let type_token: TokenStream = simple_type.into(); set_text("e! {#type_token::from_str(text_content).unwrap()}) } } }) .filter_map(|x| x) .collect(); let struct_builder: TokenStream = data_struct .fields .iter() .map(|field| { let label = &field.ident; let value_label = &get_value_label(&field.ident); quote! { #label: #value_label, } }) .collect(); let (init_unused, write_unused, visit_unused) = if call_flatten_visitors.is_empty() { (None, None, None) } else { build_code_for_unused_xml_events(&call_flatten_visitors) }; let flatten = root_attributes.flatten; quote! { use xml::reader::{XmlEvent, EventReader}; use xml::writer::EventWriter; 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, struct_namespace) = if let XmlEvent::StartElement{name, ..} = reader.peek()?.to_owned() { (name.local_name.to_owned(), name.namespace.clone()) } else { (String::from(#root), None) }; debug!("Struct: start to parse {:?}", named_element); if reader.depth() == 0 { if let Some(ref namespace) = struct_namespace { match namespace.as_str() { #namespaces_matches bad_ns => { let msg = format!("bad namespace for {}, found {}", named_element, bad_ns); return Err(msg); } } } } #variables #field_visitors #init_unused let mut depth = 0; loop { let event = reader.peek()?.to_owned(); match event { XmlEvent::StartElement{ref name, ref attributes, ..} => { let mut skipped = false; match name.local_name.as_str() { #call_visitors named_element => { let event = reader.next_event()?; #write_unused if depth > 0 { // Don't skip root element skipped = true; reader.skip_element(|event| { #write_unused })?; } } // name => { // return Err(format!("unknown key {}", name)) // } } if depth == 0 && !skipped { // Look for attributes only at element start #attributes_loading } depth += 1; } XmlEvent::EndElement{ref name} => { if name.local_name == named_element { #write_unused break; } let event = reader.next_event()?; #write_unused depth -= 1; } XmlEvent::EndDocument => { if #flatten { break; } } XmlEvent::Characters(ref text_content) => { #set_text let event = reader.next_event()?; #write_unused } event => { return Err(format!("unknown event {:?}", event)) } } } #visit_unused Ok(#name{#struct_builder}) } } } } fn build_call_visitor( field_type: &TokenStream, visitor: &TokenStream, action: &TokenStream, field_attrs: &YaSerdeAttribute, label: &Option, namespaces: &BTreeMap, span: Span, ) -> Option { let prefix = field_attrs.prefix.clone(); // let label = &field.ident; let value_label = get_value_label(label); let label_name = field_attrs .rename .clone() .unwrap_or_else(|| label.as_ref().unwrap().to_string()); let visitor_label = build_visitor_ident(&label_name, span, None); let namespaces_matches: TokenStream = namespaces .iter() .map(|(p, ns)| { if prefix == Some(p.to_string()) { Some(quote!(#ns => {})) } else { None } }) .filter_map(|x| x) .collect(); Some(quote! { #label_name => { let visitor = #visitor_label{}; if let Some(namespace) = name.namespace.as_ref() { match namespace.as_str() { #namespaces_matches 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 Ok(XmlEvent::Characters(s)) = reader.peek() { let val = visitor.#visitor(&s); let _event = reader.next_event()?; val } else { Err(format!("unable to parse content for {}", #label_name)) } }); if let Ok(value) = result { #value_label#action } } }) } fn get_value_label(ident: &Option) -> Option { ident .clone() .map(|ident| syn::Ident::new(&format!("__{}_value", ident.to_string()), ident.span())) } fn build_visitor_ident(label: &str, span: Span, struct_name: Option<&syn::Path>) -> Ident { let struct_id = struct_name.map_or_else( || "".to_string(), |struct_name| { struct_name .segments .iter() .map(|s| s.ident.to_string()) .collect() }, ); Ident::new( &format!("__Visitor_{}_{}", label.replace(".", "_"), struct_id), span, ) } fn build_code_for_unused_xml_events( call_flatten_visitors: &TokenStream, ) -> ( Option, Option, Option, ) { ( Some(quote! { let mut buf = Vec::new(); let mut writer = Some(EventWriter::new(&mut buf)); }), Some(quote! { if let Some(ref mut w) = writer { if w.write(event.as_writer_event().unwrap()).is_err() { writer = None; } } }), Some(quote! { if writer.is_some() { let unused_xml_elements = String::from_utf8(buf).unwrap(); #call_flatten_visitors } }), ) }