diff --git a/instant-xml-macros/src/de.rs b/instant-xml-macros/src/de.rs index 4906f71..3f1f115 100644 --- a/instant-xml-macros/src/de.rs +++ b/instant-xml-macros/src/de.rs @@ -42,7 +42,7 @@ fn deserialize_scalar_enum( }; let serialize_as = meta.serialize_as; - variants.extend(quote!(Ok(#serialize_as) => Ok(#ident::#v_ident),)); + variants.extend(quote!(Ok(#serialize_as) => #ident::#v_ident,)); } let generics = meta.xml_generics(); @@ -52,12 +52,22 @@ fn deserialize_scalar_enum( quote!( impl #impl_generics FromXml<'xml> for #ident #ty_generics #where_clause { fn deserialize<'cx>( - deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml> - ) -> Result { - match deserializer.take_str() { - #variants - _ => Err(::instant_xml::Error::UnexpectedValue) + deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>, + into: &mut Option, + ) -> Result<(), ::instant_xml::Error> { + use ::instant_xml::Error; + + if into.is_some() { + return Err(Error::DuplicateValue); } + + let value = match deserializer.take_str() { + #variants + _ => return Err(Error::UnexpectedValue), + }; + + *into = Some(value); + Ok(()) } const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Scalar; @@ -106,14 +116,14 @@ fn deserialize_wrapped_enum( } let v_ident = &variant.ident; - variants.extend( - quote!(if <#no_lifetime_type as FromXml>::KIND.matches( - id, ::instant_xml::Id { ns: "", name: "" } - ) { - let mut nested = deserializer.nested(data); - #ident::#v_ident(#no_lifetime_type::deserialize(&mut nested)?) - }), - ); + variants.extend(quote!(if <#no_lifetime_type as FromXml>::KIND.matches( + id, ::instant_xml::Id { ns: "", name: "" } + ) { + let mut nested = deserializer.nested(data); + let mut value = None; + #no_lifetime_type::deserialize(&mut nested, &mut value)?; + *into = value.map(#ident::#v_ident); + })); } let name = meta.tag(); @@ -123,7 +133,10 @@ fn deserialize_wrapped_enum( let (_, ty_generics, where_clause) = input.generics.split_for_impl(); quote!( impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause { - fn deserialize<'cx>(deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>) -> Result { + fn deserialize<'cx>( + deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>, + into: &mut Option, + ) -> Result<(), ::instant_xml::Error> { use ::instant_xml::de::Node; use ::instant_xml::Error; @@ -138,7 +151,7 @@ fn deserialize_wrapped_enum( }; let id = deserializer.element_id(&data)?; - let value = #variants else { + #variants else { return Err(Error::UnexpectedTag); }; @@ -146,7 +159,7 @@ fn deserialize_wrapped_enum( return Err(Error::UnexpectedState("unexpected node after wrapped enum variant")); } - Ok(value) + Ok(()) } const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Element(::instant_xml::Id { @@ -236,10 +249,12 @@ fn deserialize_struct( quote!( impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause { - fn deserialize<'cx>(deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>) -> Result { + fn deserialize<'cx>( + deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>, + into: &mut Option, + ) -> Result<(), ::instant_xml::Error> { use ::instant_xml::de::Node; use ::instant_xml::{Error, Id}; - use ::core::marker::PhantomData; enum __Elements { #elements_enum @@ -284,7 +299,8 @@ fn deserialize_struct( } } - Ok(Self { #return_val }) + *into = Some(Self { #return_val }); + Ok(()) } const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Element(::instant_xml::Id { @@ -343,23 +359,15 @@ fn process_field( if !field_meta.attribute { tokens.r#match.extend(quote!( __Elements::#enum_name => { - if #enum_name.is_some() { - return Err(Error::DuplicateValue); - } - let mut nested = deserializer.nested(data); - #enum_name = Some(<#no_lifetime_type>::deserialize(&mut nested)?); + <#no_lifetime_type>::deserialize(&mut nested, &mut #enum_name)?; }, )); } else { tokens.r#match.extend(quote!( __Attributes::#enum_name => { - if #enum_name.is_some() { - return Err(Error::DuplicateValue); - } - let mut nested = deserializer.for_attr(attr); - #enum_name = Some(<#no_lifetime_type>::deserialize(&mut nested)?); + let new = <#no_lifetime_type>::deserialize(&mut nested, &mut #enum_name)?; }, )); } diff --git a/instant-xml-macros/src/ser.rs b/instant-xml-macros/src/ser.rs index 7f9567d..7c76578 100644 --- a/instant-xml-macros/src/ser.rs +++ b/instant-xml-macros/src/ser.rs @@ -319,10 +319,10 @@ fn process_named_field( discard_lifetimes(&mut no_lifetime_type); body.extend(quote!( match <#no_lifetime_type as ToXml>::KIND { - ::instant_xml::Kind::Element(_) => { + ::instant_xml::Kind::Element(_) | ::instant_xml::Kind::Vec(_) => { self.#field_name.serialize(serializer)?; } - ::instant_xml::Kind::Scalar | ::instant_xml::Kind::Vec => { + ::instant_xml::Kind::Scalar => { let prefix = serializer.write_start(#tag, #ns, true)?; serializer.end_start()?; self.#field_name.serialize(serializer)?; diff --git a/instant-xml/src/impls.rs b/instant-xml/src/impls.rs index 6aedae7..ec4db89 100644 --- a/instant-xml/src/impls.rs +++ b/instant-xml/src/impls.rs @@ -5,16 +5,26 @@ use std::str::FromStr; #[cfg(feature = "chrono")] use chrono::{DateTime, Utc}; -use crate::{de::Node, Deserializer, Error, FromXml, Kind, Serializer, ToXml}; +use crate::{Deserializer, Error, FromXml, Kind, Serializer, ToXml}; // Deserializer -struct FromXmlStr(Option); +struct FromXmlStr(T); impl<'xml, T: FromStr> FromXml<'xml> for FromXmlStr { - fn deserialize(deserializer: &mut Deserializer<'_, 'xml>) -> Result { + fn deserialize( + deserializer: &mut Deserializer<'_, 'xml>, + into: &mut Option, + ) -> Result<(), Error> { + if into.is_some() { + return Err(Error::DuplicateValue); + } + let value = deserializer.take_str()?; match T::from_str(value) { - Ok(value) => Ok(Self(Some(value))), + Ok(value) => { + *into = Some(FromXmlStr(value)); + Ok(()) + } Err(_) => Err(Error::UnexpectedValue), } } @@ -23,10 +33,23 @@ impl<'xml, T: FromStr> FromXml<'xml> for FromXmlStr { } impl<'xml> FromXml<'xml> for bool { - fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result { - FromXmlStr::::deserialize(deserializer)? - .0 - .ok_or(Error::MissingValue(&Kind::Scalar)) + fn deserialize<'cx>( + deserializer: &'cx mut Deserializer<'cx, 'xml>, + into: &mut Option, + ) -> Result<(), Error> { + if into.is_some() { + return Err(Error::DuplicateValue); + } + + let mut value = None; + FromXmlStr::::deserialize(deserializer, &mut value)?; + match value { + Some(value) => { + *into = Some(value.0); + Ok(()) + } + None => Err(Error::MissingValue(&Kind::Scalar)), + } } const KIND: Kind<'static> = Kind::Scalar; @@ -69,10 +92,21 @@ macro_rules! from_xml_for_number { impl<'xml> FromXml<'xml> for $typ { fn deserialize<'cx>( deserializer: &'cx mut Deserializer<'cx, 'xml>, - ) -> Result { - FromXmlStr::::deserialize(deserializer)? - .0 - .ok_or(Error::MissingValue(&Kind::Scalar)) + into: &mut Option, + ) -> Result<(), Error> { + if into.is_some() { + return Err(Error::DuplicateValue); + } + + let mut value = None; + FromXmlStr::::deserialize(deserializer, &mut value)?; + match value { + Some(value) => { + *into = Some(value.0); + Ok(()) + } + None => Err(Error::MissingValue(&Kind::Scalar)), + } } const KIND: Kind<'static> = Kind::Scalar; @@ -94,53 +128,109 @@ from_xml_for_number!(f32); from_xml_for_number!(f64); impl<'xml> FromXml<'xml> for char { - fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result { - FromXmlStr::::deserialize(deserializer)? - .0 - .ok_or(Error::MissingValue(&Kind::Scalar)) + fn deserialize<'cx>( + deserializer: &'cx mut Deserializer<'cx, 'xml>, + into: &mut Option, + ) -> Result<(), Error> { + if into.is_some() { + return Err(Error::DuplicateValue); + } + + let mut value = None; + FromXmlStr::::deserialize(deserializer, &mut value)?; + match value { + Some(value) => { + *into = Some(value.0); + Ok(()) + } + None => Err(Error::MissingValue(&Kind::Scalar)), + } } const KIND: Kind<'static> = Kind::Scalar; } impl<'xml> FromXml<'xml> for String { - fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result { - Ok( as FromXml<'xml>>::deserialize(deserializer)?.into_owned()) + fn deserialize<'cx>( + deserializer: &'cx mut Deserializer<'cx, 'xml>, + into: &mut Option, + ) -> Result<(), Error> { + if into.is_some() { + return Err(Error::DuplicateValue); + } + + let mut value = None; + as FromXml<'xml>>::deserialize(deserializer, &mut value)?; + match value { + Some(value) => { + *into = Some(value.into_owned()); + Ok(()) + } + None => Err(Error::MissingValue(&Kind::Scalar)), + } } const KIND: Kind<'static> = Kind::Scalar; } impl<'xml> FromXml<'xml> for &'xml str { - fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result { - Ok( - match as FromXml<'xml>>::deserialize(deserializer)? { - Cow::Borrowed(s) => s, - Cow::Owned(_) => return Err(Error::UnexpectedValue), - }, - ) + fn deserialize<'cx>( + deserializer: &'cx mut Deserializer<'cx, 'xml>, + into: &mut Option, + ) -> Result<(), Error> { + if into.is_some() { + return Err(Error::DuplicateValue); + } + + let mut value = None; + as FromXml<'xml>>::deserialize(deserializer, &mut value)?; + match value { + Some(Cow::Borrowed(s)) => { + *into = Some(s); + Ok(()) + } + Some(Cow::Owned(_)) => Err(Error::UnexpectedValue), + None => Err(Error::MissingValue(&Kind::Scalar)), + } } const KIND: Kind<'static> = Kind::Scalar; } impl<'xml> FromXml<'xml> for Cow<'xml, str> { - fn deserialize(deserializer: &mut Deserializer<'_, 'xml>) -> Result { + fn deserialize( + deserializer: &mut Deserializer<'_, 'xml>, + into: &mut Option, + ) -> Result<(), Error> { + if into.is_some() { + return Err(Error::DuplicateValue); + } + let value = deserializer.take_str()?; - Ok(decode(value)) + *into = Some(decode(value)); + Ok(()) } const KIND: Kind<'static> = Kind::Scalar; } -impl<'xml, T> FromXml<'xml> for Option -where - T: FromXml<'xml>, -{ - fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result { - match ::deserialize(deserializer) { - Ok(v) => Ok(Some(v)), - Err(e) => Err(e), +impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Option { + fn deserialize<'cx>( + deserializer: &'cx mut Deserializer<'cx, 'xml>, + into: &mut Option, + ) -> Result<(), Error> { + if into.is_some() { + return Err(Error::DuplicateValue); + } + + let mut value = None; + ::deserialize(deserializer, &mut value)?; + match value { + Some(v) => { + *into = Some(Some(v)); + Ok(()) + } + None => Err(Error::MissingValue(&Kind::Scalar)), } } @@ -320,39 +410,26 @@ fn decode(input: &str) -> Cow<'_, str> { Cow::Owned(result) } -const VEC_LIST_TAG: &str = "list"; -const VEC_ELEMENT_TAG: &str = "element"; - impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Vec { - fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result { - let mut result = Self::new(); - let kind = >::KIND; - - while let Some(Ok(node)) = deserializer.next() { - match (&kind, node) { - (Kind::Scalar, Node::Open(data)) => { - let id = deserializer.element_id(&data)?; - - match id.name { - VEC_ELEMENT_TAG => { - let mut nested = deserializer.nested(data); - result.push(>::deserialize(&mut nested)?) - } - _ => return Err(Error::UnexpectedState("unexpected list element name")), - } - } - (Kind::Vec | Kind::Element(_), Node::Open(data)) => { - let mut nested = deserializer.nested(data); - result.push(>::deserialize(&mut nested)?) - } - _ => return Err(Error::UnexpectedState("unexpected node for list")), - } + fn deserialize<'cx>( + deserializer: &'cx mut Deserializer<'cx, 'xml>, + into: &mut Option, + ) -> Result<(), Error> { + let mut value = None; + T::deserialize(deserializer, &mut value)?; + let dst = into.get_or_insert(Vec::new()); + if let Some(value) = value { + dst.push(value); } - Ok(result) + Ok(()) } - const KIND: Kind<'static> = Kind::Vec; + fn missing_value() -> Result { + Ok(Vec::new()) + } + + const KIND: Kind<'static> = Kind::Vec(T::KIND.element()); } impl ToXml for Vec { @@ -360,35 +437,19 @@ impl ToXml for Vec { &self, serializer: &mut Serializer, ) -> Result<(), Error> { - match ::KIND { - Kind::Element(_) => { - for i in self { - i.serialize(serializer)?; - } - } - Kind::Scalar => { - for i in self { - let prefix = serializer.write_start(VEC_ELEMENT_TAG, "", false)?; - serializer.end_start()?; - i.serialize(serializer)?; - serializer.write_close(prefix, VEC_ELEMENT_TAG)?; - } - } - // this would be a Vec>, where the internal field is unnamed - Kind::Vec => { - for i in self { - let prefix = serializer.write_start(VEC_LIST_TAG, "", false)?; - serializer.end_start()?; - i.serialize(serializer)?; - serializer.write_close(prefix, VEC_LIST_TAG)?; - } - } + match T::KIND { + Kind::Element(_) => {} + _ => return Err(Error::UnexpectedState("only elements allowed in `Vec`")), + } + + for i in self { + i.serialize(serializer)?; } Ok(()) } - const KIND: Kind<'static> = Kind::Vec; + const KIND: Kind<'static> = Kind::Vec(T::KIND.element()); } #[cfg(feature = "chrono")] @@ -405,10 +466,20 @@ impl ToXml for DateTime { #[cfg(feature = "chrono")] impl<'xml> FromXml<'xml> for DateTime { - fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result { + fn deserialize<'cx>( + deserializer: &'cx mut Deserializer<'cx, 'xml>, + into: &mut Option, + ) -> Result<(), Error> { + if into.is_some() { + return Err(Error::DuplicateValue); + } + let data = deserializer.take_str()?; match DateTime::parse_from_rfc3339(data) { - Ok(dt) if dt.timezone().utc_minus_local() == 0 => Ok(dt.with_timezone(&Utc)), + Ok(dt) if dt.timezone().utc_minus_local() == 0 => { + *into = Some(dt.with_timezone(&Utc)); + Ok(()) + } _ => Err(Error::Other("invalid date/time".into())), } } diff --git a/instant-xml/src/lib.rs b/instant-xml/src/lib.rs index ddba774..d2b5ac9 100644 --- a/instant-xml/src/lib.rs +++ b/instant-xml/src/lib.rs @@ -34,7 +34,10 @@ impl<'a, T: ToXml + ?Sized> ToXml for &'a T { } pub trait FromXml<'xml>: Sized { - fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result; + fn deserialize<'cx>( + deserializer: &'cx mut Deserializer<'cx, 'xml>, + into: &mut Option, + ) -> Result<(), Error>; // If the missing field is of type `Option` then treat is as `None`, // otherwise it is an error. @@ -50,7 +53,7 @@ pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result { let id = context.element_id(&root)?; let expected = match T::KIND { Kind::Scalar => return Err(Error::UnexpectedState("found scalar as root")), - Kind::Vec => return Err(Error::UnexpectedState("found list as root")), + Kind::Vec(_) => return Err(Error::UnexpectedState("found list as root")), Kind::Element(expected) => expected, }; @@ -58,7 +61,12 @@ pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result { return Err(Error::UnexpectedValue); } - T::deserialize(&mut Deserializer::new(root, &mut context)) + let mut value = None; + T::deserialize(&mut Deserializer::new(root, &mut context), &mut value)?; + match value { + Some(value) => Ok(value), + None => T::missing_value(), + } } pub fn to_string(value: &(impl ToXml + ?Sized)) -> Result { @@ -116,15 +124,22 @@ pub enum Error { pub enum Kind<'a> { Scalar, Element(Id<'a>), - Vec, + Vec(Id<'a>), } impl<'a> Kind<'a> { + pub const fn element(&self) -> Id<'a> { + match self { + Kind::Element(id) => *id, + _ => panic!("expected element kind"), + } + } + pub const fn name(&self, field: Id<'a>) -> Id<'a> { match self { Kind::Scalar => field, Kind::Element(name) => *name, - Kind::Vec => field, + Kind::Vec(inner) => *inner, } } @@ -133,7 +148,7 @@ impl<'a> Kind<'a> { match self { Kind::Scalar => id == field, Kind::Element(name) => id == *name, - Kind::Vec => id == field, + Kind::Vec(inner) => id == *inner, } } } diff --git a/instant-xml/tests/vec.rs b/instant-xml/tests/vec.rs new file mode 100644 index 0000000..ea9ca3e --- /dev/null +++ b/instant-xml/tests/vec.rs @@ -0,0 +1,33 @@ +use instant_xml::{from_str, to_string, FromXml, ToXml}; + +#[derive(Debug, Eq, FromXml, PartialEq, ToXml)] +struct Foo { + bar: usize, +} + +#[derive(Debug, Eq, FromXml, PartialEq, ToXml)] +struct Bar { + foo: Vec, +} + +#[test] +fn vec() { + let val = Bar { foo: vec![] }; + let xml = ""; + assert_eq!(xml, to_string(&val).unwrap()); + assert_eq!(val, from_str(xml).unwrap()); + + let val = Bar { + foo: vec![Foo { bar: 42 }], + }; + let xml = "42"; + assert_eq!(xml, to_string(&val).unwrap()); + assert_eq!(val, from_str(xml).unwrap()); + + let val = Bar { + foo: vec![Foo { bar: 42 }, Foo { bar: 73 }], + }; + let xml = "4273"; + assert_eq!(xml, to_string(&val).unwrap()); + assert_eq!(val, from_str(xml).unwrap()); +}