Add deserialization for enums with values (#8)

This commit is contained in:
Dmitry Samoylov 2019-12-27 20:37:56 +07:00
parent a2bf70c5bd
commit d277d5137b
2 changed files with 423 additions and 260 deletions

View File

@ -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]
fn de_name_issue_21() {
#[derive(YaDeserialize, PartialEq, Debug)]

View File

@ -1,8 +1,6 @@
use attribute::*;
use de::build_default_value::build_default_value;
use field_type::*;
use proc_macro2::{Span, TokenStream};
use quote::TokenStreamExt;
use std::collections::BTreeMap;
use syn::DataEnum;
use syn::Fields;
@ -14,250 +12,18 @@ pub fn parse(
root: &str,
_namespaces: &BTreeMap<String, String>,
) -> 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,
&quote! {String},
&quote! {"".to_string()},
&field_attrs.default,
),
Some(FieldType::FieldTypeBool) => build_default_value(
field_label,
&quote! {bool},
&quote! {false},
&field_attrs.default,
),
Some(FieldType::FieldTypeI8) => {
build_default_value(field_label, &quote! {i8}, &quote! {0}, &field_attrs.default)
}
Some(FieldType::FieldTypeU8) => {
build_default_value(field_label, &quote! {u8}, &quote! {0}, &field_attrs.default)
}
Some(FieldType::FieldTypeI16) => build_default_value(
field_label,
&quote! {i16},
&quote! {0},
&field_attrs.default,
),
Some(FieldType::FieldTypeU16) => build_default_value(
field_label,
&quote! {u16},
&quote! {0},
&field_attrs.default,
),
Some(FieldType::FieldTypeI32) => build_default_value(
field_label,
&quote! {i32},
&quote! {0},
&field_attrs.default,
),
Some(FieldType::FieldTypeU32) => build_default_value(
field_label,
&quote! {u32},
&quote! {0},
&field_attrs.default,
),
Some(FieldType::FieldTypeI64) => build_default_value(
field_label,
&quote! {i64},
&quote! {0},
&field_attrs.default,
),
Some(FieldType::FieldTypeU64) => build_default_value(
field_label,
&quote! {u64},
&quote! {0},
&field_attrs.default,
),
Some(FieldType::FieldTypeF32) => build_default_value(
field_label,
&quote! {f32},
&quote! {0},
&field_attrs.default,
),
Some(FieldType::FieldTypeF64) => build_default_value(
field_label,
&quote! {f64},
&quote! {0},
&field_attrs.default,
),
Some(FieldType::FieldTypeStruct { struct_name }) => build_default_value(
field_label,
&quote! {#struct_name},
&quote! {#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,
&quote! {Vec<String>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeBool) => build_default_value(
field_label,
&quote! {Vec<bool>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeI8) => build_default_value(
field_label,
&quote! {Vec<i8>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeU8) => build_default_value(
field_label,
&quote! {Vec<u8>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeI16) => build_default_value(
field_label,
&quote! {Vec<i16>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeU16) => build_default_value(
field_label,
&quote! {Vec<u16>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeI32) => build_default_value(
field_label,
&quote! {Vec<i32>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeU32) => build_default_value(
field_label,
&quote! {Vec<u32>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeI64) => build_default_value(
field_label,
&quote! {Vec<i64>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeU64) => build_default_value(
field_label,
&quote! {Vec<u64>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeF32) => build_default_value(
field_label,
&quote! {Vec<f32>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeF64) => build_default_value(
field_label,
&quote! {Vec<f64>},
&quote! {vec![]},
&field_attrs.default,
),
Some(&FieldType::FieldTypeStruct { ref struct_name }) => build_default_value(
field_label,
&quote! {Vec<#struct_name>},
&quote! {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
.variants
.iter()
.map(|variant| {
let field_attrs = YaSerdeAttribute::parse(&variant.attrs);
let renamed_label = match field_attrs.rename {
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
});
.map(|variant| parse_variant(variant, name))
.filter_map(|f| f)
.collect();
quote! {
use xml::reader::XmlEvent;
use yaserde::Visitor;
#[allow(unknown_lints, unused_imports)]
use std::str::FromStr;
impl YaDeserialize for #name {
#[allow(unused_variables)]
@ -271,41 +37,42 @@ pub fn parse(
debug!("Enum: start to parse {:?}", named_element);
#[allow(unused_assignments, unused_mut)]
let mut simple_enum_value = None;
#variables
let mut enum_value = None;
loop {
match reader.peek()?.to_owned() {
XmlEvent::StartElement{name, attributes, namespace: _namespace} => {
debug!("Enum: {}: {}", named_element, name.local_name.as_str());
if name.local_name == named_element {
let _next = reader.next_event();
XmlEvent::StartElement{ref name, ref attributes, ..} => {
if let XmlEvent::Characters(content) = reader.peek()?.to_owned() {
match content.as_str() {
#match_to_enum
_ => {}
}
match name.local_name.as_str() {
#match_to_enum
named_element => {
let _root = reader.next_event();
}
}
},
XmlEvent::EndElement{name} => {
if name.local_name.as_str() == named_element {
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();
},
xml::reader::XmlEvent::Characters(characters_content) => {
}
XmlEvent::Characters(ref text_content) => {
let _root = reader.next_event();
},
}
event => {
return Err(format!("unknown event {:?}", event))
},
}
}
}
match simple_enum_value {
match enum_value {
Some(value) => Ok(value),
None => {
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,
&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), 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),
}
}