Merge pull request #174 from media-io/fix_same_field_name

fix: support attribute and field with same name
This commit is contained in:
Marc-Antoine ARNAUD 2024-02-03 08:16:14 +01:00 committed by GitHub
commit 82b5de86ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 55 additions and 33 deletions

View File

@ -345,6 +345,45 @@ fn de_attributes_complex() {
); );
} }
#[test]
fn de_attributes_with_same_name_field() {
init();
#[derive(Default, YaDeserialize, PartialEq, Debug)]
pub struct Struct {
#[yaserde(attribute, rename = "content")]
attribute: Option<String>,
content: Option<String>,
}
convert_and_validate!(
r#"<Struct />"#,
Struct,
Struct {
attribute: None,
content: None
}
);
convert_and_validate!(
r#"<Struct content="attribute" />"#,
Struct,
Struct {
attribute: Some("attribute".to_string()),
content: None
}
);
convert_and_validate!(
r#"<Struct content="attribute"><content>Field</content></Struct>"#,
Struct,
Struct {
attribute: Some("attribute".to_string()),
content: Some("Field".to_string())
}
);
}
#[test] #[test]
fn de_rename() { fn de_rename() {
init(); init();

View File

@ -196,7 +196,6 @@ fn flatten_attribute_and_child() {
struct Node { struct Node {
#[yaserde(flatten)] #[yaserde(flatten)]
base: Base, base: Base,
#[yaserde(child)]
value: StringValue, value: StringValue,
} }
@ -235,7 +234,6 @@ fn flatten_name_in_unknown_child() {
pub struct Node { pub struct Node {
#[yaserde(flatten)] #[yaserde(flatten)]
base: Base, base: Base,
#[yaserde(child)]
value: Value, value: Value,
} }

View File

@ -90,9 +90,15 @@ impl YaSerdeField {
}, },
); );
let attribute = self
.attributes
.attribute
.then_some("Attribute_".to_string())
.unwrap_or_default();
Ident::new( Ident::new(
&format!( &format!(
"__Visitor_{}_{}", "__Visitor_{attribute}{}_{}",
label.replace('.', "_").to_upper_camel_case(), label.replace('.', "_").to_upper_camel_case(),
struct_id struct_id
), ),

View File

@ -1,6 +1,7 @@
use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; use crate::{
use crate::de::build_default_value::build_default_value; common::{Field, YaSerdeAttribute, YaSerdeField},
use heck::ToUpperCamelCase; de::build_default_value::build_default_value,
};
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::quote; use quote::quote;
use syn::{DataStruct, Ident}; use syn::{DataStruct, Ident};
@ -142,7 +143,7 @@ pub fn parse(
.fields .fields
.iter() .iter()
.map(|field| YaSerdeField::new(field.clone())) .map(|field| YaSerdeField::new(field.clone()))
.filter(|field| !field.is_attribute() || !field.is_flatten()) .filter(|field| !field.is_attribute() && !field.is_flatten())
.filter_map(|field| { .filter_map(|field| {
let value_label = field.get_value_label(); let value_label = field.get_value_label();
let label_name = field.renamed_label_without_namespace(); let label_name = field.renamed_label_without_namespace();
@ -225,7 +226,7 @@ pub fn parse(
.filter_map(|field| { .filter_map(|field| {
let label = field.get_value_label(); let label = field.get_value_label();
let label_name = field.renamed_label_without_namespace(); let label_name = field.renamed_label_without_namespace();
let visitor_label = build_visitor_ident(&label_name, field.get_span(), None); let visitor_label = field.get_visitor_ident(None);
let visit = |action: &TokenStream, visitor: &Ident, visitor_label: &Ident| { let visit = |action: &TokenStream, visitor: &Ident, visitor_label: &Ident| {
Some(quote! { Some(quote! {
@ -267,7 +268,7 @@ pub fn parse(
visit( visit(
&action, &action,
&Ident::new("visit_str", Span::call_site()), &Ident::new("visit_str", Span::call_site()),
&build_visitor_ident(&label_name, field.get_span(), Some(&struct_name)), &field.get_visitor_ident(Some(&struct_name)),
) )
}; };
@ -294,7 +295,7 @@ pub fn parse(
Field::FieldStruct { struct_name } => visit_vec( Field::FieldStruct { struct_name } => visit_vec(
&quote! { .push(value) }, &quote! { .push(value) },
&Ident::new("visit_str", field.get_span()), &Ident::new("visit_str", field.get_span()),
&build_visitor_ident(&label_name, field.get_span(), Some(struct_name)), &field.get_visitor_ident(Some(struct_name)),
), ),
Field::FieldOption { .. } | Field::FieldVec { .. } => unimplemented!("Not supported"), Field::FieldOption { .. } | Field::FieldVec { .. } => unimplemented!("Not supported"),
simple_type => visit_vec( simple_type => visit_vec(
@ -463,7 +464,7 @@ fn build_call_visitor(
) -> Option<TokenStream> { ) -> Option<TokenStream> {
let value_label = field.get_value_label(); let value_label = field.get_value_label();
let label_name = field.renamed_label_without_namespace(); let label_name = field.renamed_label_without_namespace();
let visitor_label = build_visitor_ident(&label_name, field.get_span(), None); let visitor_label = field.get_visitor_ident(None);
let namespaces_matching = field.get_namespace_matching( let namespaces_matching = field.get_namespace_matching(
root_attributes, root_attributes,
@ -494,28 +495,6 @@ fn build_call_visitor(
}) })
} }
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('.', "_").to_upper_camel_case(),
struct_id
),
span,
)
}
fn build_code_for_unused_xml_events( fn build_code_for_unused_xml_events(
call_flatten_visitors: &TokenStream, call_flatten_visitors: &TokenStream,
) -> ( ) -> (