diff --git a/instant-xml-macros/src/de.rs b/instant-xml-macros/src/de.rs index 8371f27..9fe5c6d 100644 --- a/instant-xml-macros/src/de.rs +++ b/instant-xml-macros/src/de.rs @@ -68,7 +68,7 @@ fn deserialize_scalar_enum( fn deserialize<'cx>( deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), ::instant_xml::Error> { use ::instant_xml::Error; @@ -88,6 +88,7 @@ fn deserialize_scalar_enum( Ok(()) } + type Accumulator = Option; const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Scalar; } ) @@ -163,10 +164,10 @@ fn deserialize_forward_enum( fn deserialize<'cx>( deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), ::instant_xml::Error> { use ::instant_xml::de::Node; - use ::instant_xml::{Error, FromXml}; + use ::instant_xml::{Accumulate, Error, FromXml}; let id = deserializer.parent(); #variants else { @@ -180,6 +181,7 @@ fn deserialize_forward_enum( Ok(()) } + type Accumulator = Option; const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element; } ) @@ -283,10 +285,10 @@ fn deserialize_struct( fn deserialize<'cx>( deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), ::instant_xml::Error> { use ::instant_xml::de::Node; - use ::instant_xml::{Error, FromXml, Id, Kind}; + use ::instant_xml::{Accumulate, Error, FromXml, Id, Kind}; enum __Elements { #elements_enum @@ -336,6 +338,7 @@ fn deserialize_struct( Ok(()) } + type Accumulator = Option; const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element; } ) @@ -399,7 +402,7 @@ fn named_field( } declare_values.extend(quote!( - let mut #enum_name: Option<#no_lifetime_type> = None; + let mut #enum_name = <#no_lifetime_type as FromXml>::Accumulator::default(); )); let deserialize_with = field_meta @@ -434,7 +437,7 @@ fn named_field( direct.extend(quote!( Node::Text(text) => { let mut nested = deserializer.for_node(Node::Text(text)); - FromXml::deserialize(&mut nested, &mut #enum_name)?; + <#no_lifetime_type>::deserialize(&mut nested, &mut #enum_name)?; } )); } else { @@ -442,11 +445,11 @@ fn named_field( __Elements::#enum_name => match <#no_lifetime_type as FromXml>::KIND { Kind::Element => { let mut nested = deserializer.nested(data); - FromXml::deserialize(&mut nested, &mut #enum_name)?; + <#no_lifetime_type>::deserialize(&mut nested, &mut #enum_name)?; } Kind::Scalar => { let mut nested = deserializer.nested(data); - FromXml::deserialize(&mut nested, &mut #enum_name)?; + <#no_lifetime_type>::deserialize(&mut nested, &mut #enum_name)?; nested.ignore()?; } }, @@ -479,10 +482,7 @@ fn named_field( let field_str = format!("{type_name}::{field_name}"); return_val.extend(quote!( - #field_name: match #enum_name { - Some(v) => v, - None => <#no_lifetime_type as FromXml>::missing(#field_str)?, - }, + #field_name: #enum_name.try_done(#field_str)?, )); Ok(()) @@ -540,10 +540,10 @@ fn deserialize_tuple_struct( fn deserialize<'cx>( deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), ::instant_xml::Error> { use ::instant_xml::de::Node; - use ::instant_xml::{Error, FromXml, Id, Kind}; + use ::instant_xml::{Accumulate, Error, FromXml, Id, Kind}; #declare_values deserializer.ignore()?; @@ -552,6 +552,7 @@ fn deserialize_tuple_struct( Ok(()) } + type Accumulator = Option; const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element; } ) @@ -576,7 +577,7 @@ fn unnamed_field( Kind::Element => match deserializer.next() { Some(Ok(Node::Open(data))) => { let mut nested = deserializer.nested(data); - let mut value: Option<#no_lifetime_type> = None; + let mut value = <#no_lifetime_type as FromXml>::Accumulator::default(); <#no_lifetime_type as FromXml>::deserialize(&mut nested, &mut value)?; nested.ignore()?; value @@ -586,7 +587,7 @@ fn unnamed_field( None => return Err(Error::MissingValue(#field_str)), } Kind::Scalar => { - let mut value: Option<#no_lifetime_type> = None; + let mut value = <#no_lifetime_type as FromXml>::Accumulator::default(); <#no_lifetime_type as FromXml>::deserialize(deserializer, &mut value)?; value } @@ -595,10 +596,7 @@ fn unnamed_field( let field_str = format!("{type_name}::{index}"); return_val.extend(quote!( - match #name { - Some(v) => v, - None => <#no_lifetime_type as FromXml>::missing(#field_str)?, - }, + #name.try_done(#field_str)?, )); } @@ -620,13 +618,14 @@ fn deserialize_unit_struct(input: &syn::DeriveInput, meta: &ContainerMeta) -> To fn deserialize<'cx>( deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), ::instant_xml::Error> { deserializer.ignore()?; *into = Some(Self); Ok(()) } + type Accumulator = Option; const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element; } ) diff --git a/instant-xml/src/impls.rs b/instant-xml/src/impls.rs index 099810d..ff291fa 100644 --- a/instant-xml/src/impls.rs +++ b/instant-xml/src/impls.rs @@ -1,13 +1,13 @@ -use std::any::type_name; use std::borrow::Cow; use std::fmt; use std::net::IpAddr; use std::str::FromStr; +use std::{any::type_name, marker::PhantomData}; #[cfg(feature = "chrono")] use chrono::{DateTime, NaiveDate, Utc}; -use crate::{Deserializer, Error, FromXml, Id, Kind, Serializer, ToXml}; +use crate::{Accumulate, Deserializer, Error, FromXml, Id, Kind, Serializer, ToXml}; // Deserializer @@ -49,7 +49,7 @@ impl<'xml, T: FromStr> FromXml<'xml> for FromXmlStr { fn deserialize( deserializer: &mut Deserializer<'_, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { if into.is_some() { return Err(Error::DuplicateValue); @@ -72,6 +72,7 @@ impl<'xml, T: FromStr> FromXml<'xml> for FromXmlStr { } } + type Accumulator = Option>; const KIND: Kind = Kind::Scalar; } @@ -86,7 +87,7 @@ impl<'xml> FromXml<'xml> for bool { fn deserialize<'cx>( deserializer: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { if into.is_some() { return Err(Error::DuplicateValue); @@ -111,6 +112,7 @@ impl<'xml> FromXml<'xml> for bool { Ok(()) } + type Accumulator = Option; const KIND: Kind = Kind::Scalar; } @@ -180,7 +182,7 @@ macro_rules! from_xml_for_number { fn deserialize<'cx>( deserializer: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { if into.is_some() { return Err(Error::DuplicateValue); @@ -195,6 +197,7 @@ macro_rules! from_xml_for_number { Ok(()) } + type Accumulator = Option; const KIND: Kind = Kind::Scalar; } }; @@ -224,7 +227,7 @@ impl<'xml> FromXml<'xml> for char { fn deserialize<'cx>( deserializer: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { if into.is_some() { return Err(Error::DuplicateValue); @@ -239,6 +242,7 @@ impl<'xml> FromXml<'xml> for char { Ok(()) } + type Accumulator = Option; const KIND: Kind = Kind::Scalar; } @@ -253,7 +257,7 @@ impl<'xml> FromXml<'xml> for String { fn deserialize<'cx>( deserializer: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { if into.is_some() { return Err(Error::DuplicateValue); @@ -267,6 +271,7 @@ impl<'xml> FromXml<'xml> for String { Ok(()) } + type Accumulator = Option; const KIND: Kind = Kind::Scalar; } @@ -281,7 +286,7 @@ impl<'xml> FromXml<'xml> for &'xml str { fn deserialize<'cx>( deserializer: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { if into.is_some() { return Err(Error::DuplicateValue); @@ -304,6 +309,7 @@ impl<'xml> FromXml<'xml> for &'xml str { Ok(()) } + type Accumulator = Option<&'xml str>; const KIND: Kind = Kind::Scalar; } @@ -322,21 +328,19 @@ where fn deserialize( deserializer: &mut Deserializer<'_, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { if into.is_some() { return Err(Error::DuplicateValue); } - let mut value = None; + let mut value = >::Accumulator::default(); T::Owned::deserialize(deserializer, &mut value)?; - if let Some(value) = value { - *into = Some(Cow::Owned(value)); - } - + *into = Some(Cow::Owned(value.try_done("Cow")?)); Ok(()) } + type Accumulator = Option>; const KIND: Kind = Kind::Scalar; } @@ -348,31 +352,39 @@ impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Option { fn deserialize<'cx>( deserializer: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { - match into.as_mut() { - Some(value) => { - ::deserialize(deserializer, value)?; - } - None => { - let mut value = None; - ::deserialize(deserializer, &mut value)?; - if let Some(value) = value { - *into = Some(Some(value)); - } - } - } - + ::deserialize(deserializer, &mut into.value)?; Ok(()) } - fn missing(_: &'static str) -> Result { - Ok(None) - } - + type Accumulator = OptionAccumulator; const KIND: Kind = ::KIND; } +pub struct OptionAccumulator> { + value: A, + marker: PhantomData, +} + +impl> Default for OptionAccumulator { + fn default() -> Self { + Self { + value: A::default(), + marker: PhantomData, + } + } +} + +impl> Accumulate> for OptionAccumulator { + fn try_done(self, field: &'static str) -> Result, Error> { + match self.value.try_done(field) { + Ok(value) => Ok(Some(value)), + Err(_) => Ok(None), + } + } +} + to_xml_for_number!(i8); to_xml_for_number!(i16); to_xml_for_number!(i32); @@ -556,22 +568,15 @@ impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Vec { fn deserialize<'cx>( deserializer: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { - let mut value = None; + let mut value = T::Accumulator::default(); T::deserialize(deserializer, &mut value)?; - let dst = into.get_or_insert(Vec::new()); - if let Some(value) = value { - dst.push(value); - } - + into.push(value.try_done("Vec")?); Ok(()) } - fn missing(_: &'static str) -> Result { - Ok(Vec::new()) - } - + type Accumulator = Vec; const KIND: Kind = T::KIND; } @@ -636,7 +641,7 @@ impl<'xml> FromXml<'xml> for DateTime { fn deserialize<'cx>( deserializer: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { if into.is_some() { return Err(Error::DuplicateValue); @@ -656,6 +661,7 @@ impl<'xml> FromXml<'xml> for DateTime { } } + type Accumulator = Option; const KIND: Kind = Kind::Scalar; } @@ -696,7 +702,7 @@ impl<'xml> FromXml<'xml> for NaiveDate { fn deserialize<'cx>( deserializer: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { if into.is_some() { return Err(Error::DuplicateValue); @@ -716,6 +722,7 @@ impl<'xml> FromXml<'xml> for NaiveDate { } } + type Accumulator = Option; const KIND: Kind = Kind::Scalar; } @@ -730,12 +737,13 @@ impl<'xml> FromXml<'xml> for () { fn deserialize<'cx>( _: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { *into = Some(()); Ok(()) } + type Accumulator = Option; const KIND: Kind = Kind::Scalar; } @@ -760,7 +768,7 @@ impl<'xml> FromXml<'xml> for IpAddr { fn deserialize<'cx>( deserializer: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error> { if into.is_some() { return Err(Error::DuplicateValue); @@ -775,6 +783,7 @@ impl<'xml> FromXml<'xml> for IpAddr { Ok(()) } + type Accumulator = Option; const KIND: Kind = Kind::Scalar; } diff --git a/instant-xml/src/lib.rs b/instant-xml/src/lib.rs index aed6d8e..58c4a86 100644 --- a/instant-xml/src/lib.rs +++ b/instant-xml/src/lib.rs @@ -41,18 +41,36 @@ pub trait FromXml<'xml>: Sized { fn deserialize<'cx>( deserializer: &mut Deserializer<'cx, 'xml>, - into: &mut Option, + into: &mut Self::Accumulator, ) -> Result<(), Error>; - // If the missing field is of type `Option` then treat is as `None`, - // otherwise it is an error. - fn missing(field: &'static str) -> Result { - Err(Error::MissingValue(field)) - } - + type Accumulator: Accumulate; const KIND: Kind; } +/// A type implementing `Accumulate` is used to accumulate a value of type `T`. +pub trait Accumulate: Default { + fn try_done(self, field: &'static str) -> Result; +} + +impl Accumulate for Option { + fn try_done(self, field: &'static str) -> Result { + self.ok_or(Error::MissingValue(field)) + } +} + +impl Accumulate> for Vec { + fn try_done(self, _: &'static str) -> Result, Error> { + Ok(self) + } +} + +impl Accumulate> for Option { + fn try_done(self, _: &'static str) -> Result, Error> { + Ok(self) + } +} + pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result { let (mut context, root) = Context::new(input)?; let id = context.element_id(&root)?; @@ -67,12 +85,9 @@ pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result { })); } - let mut value = None; + let mut value = T::Accumulator::default(); T::deserialize(&mut Deserializer::new(root, &mut context), &mut value)?; - match value { - Some(value) => Ok(value), - None => T::missing(""), - } + value.try_done("root element") } pub fn to_string(value: &(impl ToXml + ?Sized)) -> Result {