Implement flatten (de)
This commit is contained in:
parent
6cf86c4e8e
commit
cc7cf76a45
@ -647,3 +647,89 @@ fn de_custom() {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn de_flatten() {
|
||||
#[derive(Default, PartialEq, Debug, YaDeserialize)]
|
||||
struct DateTime {
|
||||
#[yaserde(flatten)]
|
||||
date: Date,
|
||||
time: String,
|
||||
#[yaserde(flatten)]
|
||||
kind: DateKind,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaDeserialize)]
|
||||
struct Date {
|
||||
year: i32,
|
||||
month: i32,
|
||||
day: i32,
|
||||
#[yaserde(flatten)]
|
||||
extra: Extra,
|
||||
#[yaserde(flatten)]
|
||||
optional_extra: Option<OptionalExtra>,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaDeserialize)]
|
||||
pub struct Extra {
|
||||
week: i32,
|
||||
century: i32,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaDeserialize)]
|
||||
pub struct OptionalExtra {
|
||||
lunar_day: i32,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, YaDeserialize)]
|
||||
pub enum DateKind {
|
||||
#[yaserde(rename = "holidays")]
|
||||
Holidays(Vec<String>),
|
||||
#[yaserde(rename = "working")]
|
||||
Working,
|
||||
}
|
||||
|
||||
impl Default for DateKind {
|
||||
fn default() -> Self {
|
||||
DateKind::Working
|
||||
}
|
||||
};
|
||||
|
||||
let content = r#"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<DateTime>
|
||||
<year>2020</year>
|
||||
<month>1</month>
|
||||
<day>1</day>
|
||||
<week>1</week>
|
||||
<century>21</century>
|
||||
<lunar_day>1</lunar_day>
|
||||
<time>10:40:03</time>
|
||||
<holidays>New Year's Day</holidays>
|
||||
<holidays>Novy God Day</holidays>
|
||||
<holidays>Polar Bear Swim Day</holidays>
|
||||
</DateTime>
|
||||
"#;
|
||||
convert_and_validate!(
|
||||
content,
|
||||
DateTime,
|
||||
DateTime {
|
||||
date: Date {
|
||||
year: 2020,
|
||||
month: 1,
|
||||
day: 1,
|
||||
extra: Extra {
|
||||
week: 1,
|
||||
century: 21,
|
||||
},
|
||||
optional_extra: Some(OptionalExtra { lunar_day: 1 }),
|
||||
},
|
||||
time: "10:40:03".to_string(),
|
||||
kind: DateKind::Holidays(vec![
|
||||
"New Year's Day".into(),
|
||||
"Novy God Day".into(),
|
||||
"Polar Bear Swim Day".into()
|
||||
])
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ pub struct YaSerdeAttribute {
|
||||
pub namespaces: BTreeMap<String, String>,
|
||||
pub attribute: bool,
|
||||
pub text: bool,
|
||||
pub flatten: bool,
|
||||
}
|
||||
|
||||
fn get_value(iter: &mut IntoIter) -> Option<String> {
|
||||
@ -38,6 +39,7 @@ impl YaSerdeAttribute {
|
||||
let mut root = None;
|
||||
let mut default = None;
|
||||
let mut text = false;
|
||||
let mut flatten = false;
|
||||
|
||||
for attr in attrs.iter() {
|
||||
let mut attr_iter = attr.clone().tokens.into_iter();
|
||||
@ -78,6 +80,9 @@ impl YaSerdeAttribute {
|
||||
"text" => {
|
||||
text = true;
|
||||
}
|
||||
"flatten" => {
|
||||
flatten = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -95,6 +100,7 @@ impl YaSerdeAttribute {
|
||||
root,
|
||||
default,
|
||||
text,
|
||||
flatten,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,6 +119,7 @@ fn parse_empty_attributes() {
|
||||
namespaces: BTreeMap::new(),
|
||||
attribute: false,
|
||||
text: false,
|
||||
flatten: false,
|
||||
},
|
||||
attrs
|
||||
);
|
||||
@ -160,6 +167,7 @@ fn parse_attributes() {
|
||||
namespaces: BTreeMap::new(),
|
||||
attribute: true,
|
||||
text: false,
|
||||
flatten: false,
|
||||
},
|
||||
attrs
|
||||
);
|
||||
|
||||
@ -186,7 +186,7 @@ pub fn parse(
|
||||
let label = &field.ident;
|
||||
let value_label = &get_value_label(&field.ident);
|
||||
|
||||
if field_attrs.attribute {
|
||||
if field_attrs.attribute || field_attrs.flatten {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -288,6 +288,33 @@ pub fn parse(
|
||||
.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;
|
||||
}
|
||||
|
||||
get_field_type(field).and_then(|f| match f {
|
||||
FieldType::FieldTypeStruct { .. } => Some(quote! {
|
||||
#value_label = yaserde::de::from_str(&unused_xml_elements)?;
|
||||
}),
|
||||
FieldType::FieldTypeOption { data_type } => match *data_type {
|
||||
FieldType::FieldTypeStruct { .. } => 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()
|
||||
@ -410,8 +437,15 @@ pub fn parse(
|
||||
.filter_map(|x| x)
|
||||
.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)
|
||||
};
|
||||
|
||||
quote! {
|
||||
use xml::reader::XmlEvent;
|
||||
use xml::reader::{XmlEvent, EventReader};
|
||||
use xml::writer::EventWriter;
|
||||
use yaserde::Visitor;
|
||||
#[allow(unknown_lints, unused_imports)]
|
||||
use std::str::FromStr;
|
||||
@ -439,15 +473,19 @@ pub fn parse(
|
||||
|
||||
#variables
|
||||
#field_visitors
|
||||
#init_unused
|
||||
|
||||
loop {
|
||||
match reader.peek()?.to_owned() {
|
||||
let event = reader.peek()?.to_owned();
|
||||
|
||||
match event {
|
||||
XmlEvent::StartElement{ref name, ref attributes, ..} => {
|
||||
|
||||
match name.local_name.as_str() {
|
||||
#call_visitors
|
||||
named_element => {
|
||||
let _root = reader.next_event();
|
||||
let event = reader.next_event()?;
|
||||
#write_unused
|
||||
}
|
||||
// name => {
|
||||
// return Err(format!("unknown key {}", name))
|
||||
@ -457,13 +495,16 @@ pub fn parse(
|
||||
}
|
||||
XmlEvent::EndElement{ref name} => {
|
||||
if name.local_name == named_element {
|
||||
#write_unused
|
||||
break;
|
||||
}
|
||||
let _root = reader.next_event();
|
||||
let event = reader.next_event()?;
|
||||
#write_unused
|
||||
}
|
||||
XmlEvent::Characters(ref text_content) => {
|
||||
#set_text
|
||||
let _root = reader.next_event();
|
||||
let event = reader.next_event()?;
|
||||
#write_unused
|
||||
}
|
||||
event => {
|
||||
return Err(format!("unknown event {:?}", event))
|
||||
@ -471,6 +512,8 @@ pub fn parse(
|
||||
}
|
||||
}
|
||||
|
||||
#visit_unused
|
||||
|
||||
Ok(#name{#struct_builder})
|
||||
}
|
||||
}
|
||||
@ -613,3 +656,31 @@ fn build_visitor_ident(label: &str, span: Span, struct_id: Option<&str>) -> Iden
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_code_for_unused_xml_events(
|
||||
call_flatten_visitors: &TokenStream,
|
||||
) -> (
|
||||
Option<TokenStream>,
|
||||
Option<TokenStream>,
|
||||
Option<TokenStream>,
|
||||
) {
|
||||
(
|
||||
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
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user