Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for internally tagged enum #182

Merged
merged 2 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions yaserde/tests/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,41 @@ fn unnamed_enum() {
serialize_and_validate!(model, content);
deserialize_and_validate!(content, model, XmlStruct);
}

#[test]
fn tagged_enum() {
#[derive(Debug, PartialEq, YaSerialize, YaDeserialize, Default)]
#[yaserde(tag = "type")]
#[yaserde(rename = "foobar")]
enum XmlEnum {
#[default]
#[yaserde(rename = "foo")]
Foo,
#[yaserde(rename = "bar")]
Bar,
}

#[derive(Debug, PartialEq, YaSerialize, YaDeserialize, Default)]
#[yaserde(rename = "base")]
struct XmlStruct {
#[yaserde(rename = "foobar")]
foo_bar: XmlEnum,
}

let model = XmlEnum::Foo;
let content = "<foobar type=\"foo\" />";
serialize_and_validate!(model, content);
deserialize_and_validate!(content, model, XmlEnum);

let model = XmlEnum::Bar;
let content = "<foobar type=\"bar\" />";
serialize_and_validate!(model, content);
deserialize_and_validate!(content, model, XmlEnum);

let model = XmlStruct {
foo_bar: XmlEnum::Foo,
};
let content = "<base><foobar type=\"foo\" /></base>";
MarcAntoine-Arnaud marked this conversation as resolved.
Show resolved Hide resolved
serialize_and_validate!(model, content);
deserialize_and_validate!(content, model, XmlStruct);
}
10 changes: 10 additions & 0 deletions yaserde_derive/src/common/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct YaSerdeAttribute {
pub namespaces: BTreeMap<Option<String>, String>,
pub prefix: Option<String>,
pub rename: Option<String>,
pub tag: Option<String>,
pub skip_serializing: bool,
pub skip_serializing_if: Option<String>,
pub text: bool,
Expand Down Expand Up @@ -40,6 +41,7 @@ impl YaSerdeAttribute {
let mut namespaces = BTreeMap::new();
let mut prefix = None;
let mut rename = None;
let mut tag = None;
let mut skip_serializing = false;
let mut skip_serializing_if = None;
let mut text = false;
Expand Down Expand Up @@ -82,6 +84,9 @@ impl YaSerdeAttribute {
"rename" => {
rename = get_value(&mut attr_iter);
}
"tag" => {
tag = get_value(&mut attr_iter);
}
"skip_serializing" => {
skip_serializing = true;
}
Expand All @@ -107,6 +112,7 @@ impl YaSerdeAttribute {
namespaces,
prefix,
rename,
tag,
skip_serializing,
skip_serializing_if,
text,
Expand Down Expand Up @@ -182,6 +188,7 @@ fn parse_empty_attributes() {
namespaces: BTreeMap::new(),
prefix: None,
rename: None,
tag: None,
skip_serializing: false,
skip_serializing_if: None,
text: false,
Expand Down Expand Up @@ -232,6 +239,7 @@ fn parse_attributes() {
namespaces: BTreeMap::new(),
prefix: None,
rename: None,
tag: None,
skip_serializing: false,
skip_serializing_if: None,
text: false,
Expand Down Expand Up @@ -282,6 +290,7 @@ fn only_parse_yaserde_attributes() {
namespaces: BTreeMap::new(),
prefix: None,
rename: None,
tag: None,
skip_serializing: false,
skip_serializing_if: None,
text: false,
Expand Down Expand Up @@ -338,6 +347,7 @@ fn parse_attributes_with_values() {
namespaces,
prefix: None,
rename: None,
tag: None,
skip_serializing: false,
skip_serializing_if: None,
text: false,
Expand Down
16 changes: 15 additions & 1 deletion yaserde_derive/src/de/expand_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ pub fn parse(

let flatten = root_attributes.flatten;

let element_name = if let Some(tag) = &root_attributes.tag {
quote! {
attributes
.iter()
.find(|attr| attr.name.local_name.as_str() == #tag)
.ok_or(format!("Expected enum tagged with {}, found {:?}", #tag, event))?
.value.as_str()
}
} else {
quote! {
name.local_name.as_str()
}
};

quote! {
impl ::yaserde::YaDeserialize for #name {
#[allow(unused_variables)]
Expand All @@ -50,7 +64,7 @@ pub fn parse(
::yaserde::__derive_trace!("Enum {} @ {}: matching {:?}", stringify!(#name), start_depth, event);
match event {
::yaserde::__xml::reader::XmlEvent::StartElement { ref name, ref attributes, .. } => {
match name.local_name.as_str() {
match #element_name {
#match_to_enum
_named_element => {
let _root = reader.next_event();
Expand Down
35 changes: 29 additions & 6 deletions yaserde_derive/src/ser/expand_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ pub fn serialize(
.map(|variant| -> TokenStream {
let _attrs = crate::common::YaSerdeAttribute::parse(&variant.attrs);

let add_tag = if let Some(tag) = &root_attributes.tag {
let attrs = crate::common::YaSerdeAttribute::parse(&variant.attrs);
let label = variant.ident.clone();
let element_name = attrs.xml_element_name(&variant.ident);
quote! {
match self {
#name::#label { .. } => {
let tag = ::yaserde::__xml::name::OwnedName::local(#tag);
child_attributes.push(::yaserde::__xml::attribute::OwnedAttribute::new(tag, #element_name));
}
_ => {}
}
}
} else { quote!() };

let all_fields = variant
.fields
.iter()
Expand All @@ -39,7 +54,7 @@ pub fn serialize(
})
.collect();

attribute_fields
let add_attributes : TokenStream = attribute_fields
.iter()
.map(|field| {
let label = variant.ident.clone();
Expand Down Expand Up @@ -79,7 +94,9 @@ pub fn serialize(
}
}
})
.collect()
.collect();

quote!( #add_attributes #add_tag)
})
.collect();

Expand Down Expand Up @@ -109,10 +126,16 @@ fn inner_enum_inspector(
let label_name = build_label_name(label, &variant_attrs, &root_attributes.default_namespace);

match variant.fields {
Fields::Unit => quote! {
&#name::#label => {
let data_event = ::yaserde::__xml::writer::XmlEvent::characters(#label_name);
writer.write(data_event).map_err(|e| e.to_string())?;
Fields::Unit => {
if let Some(_tag) = &root_attributes.tag {
quote! { #name::#label => {} }
} else {
quote! {
#name::#label => {
let data_event = ::yaserde::__xml::writer::XmlEvent::characters(#label_name);
writer.write(data_event).map_err(|e| e.to_string())?;
}
}
}
},
Fields::Named(ref fields) => {
Expand Down
Loading