Switch from wrapped enums to forward enums

This commit is contained in:
Dirkjan Ochtman 2022-11-29 16:49:50 +01:00
parent 0a323ba302
commit 4ad194fc1f
7 changed files with 32 additions and 45 deletions

View File

@ -21,7 +21,7 @@ pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream {
syn::Fields::Unit => deserialize_unit_struct(input, &meta), syn::Fields::Unit => deserialize_unit_struct(input, &meta),
}, },
(syn::Data::Enum(data), Some(Mode::Scalar)) => deserialize_scalar_enum(input, data, meta), (syn::Data::Enum(data), Some(Mode::Scalar)) => deserialize_scalar_enum(input, data, meta),
(syn::Data::Enum(data), Some(Mode::Wrapped)) => deserialize_wrapped_enum(input, data, meta), (syn::Data::Enum(data), Some(Mode::Forward)) => deserialize_forward_enum(input, data, meta),
(syn::Data::Struct(_), _) => { (syn::Data::Struct(_), _) => {
syn::Error::new(input.span(), "no enum mode allowed on struct type").to_compile_error() syn::Error::new(input.span(), "no enum mode allowed on struct type").to_compile_error()
} }
@ -89,7 +89,7 @@ fn deserialize_scalar_enum(
) )
} }
fn deserialize_wrapped_enum( fn deserialize_forward_enum(
input: &syn::DeriveInput, input: &syn::DeriveInput,
data: &syn::DataEnum, data: &syn::DataEnum,
meta: ContainerMeta, meta: ContainerMeta,
@ -99,6 +99,7 @@ fn deserialize_wrapped_enum(
} }
let ident = &input.ident; let ident = &input.ident;
let mut matches = TokenStream::new();
let mut variants = TokenStream::new(); let mut variants = TokenStream::new();
let mut borrowed = BTreeSet::new(); let mut borrowed = BTreeSet::new();
for variant in data.variants.iter() { for variant in data.variants.iter() {
@ -126,6 +127,11 @@ fn deserialize_wrapped_enum(
let mut no_lifetime_type = field.ty.clone(); let mut no_lifetime_type = field.ty.clone();
discard_lifetimes(&mut no_lifetime_type, &mut borrowed, false, true); discard_lifetimes(&mut no_lifetime_type, &mut borrowed, false, true);
if !matches.is_empty() {
matches.extend(quote!(||));
}
matches.extend(quote!(#no_lifetime_type::matches(id, field)));
if !variants.is_empty() { if !variants.is_empty() {
variants.extend(quote!(else)); variants.extend(quote!(else));
} }
@ -133,16 +139,13 @@ fn deserialize_wrapped_enum(
let v_ident = &variant.ident; let v_ident = &variant.ident;
variants.extend( variants.extend(
quote!(if <#no_lifetime_type as FromXml>::matches(id, None) { quote!(if <#no_lifetime_type as FromXml>::matches(id, None) {
let mut nested = deserializer.nested(data);
let mut value = None; let mut value = None;
#no_lifetime_type::deserialize(&mut nested, &mut value)?; #no_lifetime_type::deserialize(deserializer, &mut value)?;
*into = value.map(#ident::#v_ident); *into = value.map(#ident::#v_ident);
}), }),
); );
} }
let name = meta.tag();
let default_ns = meta.default_namespace();
let generics = meta.xml_generics(borrowed); let generics = meta.xml_generics(borrowed);
let (xml_impl_generics, _, _) = generics.split_for_impl(); let (xml_impl_generics, _, _) = generics.split_for_impl();
let (_, ty_generics, where_clause) = input.generics.split_for_impl(); let (_, ty_generics, where_clause) = input.generics.split_for_impl();
@ -150,7 +153,7 @@ fn deserialize_wrapped_enum(
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause { impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
#[inline] #[inline]
fn matches(id: ::instant_xml::Id<'_>, field: Option<::instant_xml::Id<'_>>) -> bool { fn matches(id: ::instant_xml::Id<'_>, field: Option<::instant_xml::Id<'_>>) -> bool {
id == ::instant_xml::Id { name: #name, ns: #default_ns } #matches
} }
fn deserialize<'cx>( fn deserialize<'cx>(
@ -160,19 +163,9 @@ fn deserialize_wrapped_enum(
use ::instant_xml::de::Node; use ::instant_xml::de::Node;
use ::instant_xml::Error; use ::instant_xml::Error;
let node = match deserializer.next() { let id = deserializer.parent();
Some(result) => result?,
None => return Err(Error::MissingValue(Self::KIND)),
};
let data = match node {
Node::Open(data) => data,
_ => return Err(Error::UnexpectedState("unexpected node type for wrapped enum variant")),
};
let id = deserializer.element_id(&data)?;
#variants else { #variants else {
return Err(Error::UnexpectedTag); return Err(Error::UnexpectedTag(format!("{:?}", id)));
}; };
if let Some(_) = deserializer.next() { if let Some(_) = deserializer.next() {

View File

@ -300,8 +300,8 @@ fn discard_path_lifetimes(
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Mode { enum Mode {
Forward,
Scalar, Scalar,
Wrapped,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -304,12 +304,12 @@ pub(crate) fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> {
MetaState::Rename MetaState::Rename
} else if id == "rename_all" { } else if id == "rename_all" {
MetaState::RenameAll MetaState::RenameAll
} else if id == "forward" {
items.push((MetaItem::Mode(Mode::Forward), span));
MetaState::Comma
} else if id == "scalar" { } else if id == "scalar" {
items.push((MetaItem::Mode(Mode::Scalar), span)); items.push((MetaItem::Mode(Mode::Scalar), span));
MetaState::Comma MetaState::Comma
} else if id == "wrapped" {
items.push((MetaItem::Mode(Mode::Wrapped), span));
MetaState::Comma
} else if id == "serialize_with" { } else if id == "serialize_with" {
MetaState::SerializeWith MetaState::SerializeWith
} else if id == "deserialize_with" { } else if id == "deserialize_with" {

View File

@ -16,7 +16,7 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
match (&input.data, meta.mode) { match (&input.data, meta.mode) {
(syn::Data::Struct(data), None) => serialize_struct(input, data, meta), (syn::Data::Struct(data), None) => serialize_struct(input, data, meta),
(syn::Data::Enum(data), Some(Mode::Scalar)) => serialize_scalar_enum(input, data, meta), (syn::Data::Enum(data), Some(Mode::Scalar)) => serialize_scalar_enum(input, data, meta),
(syn::Data::Enum(data), Some(Mode::Wrapped)) => serialize_wrapped_enum(input, data, meta), (syn::Data::Enum(data), Some(Mode::Forward)) => serialize_forward_enum(input, data, meta),
(syn::Data::Struct(_), _) => { (syn::Data::Struct(_), _) => {
syn::Error::new(input.span(), "enum mode not allowed on struct type").to_compile_error() syn::Error::new(input.span(), "enum mode not allowed on struct type").to_compile_error()
} }
@ -74,7 +74,7 @@ fn serialize_scalar_enum(
) )
} }
fn serialize_wrapped_enum( fn serialize_forward_enum(
input: &syn::DeriveInput, input: &syn::DeriveInput,
data: &syn::DataEnum, data: &syn::DataEnum,
meta: ContainerMeta, meta: ContainerMeta,
@ -134,7 +134,6 @@ fn serialize_wrapped_enum(
} }
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let tag = meta.tag();
quote!( quote!(
impl #impl_generics ToXml for #ident #ty_generics #where_clause { impl #impl_generics ToXml for #ident #ty_generics #where_clause {
fn serialize<W: ::core::fmt::Write + ?::core::marker::Sized>( fn serialize<W: ::core::fmt::Write + ?::core::marker::Sized>(
@ -142,25 +141,10 @@ fn serialize_wrapped_enum(
field: Option<::instant_xml::Id<'_>>, field: Option<::instant_xml::Id<'_>>,
serializer: &mut instant_xml::Serializer<W>, serializer: &mut instant_xml::Serializer<W>,
) -> Result<(), instant_xml::Error> { ) -> Result<(), instant_xml::Error> {
// Start tag
let prefix = serializer.write_start(#tag, #default_namespace)?;
debug_assert_eq!(prefix, None);
// Set up element context, this will also emit namespace declarations
#context
let old = serializer.push(new)?;
// Finalize start element
serializer.end_start()?;
match self { match self {
#variants #variants
} }
// Close tag
serializer.write_close(prefix, #tag)?;
serializer.pop(old);
Ok(()) Ok(())
} }
}; };

View File

@ -75,6 +75,16 @@ impl<'cx, 'xml> Deserializer<'cx, 'xml> {
} }
} }
pub fn parent(&self) -> Id<'xml> {
Id {
ns: match self.prefix {
Some(ns) => self.context.lookup(ns).unwrap(),
None => self.context.default_ns(),
},
name: self.local,
}
}
#[inline] #[inline]
pub fn element_id(&self, element: &Element<'xml>) -> Result<Id<'xml>, Error> { pub fn element_id(&self, element: &Element<'xml>) -> Result<Id<'xml>, Error> {
self.context.element_id(element) self.context.element_id(element)

View File

@ -93,8 +93,8 @@ pub enum Error {
UnexpectedEndOfStream, UnexpectedEndOfStream,
#[error("unexpected value")] #[error("unexpected value")]
UnexpectedValue(&'static str), UnexpectedValue(&'static str),
#[error("unexpected tag")] #[error("unexpected tag: {0}")]
UnexpectedTag, UnexpectedTag(String),
#[error("missing tag")] #[error("missing tag")]
MissingTag, MissingTag,
#[error("missing value")] #[error("missing value")]

View File

@ -3,7 +3,7 @@ use similar_asserts::assert_eq;
use instant_xml::{from_str, to_string, FromXml, ToXml}; use instant_xml::{from_str, to_string, FromXml, ToXml};
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)] #[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
#[xml(wrapped)] #[xml(forward)]
enum Foo { enum Foo {
Bar(Bar), Bar(Bar),
Baz(Baz), Baz(Baz),
@ -22,7 +22,7 @@ struct Baz {
#[test] #[test]
fn wrapped_enum() { fn wrapped_enum() {
let v = Foo::Bar(Bar { bar: 42 }); let v = Foo::Bar(Bar { bar: 42 });
let xml = r#"<Foo><Bar><bar>42</bar></Bar></Foo>"#; let xml = r#"<Bar><bar>42</bar></Bar>"#;
assert_eq!(xml, to_string(&v).unwrap()); assert_eq!(xml, to_string(&v).unwrap());
assert_eq!(v, from_str(xml).unwrap()); assert_eq!(v, from_str(xml).unwrap());
} }