Merge pull request #38 from DmitrySamoylov/feature-attribute-option
Add support for Option<struct> in 'attribute' fields
This commit is contained in:
commit
df674965f8
@ -172,6 +172,53 @@ fn de_attributes() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn de_attributes_complex() {
|
||||
mod other_mod {
|
||||
use super::*;
|
||||
|
||||
#[derive(YaDeserialize, PartialEq, Debug)]
|
||||
pub enum AttrEnum {
|
||||
#[yaserde(rename = "variant 1")]
|
||||
Variant1,
|
||||
#[yaserde(rename = "variant 2")]
|
||||
Variant2,
|
||||
}
|
||||
|
||||
impl Default for AttrEnum {
|
||||
fn default() -> AttrEnum {
|
||||
AttrEnum::Variant1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, YaDeserialize, PartialEq, Debug)]
|
||||
pub struct Struct {
|
||||
#[yaserde(attribute)]
|
||||
attr_option_string: Option<std::string::String>,
|
||||
#[yaserde(attribute)]
|
||||
attr_option_enum: Option<other_mod::AttrEnum>,
|
||||
}
|
||||
|
||||
convert_and_validate!(
|
||||
r#"<Struct />"#,
|
||||
Struct,
|
||||
Struct {
|
||||
attr_option_string: None,
|
||||
attr_option_enum: None
|
||||
}
|
||||
);
|
||||
|
||||
convert_and_validate!(
|
||||
r#"<Struct attr_option_string="some value" attr_option_enum="variant 2" />"#,
|
||||
Struct,
|
||||
Struct {
|
||||
attr_option_string: Some("some value".to_string()),
|
||||
attr_option_enum: Some(other_mod::AttrEnum::Variant2)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn de_rename() {
|
||||
#[derive(YaDeserialize, PartialEq, Debug)]
|
||||
|
||||
@ -124,6 +124,66 @@ fn se_attributes() {
|
||||
convert_and_validate!(model, content);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn se_attributes_complex() {
|
||||
mod other_mod {
|
||||
use super::*;
|
||||
|
||||
#[derive(YaSerialize, PartialEq, Debug)]
|
||||
pub enum AttrEnum {
|
||||
#[yaserde(rename = "variant 1")]
|
||||
Variant1,
|
||||
#[yaserde(rename = "variant 2")]
|
||||
Variant2,
|
||||
}
|
||||
|
||||
impl Default for AttrEnum {
|
||||
fn default() -> AttrEnum {
|
||||
AttrEnum::Variant1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(YaSerialize, PartialEq, Debug)]
|
||||
pub struct Struct {
|
||||
#[yaserde(attribute)]
|
||||
attr_option_string: Option<std::string::String>,
|
||||
#[yaserde(attribute)]
|
||||
attr_option_enum: Option<other_mod::AttrEnum>,
|
||||
}
|
||||
|
||||
impl Default for Struct {
|
||||
fn default() -> Struct {
|
||||
Struct {
|
||||
attr_option_string: None,
|
||||
attr_option_enum: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
convert_and_validate!(
|
||||
Struct {
|
||||
attr_option_string: None,
|
||||
attr_option_enum: None,
|
||||
},
|
||||
r#"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Struct />
|
||||
"#
|
||||
);
|
||||
|
||||
convert_and_validate!(
|
||||
Struct {
|
||||
attr_option_string: Some("some value".to_string()),
|
||||
attr_option_enum: Some(other_mod::AttrEnum::Variant2),
|
||||
},
|
||||
r#"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Struct attr_option_string="some value" attr_option_enum="variant 2" />
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_rename() {
|
||||
#[derive(YaSerialize, PartialEq, Debug)]
|
||||
|
||||
@ -103,76 +103,51 @@ pub fn parse(
|
||||
|
||||
let visitor_label = build_visitor_ident(&label_name, field.span(), None);
|
||||
|
||||
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();
|
||||
let struct_ident = build_visitor_ident(&label_name, field.span(), Some(&struct_id));
|
||||
let struct_visitor = |struct_name: syn::Path| {
|
||||
let struct_id: String = struct_name
|
||||
.segments
|
||||
.iter()
|
||||
.map(|s| s.ident.to_string())
|
||||
.collect();
|
||||
|
||||
Some(quote! {
|
||||
#[allow(non_snake_case, non_camel_case_types)]
|
||||
struct #struct_ident;
|
||||
impl<'de> Visitor<'de> for #struct_ident {
|
||||
type Value = #struct_name;
|
||||
let struct_ident = build_visitor_ident(&label_name, field.span(), Some(&struct_name));
|
||||
|
||||
fn visit_str(self, v: &str) -> Result<Self::Value, String> {
|
||||
let content = "<".to_string() + #struct_id + ">" + v + "</" + #struct_id + ">";
|
||||
let value : Result<#struct_name, String> = yaserde::de::from_str(&content);
|
||||
value
|
||||
}
|
||||
Some(quote! {
|
||||
#[allow(non_snake_case, non_camel_case_types)]
|
||||
struct #struct_ident;
|
||||
impl<'de> Visitor<'de> for #struct_ident {
|
||||
type Value = #struct_name;
|
||||
|
||||
fn visit_str(self, v: &str) -> Result<Self::Value, String> {
|
||||
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 } => match *data_type {
|
||||
FieldType::FieldTypeStruct { ref struct_name } => {
|
||||
let struct_ident = Ident::new(
|
||||
&format!("{}", struct_name.into_token_stream()),
|
||||
field.span(),
|
||||
);
|
||||
Some(quote! {
|
||||
#[allow(non_snake_case, non_camel_case_types)]
|
||||
struct #visitor_label;
|
||||
impl<'de> Visitor<'de> for #visitor_label {
|
||||
type Value = #struct_ident;
|
||||
}
|
||||
})
|
||||
}
|
||||
FieldType::FieldTypeOption { .. } | FieldType::FieldTypeVec { .. } => None,
|
||||
simple_type => build_declare_visitor(
|
||||
&get_simple_type_token(&simple_type),
|
||||
&get_simple_type_visitor(&simple_type),
|
||||
&visitor_label,
|
||||
),
|
||||
},
|
||||
FieldType::FieldTypeVec { data_type } => match *data_type {
|
||||
FieldType::FieldTypeStruct { ref struct_name } => {
|
||||
let struct_ident = Ident::new(
|
||||
&format!("{}", struct_name.into_token_stream()),
|
||||
field.span(),
|
||||
);
|
||||
Some(quote! {
|
||||
#[allow(non_snake_case, non_camel_case_types)]
|
||||
struct #visitor_label;
|
||||
impl<'de> Visitor<'de> for #visitor_label {
|
||||
type Value = #struct_ident;
|
||||
}
|
||||
})
|
||||
}
|
||||
FieldType::FieldTypeOption { .. } | FieldType::FieldTypeVec { .. } => None,
|
||||
simple_type => build_declare_visitor(
|
||||
&get_simple_type_token(&simple_type),
|
||||
&get_simple_type_visitor(&simple_type),
|
||||
&visitor_label,
|
||||
),
|
||||
},
|
||||
simple_type => build_declare_visitor(
|
||||
})
|
||||
};
|
||||
|
||||
let simple_type_visitor = |simple_type: FieldType| {
|
||||
build_declare_visitor(
|
||||
&get_simple_type_token(&simple_type),
|
||||
&get_simple_type_visitor(&simple_type),
|
||||
&visitor_label,
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
get_field_type(field).and_then(|f| match f {
|
||||
FieldType::FieldTypeStruct { struct_name } => struct_visitor(struct_name),
|
||||
FieldType::FieldTypeOption { data_type } => match *data_type {
|
||||
FieldType::FieldTypeStruct { struct_name } => struct_visitor(struct_name),
|
||||
FieldType::FieldTypeOption { .. } | FieldType::FieldTypeVec { .. } => None,
|
||||
simple_type => simple_type_visitor(simple_type),
|
||||
},
|
||||
FieldType::FieldTypeVec { data_type } => match *data_type {
|
||||
FieldType::FieldTypeStruct { struct_name } => struct_visitor(struct_name),
|
||||
FieldType::FieldTypeOption { .. } | FieldType::FieldTypeVec { .. } => None,
|
||||
simple_type => simple_type_visitor(simple_type),
|
||||
},
|
||||
simple_type => simple_type_visitor(simple_type),
|
||||
})
|
||||
})
|
||||
.filter_map(|x| x)
|
||||
@ -341,9 +316,14 @@ pub fn parse(
|
||||
}
|
||||
}),
|
||||
FieldType::FieldTypeOption { data_type } => match *data_type {
|
||||
FieldType::FieldTypeStruct { .. }
|
||||
| FieldType::FieldTypeOption { .. }
|
||||
| FieldType::FieldTypeVec { .. } => None,
|
||||
FieldType::FieldTypeOption { .. } | FieldType::FieldTypeVec { .. } => unimplemented!(),
|
||||
FieldType::FieldTypeStruct { struct_name } => build_call_visitor_for_attribute(
|
||||
label,
|
||||
&label_name,
|
||||
"e! {= Some(value) },
|
||||
"e! {visit_str},
|
||||
&build_visitor_ident(&label_name, field.span(), Some(&struct_name)),
|
||||
),
|
||||
simple_type => {
|
||||
let visitor = get_simple_type_visitor(&simple_type);
|
||||
|
||||
@ -356,28 +336,13 @@ pub fn parse(
|
||||
)
|
||||
}
|
||||
},
|
||||
FieldType::FieldTypeStruct { struct_name } => {
|
||||
let struct_ident = Ident::new(
|
||||
&format!(
|
||||
"__Visitor_{}_{}",
|
||||
label_name,
|
||||
struct_name.into_token_stream()
|
||||
),
|
||||
field.span(),
|
||||
);
|
||||
|
||||
Some(quote! {
|
||||
for attr in attributes {
|
||||
if attr.name.local_name == #label_name {
|
||||
let visitor = #struct_ident{};
|
||||
match visitor.visit_str(&attr.value) {
|
||||
Ok(value) => {#label = value;}
|
||||
Err(msg) => {return Err(msg);}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
FieldType::FieldTypeStruct { struct_name } => build_call_visitor_for_attribute(
|
||||
label,
|
||||
&label_name,
|
||||
"e! {= value },
|
||||
"e! {visit_str},
|
||||
&build_visitor_ident(&label_name, field.span(), Some(&struct_name)),
|
||||
),
|
||||
FieldType::FieldTypeVec { .. } => None,
|
||||
simple_type => {
|
||||
let visitor = get_simple_type_visitor(&simple_type);
|
||||
@ -646,13 +611,20 @@ fn get_value_label(ident: &Option<syn::Ident>) -> Option<syn::Ident> {
|
||||
.map(|ident| syn::Ident::new(&format!("__{}_value", ident.to_string()), ident.span()))
|
||||
}
|
||||
|
||||
fn build_visitor_ident(label: &str, span: Span, struct_id: Option<&str>) -> Ident {
|
||||
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.unwrap_or("")
|
||||
),
|
||||
&format!("__Visitor_{}_{}", label.replace(".", "_"), struct_id),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
@ -37,10 +37,10 @@ impl FieldType {
|
||||
"f32" => Some(FieldType::FieldTypeF32),
|
||||
"f64" => Some(FieldType::FieldTypeF64),
|
||||
"Option" => get_sub_type(t).map(|data_type| FieldType::FieldTypeOption {
|
||||
data_type: Box::new(FieldType::from_ident(&syn::Path::from(data_type)).unwrap()),
|
||||
data_type: Box::new(FieldType::from_ident(&data_type).unwrap()),
|
||||
}),
|
||||
"Vec" => get_sub_type(t).map(|data_type| FieldType::FieldTypeVec {
|
||||
data_type: Box::new(FieldType::from_ident(&syn::Path::from(data_type)).unwrap()),
|
||||
data_type: Box::new(FieldType::from_ident(&data_type).unwrap()),
|
||||
}),
|
||||
_ => Some(FieldType::FieldTypeStruct {
|
||||
struct_name: path.clone(),
|
||||
@ -58,14 +58,12 @@ pub fn get_field_type(field: &syn::Field) -> Option<FieldType> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_sub_type(t: &syn::PathSegment) -> Option<syn::PathSegment> {
|
||||
fn get_sub_type(t: &syn::PathSegment) -> Option<syn::Path> {
|
||||
if let syn::PathArguments::AngleBracketed(ref args) = t.arguments {
|
||||
if let Some(tt) = args.args.first() {
|
||||
if let syn::GenericArgument::Type(ref argument) = *tt {
|
||||
if let Path(ref path2) = *argument {
|
||||
if let Some(ttt) = path2.path.segments.first() {
|
||||
return Some(ttt.clone());
|
||||
}
|
||||
if let Path(ref path) = *argument {
|
||||
return Some(path.path.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,6 +176,54 @@ pub fn serialize(
|
||||
})
|
||||
}
|
||||
}
|
||||
FieldType::FieldTypeStruct { .. } => {
|
||||
if let Some(ref d) = field_attrs.default {
|
||||
let default_function = Ident::new(&d, field.span());
|
||||
Some(quote! {
|
||||
let struct_start_event = if let Some(ref value) = self.#label {
|
||||
if *value != #default_function() {
|
||||
struct_start_event.attr(#label_name, &*{
|
||||
use std::mem;
|
||||
match yaserde::ser::to_string_content(value) {
|
||||
Ok(value) => {
|
||||
unsafe {
|
||||
let ret : &'static str = mem::transmute(&value as &str);
|
||||
mem::forget(value);
|
||||
ret
|
||||
}
|
||||
},
|
||||
Err(msg) => return Err("Unable to serialize content".to_owned()),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
struct_start_event
|
||||
}
|
||||
} else {
|
||||
struct_start_event
|
||||
};
|
||||
})
|
||||
} else {
|
||||
Some(quote! {
|
||||
let struct_start_event = if let Some(ref value) = self.#label {
|
||||
struct_start_event.attr(#label_name, &*{
|
||||
use std::mem;
|
||||
match yaserde::ser::to_string_content(value) {
|
||||
Ok(value) => {
|
||||
unsafe {
|
||||
let ret : &'static str = mem::transmute(&value as &str);
|
||||
mem::forget(value);
|
||||
ret
|
||||
}
|
||||
},
|
||||
Err(msg) => return Err("Unable to serialize content".to_owned()),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
struct_start_event
|
||||
};
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
FieldType::FieldTypeStruct { .. } => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user