Add deserialization for enums with values (#8)
This commit is contained in:
parent
a2bf70c5bd
commit
d277d5137b
@ -351,6 +351,178 @@ fn de_attribute_enum() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn de_complex_enum() {
|
||||||
|
#[derive(YaDeserialize, PartialEq, Debug)]
|
||||||
|
pub struct XmlStruct {
|
||||||
|
background: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(YaDeserialize, PartialEq, Debug, Default)]
|
||||||
|
pub struct OtherStruct {
|
||||||
|
fi: i32,
|
||||||
|
se: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(YaDeserialize, PartialEq, Debug)]
|
||||||
|
pub enum Color {
|
||||||
|
White,
|
||||||
|
Black(String),
|
||||||
|
Orange(std::string::String),
|
||||||
|
Red(i32),
|
||||||
|
Green(OtherStruct),
|
||||||
|
Yellow(Option<String>),
|
||||||
|
Brown(Option<OtherStruct>),
|
||||||
|
Blue(Vec<String>),
|
||||||
|
Purple(Vec<i32>),
|
||||||
|
Magenta(Vec<OtherStruct>),
|
||||||
|
#[yaserde(rename = "NotSoCyan")]
|
||||||
|
Cyan(Vec<OtherStruct>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Color {
|
||||||
|
fn default() -> Color {
|
||||||
|
Color::White
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = r#"<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<base>
|
||||||
|
<background>
|
||||||
|
<Black>text</Black>
|
||||||
|
</background>
|
||||||
|
</base>
|
||||||
|
"#;
|
||||||
|
convert_and_validate!(
|
||||||
|
content,
|
||||||
|
XmlStruct,
|
||||||
|
XmlStruct {
|
||||||
|
background: Color::Black(String::from("text")),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let content = r#"<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<base>
|
||||||
|
<background>
|
||||||
|
<Orange>text</Orange>
|
||||||
|
</background>
|
||||||
|
</base>
|
||||||
|
"#;
|
||||||
|
convert_and_validate!(
|
||||||
|
content,
|
||||||
|
XmlStruct,
|
||||||
|
XmlStruct {
|
||||||
|
background: Color::Orange(String::from("text")),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let content = r#"<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<base>
|
||||||
|
<background>
|
||||||
|
<Green>
|
||||||
|
<fi>12</fi>
|
||||||
|
<se>23</se>
|
||||||
|
</Green>
|
||||||
|
</background>
|
||||||
|
</base>
|
||||||
|
"#;
|
||||||
|
convert_and_validate!(
|
||||||
|
content,
|
||||||
|
XmlStruct,
|
||||||
|
XmlStruct {
|
||||||
|
background: Color::Green(OtherStruct { fi: 12, se: 23 }),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let content = r#"<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<base>
|
||||||
|
<background>
|
||||||
|
<Brown>
|
||||||
|
<fi>12</fi>
|
||||||
|
<se>23</se>
|
||||||
|
</Brown>
|
||||||
|
</background>
|
||||||
|
</base>
|
||||||
|
"#;
|
||||||
|
convert_and_validate!(
|
||||||
|
content,
|
||||||
|
XmlStruct,
|
||||||
|
XmlStruct {
|
||||||
|
background: Color::Brown(Some(OtherStruct { fi: 12, se: 23 })),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let content = r#"<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<base>
|
||||||
|
<background>
|
||||||
|
<Blue>abc</Blue>
|
||||||
|
<Blue>def</Blue>
|
||||||
|
</background>
|
||||||
|
</base>
|
||||||
|
"#;
|
||||||
|
convert_and_validate!(
|
||||||
|
content,
|
||||||
|
XmlStruct,
|
||||||
|
XmlStruct {
|
||||||
|
background: Color::Blue(vec![String::from("abc"), String::from("def")]),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let content = r#"<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<base>
|
||||||
|
<background>
|
||||||
|
<Purple>12</Purple>
|
||||||
|
<Purple>43</Purple>
|
||||||
|
</background>
|
||||||
|
</base>
|
||||||
|
"#;
|
||||||
|
convert_and_validate!(
|
||||||
|
content,
|
||||||
|
XmlStruct,
|
||||||
|
XmlStruct {
|
||||||
|
background: Color::Purple(vec![12, 43]),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let content = r#"<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<base>
|
||||||
|
<background>
|
||||||
|
<Magenta><fi>12</fi><se>23</se></Magenta>
|
||||||
|
<Magenta><fi>63</fi><se>98</se></Magenta>
|
||||||
|
</background>
|
||||||
|
</base>
|
||||||
|
"#;
|
||||||
|
convert_and_validate!(
|
||||||
|
content,
|
||||||
|
XmlStruct,
|
||||||
|
XmlStruct {
|
||||||
|
background: Color::Magenta(vec![
|
||||||
|
OtherStruct { fi: 12, se: 23 },
|
||||||
|
OtherStruct { fi: 63, se: 98 }
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let content = r#"<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<base xmlns:ns="http://www.sample.com/ns/domain">
|
||||||
|
<background>
|
||||||
|
<NotSoCyan><fi>12</fi><se>23</se></NotSoCyan>
|
||||||
|
<NotSoCyan><fi>63</fi><se>98</se></NotSoCyan>
|
||||||
|
</background>
|
||||||
|
</base>
|
||||||
|
"#;
|
||||||
|
convert_and_validate!(
|
||||||
|
content,
|
||||||
|
XmlStruct,
|
||||||
|
XmlStruct {
|
||||||
|
background: Color::Cyan(vec![
|
||||||
|
OtherStruct { fi: 12, se: 23 },
|
||||||
|
OtherStruct { fi: 63, se: 98 }
|
||||||
|
])
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn de_name_issue_21() {
|
fn de_name_issue_21() {
|
||||||
#[derive(YaDeserialize, PartialEq, Debug)]
|
#[derive(YaDeserialize, PartialEq, Debug)]
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
use attribute::*;
|
use attribute::*;
|
||||||
use de::build_default_value::build_default_value;
|
|
||||||
use field_type::*;
|
use field_type::*;
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::TokenStreamExt;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use syn::DataEnum;
|
use syn::DataEnum;
|
||||||
use syn::Fields;
|
use syn::Fields;
|
||||||
@ -14,250 +12,18 @@ pub fn parse(
|
|||||||
root: &str,
|
root: &str,
|
||||||
_namespaces: &BTreeMap<String, String>,
|
_namespaces: &BTreeMap<String, String>,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let variables: TokenStream = data_enum
|
|
||||||
.variants
|
|
||||||
.iter()
|
|
||||||
.map(|variant| match variant.fields {
|
|
||||||
Fields::Unit => None,
|
|
||||||
Fields::Named(ref fields) => {
|
|
||||||
let enum_fields = fields
|
|
||||||
.named
|
|
||||||
.iter()
|
|
||||||
.map(|field| {
|
|
||||||
let field_label = &field.ident;
|
|
||||||
let field_attrs = YaSerdeAttribute::parse(&field.attrs);
|
|
||||||
|
|
||||||
match get_field_type(field) {
|
|
||||||
Some(FieldType::FieldTypeString) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {String},
|
|
||||||
"e! {"".to_string()},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(FieldType::FieldTypeBool) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {bool},
|
|
||||||
"e! {false},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(FieldType::FieldTypeI8) => {
|
|
||||||
build_default_value(field_label, "e! {i8}, "e! {0}, &field_attrs.default)
|
|
||||||
}
|
|
||||||
Some(FieldType::FieldTypeU8) => {
|
|
||||||
build_default_value(field_label, "e! {u8}, "e! {0}, &field_attrs.default)
|
|
||||||
}
|
|
||||||
Some(FieldType::FieldTypeI16) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {i16},
|
|
||||||
"e! {0},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(FieldType::FieldTypeU16) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {u16},
|
|
||||||
"e! {0},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(FieldType::FieldTypeI32) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {i32},
|
|
||||||
"e! {0},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(FieldType::FieldTypeU32) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {u32},
|
|
||||||
"e! {0},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(FieldType::FieldTypeI64) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {i64},
|
|
||||||
"e! {0},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(FieldType::FieldTypeU64) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {u64},
|
|
||||||
"e! {0},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(FieldType::FieldTypeF32) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {f32},
|
|
||||||
"e! {0},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(FieldType::FieldTypeF64) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {f64},
|
|
||||||
"e! {0},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(FieldType::FieldTypeStruct { struct_name }) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {#struct_name},
|
|
||||||
"e! {#struct_name::default()},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(FieldType::FieldTypeOption { .. }) => {
|
|
||||||
if let Some(d) = &field_attrs.default {
|
|
||||||
let default_function = Ident::new(&d, Span::call_site());
|
|
||||||
|
|
||||||
Some(quote! {
|
|
||||||
#[allow(unused_mut, non_snake_case, non_camel_case_types)]
|
|
||||||
let mut #field_label = #default_function();
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Some(quote! {
|
|
||||||
#[allow(unused_mut, non_snake_case, non_camel_case_types)]
|
|
||||||
let mut #field_label = None;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(FieldType::FieldTypeVec { data_type }) => {
|
|
||||||
let dt = Box::into_raw(data_type);
|
|
||||||
match unsafe { dt.as_ref() } {
|
|
||||||
Some(&FieldType::FieldTypeString) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<String>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeBool) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<bool>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeI8) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<i8>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeU8) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<u8>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeI16) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<i16>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeU16) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<u16>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeI32) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<i32>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeU32) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<u32>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeI64) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<i64>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeU64) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<u64>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeF32) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<f32>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeF64) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<f64>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeStruct { ref struct_name }) => build_default_value(
|
|
||||||
field_label,
|
|
||||||
"e! {Vec<#struct_name>},
|
|
||||||
"e! {vec![]},
|
|
||||||
&field_attrs.default,
|
|
||||||
),
|
|
||||||
Some(&FieldType::FieldTypeOption { .. })
|
|
||||||
| Some(&FieldType::FieldTypeVec { .. }) => {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(|x| x.is_some())
|
|
||||||
.map(|x| x.unwrap())
|
|
||||||
.fold(TokenStream::new(), |mut sum, val| {
|
|
||||||
sum.append_all(val);
|
|
||||||
sum
|
|
||||||
});
|
|
||||||
|
|
||||||
Some(enum_fields)
|
|
||||||
}
|
|
||||||
Fields::Unnamed(ref _fields) => {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(|x| x.is_some())
|
|
||||||
.map(|x| x.unwrap())
|
|
||||||
.fold(TokenStream::new(), |mut sum, val| {
|
|
||||||
sum.append_all(val);
|
|
||||||
sum
|
|
||||||
});
|
|
||||||
|
|
||||||
let match_to_enum: TokenStream = data_enum
|
let match_to_enum: TokenStream = data_enum
|
||||||
.variants
|
.variants
|
||||||
.iter()
|
.iter()
|
||||||
.map(|variant| {
|
.map(|variant| parse_variant(variant, name))
|
||||||
let field_attrs = YaSerdeAttribute::parse(&variant.attrs);
|
.filter_map(|f| f)
|
||||||
let renamed_label = match field_attrs.rename {
|
.collect();
|
||||||
Some(value) => Ident::new(&value.to_string(), Span::call_site()),
|
|
||||||
None => variant.ident.clone(),
|
|
||||||
};
|
|
||||||
let label = &variant.ident;
|
|
||||||
let label_name = renamed_label.to_string();
|
|
||||||
|
|
||||||
match variant.fields {
|
|
||||||
Fields::Unit => Some(quote! {
|
|
||||||
#label_name => {
|
|
||||||
simple_enum_value = Some(#name::#label);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(|x| x.is_some())
|
|
||||||
.map(|x| x.unwrap())
|
|
||||||
.fold(TokenStream::new(), |mut tokens, token| {
|
|
||||||
tokens.append_all(token);
|
|
||||||
tokens
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
use xml::reader::XmlEvent;
|
use xml::reader::XmlEvent;
|
||||||
|
use yaserde::Visitor;
|
||||||
|
#[allow(unknown_lints, unused_imports)]
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
impl YaDeserialize for #name {
|
impl YaDeserialize for #name {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
@ -271,41 +37,42 @@ pub fn parse(
|
|||||||
debug!("Enum: start to parse {:?}", named_element);
|
debug!("Enum: start to parse {:?}", named_element);
|
||||||
|
|
||||||
#[allow(unused_assignments, unused_mut)]
|
#[allow(unused_assignments, unused_mut)]
|
||||||
let mut simple_enum_value = None;
|
let mut enum_value = None;
|
||||||
|
|
||||||
#variables
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match reader.peek()?.to_owned() {
|
match reader.peek()?.to_owned() {
|
||||||
XmlEvent::StartElement{name, attributes, namespace: _namespace} => {
|
XmlEvent::StartElement{ref name, ref attributes, ..} => {
|
||||||
debug!("Enum: {}: {}", named_element, name.local_name.as_str());
|
|
||||||
if name.local_name == named_element {
|
|
||||||
let _next = reader.next_event();
|
|
||||||
|
|
||||||
if let XmlEvent::Characters(content) = reader.peek()?.to_owned() {
|
match name.local_name.as_str() {
|
||||||
match content.as_str() {
|
#match_to_enum
|
||||||
#match_to_enum
|
named_element => {
|
||||||
_ => {}
|
let _root = reader.next_event();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
XmlEvent::EndElement{name} => {
|
if let XmlEvent::Characters(content) = reader.peek()?.to_owned() {
|
||||||
if name.local_name.as_str() == named_element {
|
match content.as_str() {
|
||||||
|
#match_to_enum
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XmlEvent::EndElement{ref name} => {
|
||||||
|
if name.local_name == named_element {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let _root = reader.next_event();
|
let _root = reader.next_event();
|
||||||
},
|
}
|
||||||
xml::reader::XmlEvent::Characters(characters_content) => {
|
XmlEvent::Characters(ref text_content) => {
|
||||||
let _root = reader.next_event();
|
let _root = reader.next_event();
|
||||||
},
|
}
|
||||||
event => {
|
event => {
|
||||||
return Err(format!("unknown event {:?}", event))
|
return Err(format!("unknown event {:?}", event))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match simple_enum_value {
|
match enum_value {
|
||||||
Some(value) => Ok(value),
|
Some(value) => Ok(value),
|
||||||
None => {
|
None => {
|
||||||
Ok(#name::default())
|
Ok(#name::default())
|
||||||
@ -315,3 +82,227 @@ pub fn parse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_variant(variant: &syn::Variant, name: &Ident) -> Option<TokenStream> {
|
||||||
|
let xml_element_name = YaSerdeAttribute::parse(&variant.attrs)
|
||||||
|
.rename
|
||||||
|
.unwrap_or(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), Span::call_site());
|
||||||
|
|
||||||
|
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, visitor) = convert_simple_type(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 + "</" + #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), Span::call_site());
|
||||||
|
|
||||||
|
let call_simple_type_visitor = |simple_type, action| {
|
||||||
|
let (field_type, visitor) = convert_simple_type(simple_type);
|
||||||
|
|
||||||
|
let label_name = format!("field_{}", idx);
|
||||||
|
|
||||||
|
Some(quote! {
|
||||||
|
let visitor = #visitor_label{};
|
||||||
|
|
||||||
|
if let XmlEvent::StartElement {name, ..} = reader.peek()?.clone() {
|
||||||
|
if let Some(namespace) = name.namespace {
|
||||||
|
match namespace.as_str() {
|
||||||
|
bad_ns => {
|
||||||
|
let msg = format!("bad field namespace for {}, found {}",
|
||||||
|
name.local_name.as_str(),
|
||||||
|
bad_ns);
|
||||||
|
return Err(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.set_map_value()
|
||||||
|
}
|
||||||
|
|
||||||
|
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! {
|
||||||
|
reader.set_map_value();
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_simple_type(simple_type: FieldType) -> (TokenStream, TokenStream) {
|
||||||
|
match simple_type {
|
||||||
|
FieldType::FieldTypeString => (quote! {String}, quote! {visit_str}),
|
||||||
|
FieldType::FieldTypeBool => (quote! {bool}, quote! {visit_bool}),
|
||||||
|
FieldType::FieldTypeU8 => (quote! {u8}, quote! {visit_u8}),
|
||||||
|
FieldType::FieldTypeI8 => (quote! {i8}, quote! {visit_i8}),
|
||||||
|
FieldType::FieldTypeU16 => (quote! {u16}, quote! {visit_u16}),
|
||||||
|
FieldType::FieldTypeI16 => (quote! {i16}, quote! {visit_i16}),
|
||||||
|
FieldType::FieldTypeU32 => (quote! {u32}, quote! {visit_u32}),
|
||||||
|
FieldType::FieldTypeI32 => (quote! {i32}, quote! {visit_i32}),
|
||||||
|
FieldType::FieldTypeU64 => (quote! {u64}, quote! {visit_u64}),
|
||||||
|
FieldType::FieldTypeI64 => (quote! {i64}, quote! {visit_i64}),
|
||||||
|
FieldType::FieldTypeF32 => (quote! {f32}, quote! {visit_f32}),
|
||||||
|
FieldType::FieldTypeF64 => (quote! {f64}, quote! {visit_f64}),
|
||||||
|
_ => panic!("Not a simple type: {:?}", simple_type),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user