From 4ad194fc1fcefcf4d34967e1136f01293970079e Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 29 Nov 2022 16:49:50 +0100 Subject: [PATCH] Switch from wrapped enums to forward enums --- instant-xml-macros/src/de.rs | 31 +++++++------------ instant-xml-macros/src/lib.rs | 2 +- instant-xml-macros/src/meta.rs | 6 ++-- instant-xml-macros/src/ser.rs | 20 ++---------- instant-xml/src/de.rs | 10 ++++++ instant-xml/src/lib.rs | 4 +-- .../{wrapped-enum.rs => forward-enum.rs} | 4 +-- 7 files changed, 32 insertions(+), 45 deletions(-) rename instant-xml/tests/{wrapped-enum.rs => forward-enum.rs} (87%) diff --git a/instant-xml-macros/src/de.rs b/instant-xml-macros/src/de.rs index ca3036a..ddca809 100644 --- a/instant-xml-macros/src/de.rs +++ b/instant-xml-macros/src/de.rs @@ -21,7 +21,7 @@ pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream { 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::Wrapped)) => deserialize_wrapped_enum(input, data, meta), + (syn::Data::Enum(data), Some(Mode::Forward)) => deserialize_forward_enum(input, data, meta), (syn::Data::Struct(_), _) => { 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, data: &syn::DataEnum, meta: ContainerMeta, @@ -99,6 +99,7 @@ fn deserialize_wrapped_enum( } let ident = &input.ident; + let mut matches = TokenStream::new(); let mut variants = TokenStream::new(); let mut borrowed = BTreeSet::new(); for variant in data.variants.iter() { @@ -126,6 +127,11 @@ fn deserialize_wrapped_enum( let mut no_lifetime_type = field.ty.clone(); 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() { variants.extend(quote!(else)); } @@ -133,16 +139,13 @@ fn deserialize_wrapped_enum( let v_ident = &variant.ident; variants.extend( quote!(if <#no_lifetime_type as FromXml>::matches(id, None) { - let mut nested = deserializer.nested(data); 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); }), ); } - let name = meta.tag(); - let default_ns = meta.default_namespace(); let generics = meta.xml_generics(borrowed); let (xml_impl_generics, _, _) = 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 { #[inline] 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>( @@ -160,19 +163,9 @@ fn deserialize_wrapped_enum( use ::instant_xml::de::Node; use ::instant_xml::Error; - let node = match deserializer.next() { - 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)?; + let id = deserializer.parent(); #variants else { - return Err(Error::UnexpectedTag); + return Err(Error::UnexpectedTag(format!("{:?}", id))); }; if let Some(_) = deserializer.next() { diff --git a/instant-xml-macros/src/lib.rs b/instant-xml-macros/src/lib.rs index 0986854..b614f49 100644 --- a/instant-xml-macros/src/lib.rs +++ b/instant-xml-macros/src/lib.rs @@ -300,8 +300,8 @@ fn discard_path_lifetimes( #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum Mode { + Forward, Scalar, - Wrapped, } #[cfg(test)] diff --git a/instant-xml-macros/src/meta.rs b/instant-xml-macros/src/meta.rs index c59c3e9..80d11eb 100644 --- a/instant-xml-macros/src/meta.rs +++ b/instant-xml-macros/src/meta.rs @@ -304,12 +304,12 @@ pub(crate) fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> { MetaState::Rename } else if id == "rename_all" { MetaState::RenameAll + } else if id == "forward" { + items.push((MetaItem::Mode(Mode::Forward), span)); + MetaState::Comma } else if id == "scalar" { items.push((MetaItem::Mode(Mode::Scalar), span)); MetaState::Comma - } else if id == "wrapped" { - items.push((MetaItem::Mode(Mode::Wrapped), span)); - MetaState::Comma } else if id == "serialize_with" { MetaState::SerializeWith } else if id == "deserialize_with" { diff --git a/instant-xml-macros/src/ser.rs b/instant-xml-macros/src/ser.rs index 95ed660..b036a18 100644 --- a/instant-xml-macros/src/ser.rs +++ b/instant-xml-macros/src/ser.rs @@ -16,7 +16,7 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream { match (&input.data, meta.mode) { (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::Wrapped)) => serialize_wrapped_enum(input, data, meta), + (syn::Data::Enum(data), Some(Mode::Forward)) => serialize_forward_enum(input, data, meta), (syn::Data::Struct(_), _) => { 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, data: &syn::DataEnum, meta: ContainerMeta, @@ -134,7 +134,6 @@ fn serialize_wrapped_enum( } let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let tag = meta.tag(); quote!( impl #impl_generics ToXml for #ident #ty_generics #where_clause { fn serialize( @@ -142,25 +141,10 @@ fn serialize_wrapped_enum( field: Option<::instant_xml::Id<'_>>, serializer: &mut instant_xml::Serializer, ) -> 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 { #variants } - // Close tag - serializer.write_close(prefix, #tag)?; - serializer.pop(old); - Ok(()) } }; diff --git a/instant-xml/src/de.rs b/instant-xml/src/de.rs index 9ec7617..ebe83ca 100644 --- a/instant-xml/src/de.rs +++ b/instant-xml/src/de.rs @@ -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] pub fn element_id(&self, element: &Element<'xml>) -> Result, Error> { self.context.element_id(element) diff --git a/instant-xml/src/lib.rs b/instant-xml/src/lib.rs index 6d25f66..e1ba43f 100644 --- a/instant-xml/src/lib.rs +++ b/instant-xml/src/lib.rs @@ -93,8 +93,8 @@ pub enum Error { UnexpectedEndOfStream, #[error("unexpected value")] UnexpectedValue(&'static str), - #[error("unexpected tag")] - UnexpectedTag, + #[error("unexpected tag: {0}")] + UnexpectedTag(String), #[error("missing tag")] MissingTag, #[error("missing value")] diff --git a/instant-xml/tests/wrapped-enum.rs b/instant-xml/tests/forward-enum.rs similarity index 87% rename from instant-xml/tests/wrapped-enum.rs rename to instant-xml/tests/forward-enum.rs index 0fbb39f..e6dcff7 100644 --- a/instant-xml/tests/wrapped-enum.rs +++ b/instant-xml/tests/forward-enum.rs @@ -3,7 +3,7 @@ use similar_asserts::assert_eq; use instant_xml::{from_str, to_string, FromXml, ToXml}; #[derive(Debug, Eq, FromXml, PartialEq, ToXml)] -#[xml(wrapped)] +#[xml(forward)] enum Foo { Bar(Bar), Baz(Baz), @@ -22,7 +22,7 @@ struct Baz { #[test] fn wrapped_enum() { let v = Foo::Bar(Bar { bar: 42 }); - let xml = r#"42"#; + let xml = r#"42"#; assert_eq!(xml, to_string(&v).unwrap()); assert_eq!(v, from_str(xml).unwrap()); }