Use associated accumulator type to deserialize into

This commit is contained in:
Dirkjan Ochtman 2023-02-22 15:41:39 +01:00
parent be7902925e
commit 7a2e6ac735
3 changed files with 104 additions and 81 deletions

View File

@ -68,7 +68,7 @@ fn deserialize_scalar_enum(
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>, deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), ::instant_xml::Error> { ) -> Result<(), ::instant_xml::Error> {
use ::instant_xml::Error; use ::instant_xml::Error;
@ -88,6 +88,7 @@ fn deserialize_scalar_enum(
Ok(()) Ok(())
} }
type Accumulator = Option<Self>;
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Scalar; const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Scalar;
} }
) )
@ -163,10 +164,10 @@ fn deserialize_forward_enum(
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>, deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), ::instant_xml::Error> { ) -> Result<(), ::instant_xml::Error> {
use ::instant_xml::de::Node; use ::instant_xml::de::Node;
use ::instant_xml::{Error, FromXml}; use ::instant_xml::{Accumulate, Error, FromXml};
let id = deserializer.parent(); let id = deserializer.parent();
#variants else { #variants else {
@ -180,6 +181,7 @@ fn deserialize_forward_enum(
Ok(()) Ok(())
} }
type Accumulator = Option<Self>;
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element; const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element;
} }
) )
@ -283,10 +285,10 @@ fn deserialize_struct(
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>, deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), ::instant_xml::Error> { ) -> Result<(), ::instant_xml::Error> {
use ::instant_xml::de::Node; use ::instant_xml::de::Node;
use ::instant_xml::{Error, FromXml, Id, Kind}; use ::instant_xml::{Accumulate, Error, FromXml, Id, Kind};
enum __Elements { enum __Elements {
#elements_enum #elements_enum
@ -336,6 +338,7 @@ fn deserialize_struct(
Ok(()) Ok(())
} }
type Accumulator = Option<Self>;
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element; const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element;
} }
) )
@ -399,7 +402,7 @@ fn named_field(
} }
declare_values.extend(quote!( 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 let deserialize_with = field_meta
@ -434,7 +437,7 @@ fn named_field(
direct.extend(quote!( direct.extend(quote!(
Node::Text(text) => { Node::Text(text) => {
let mut nested = deserializer.for_node(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 { } else {
@ -442,11 +445,11 @@ fn named_field(
__Elements::#enum_name => match <#no_lifetime_type as FromXml>::KIND { __Elements::#enum_name => match <#no_lifetime_type as FromXml>::KIND {
Kind::Element => { Kind::Element => {
let mut nested = deserializer.nested(data); let mut nested = deserializer.nested(data);
FromXml::deserialize(&mut nested, &mut #enum_name)?; <#no_lifetime_type>::deserialize(&mut nested, &mut #enum_name)?;
} }
Kind::Scalar => { Kind::Scalar => {
let mut nested = deserializer.nested(data); let mut nested = deserializer.nested(data);
FromXml::deserialize(&mut nested, &mut #enum_name)?; <#no_lifetime_type>::deserialize(&mut nested, &mut #enum_name)?;
nested.ignore()?; nested.ignore()?;
} }
}, },
@ -479,10 +482,7 @@ fn named_field(
let field_str = format!("{type_name}::{field_name}"); let field_str = format!("{type_name}::{field_name}");
return_val.extend(quote!( return_val.extend(quote!(
#field_name: match #enum_name { #field_name: #enum_name.try_done(#field_str)?,
Some(v) => v,
None => <#no_lifetime_type as FromXml>::missing(#field_str)?,
},
)); ));
Ok(()) Ok(())
@ -540,10 +540,10 @@ fn deserialize_tuple_struct(
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>, deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), ::instant_xml::Error> { ) -> Result<(), ::instant_xml::Error> {
use ::instant_xml::de::Node; use ::instant_xml::de::Node;
use ::instant_xml::{Error, FromXml, Id, Kind}; use ::instant_xml::{Accumulate, Error, FromXml, Id, Kind};
#declare_values #declare_values
deserializer.ignore()?; deserializer.ignore()?;
@ -552,6 +552,7 @@ fn deserialize_tuple_struct(
Ok(()) Ok(())
} }
type Accumulator = Option<Self>;
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element; const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element;
} }
) )
@ -576,7 +577,7 @@ fn unnamed_field(
Kind::Element => match deserializer.next() { Kind::Element => match deserializer.next() {
Some(Ok(Node::Open(data))) => { Some(Ok(Node::Open(data))) => {
let mut nested = deserializer.nested(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)?; <#no_lifetime_type as FromXml>::deserialize(&mut nested, &mut value)?;
nested.ignore()?; nested.ignore()?;
value value
@ -586,7 +587,7 @@ fn unnamed_field(
None => return Err(Error::MissingValue(#field_str)), None => return Err(Error::MissingValue(#field_str)),
} }
Kind::Scalar => { 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)?; <#no_lifetime_type as FromXml>::deserialize(deserializer, &mut value)?;
value value
} }
@ -595,10 +596,7 @@ fn unnamed_field(
let field_str = format!("{type_name}::{index}"); let field_str = format!("{type_name}::{index}");
return_val.extend(quote!( return_val.extend(quote!(
match #name { #name.try_done(#field_str)?,
Some(v) => v,
None => <#no_lifetime_type as FromXml>::missing(#field_str)?,
},
)); ));
} }
@ -620,13 +618,14 @@ fn deserialize_unit_struct(input: &syn::DeriveInput, meta: &ContainerMeta) -> To
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>, deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), ::instant_xml::Error> { ) -> Result<(), ::instant_xml::Error> {
deserializer.ignore()?; deserializer.ignore()?;
*into = Some(Self); *into = Some(Self);
Ok(()) Ok(())
} }
type Accumulator = Option<Self>;
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element; const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element;
} }
) )

View File

@ -1,13 +1,13 @@
use std::any::type_name;
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::net::IpAddr; use std::net::IpAddr;
use std::str::FromStr; use std::str::FromStr;
use std::{any::type_name, marker::PhantomData};
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
use chrono::{DateTime, NaiveDate, Utc}; 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 // Deserializer
@ -49,7 +49,7 @@ impl<'xml, T: FromStr> FromXml<'xml> for FromXmlStr<T> {
fn deserialize( fn deserialize(
deserializer: &mut Deserializer<'_, 'xml>, deserializer: &mut Deserializer<'_, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
@ -72,6 +72,7 @@ impl<'xml, T: FromStr> FromXml<'xml> for FromXmlStr<T> {
} }
} }
type Accumulator = Option<FromXmlStr<T>>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }
@ -86,7 +87,7 @@ impl<'xml> FromXml<'xml> for bool {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>, deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
@ -111,6 +112,7 @@ impl<'xml> FromXml<'xml> for bool {
Ok(()) Ok(())
} }
type Accumulator = Option<bool>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }
@ -180,7 +182,7 @@ macro_rules! from_xml_for_number {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>, deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
@ -195,6 +197,7 @@ macro_rules! from_xml_for_number {
Ok(()) Ok(())
} }
type Accumulator = Option<Self>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }
}; };
@ -224,7 +227,7 @@ impl<'xml> FromXml<'xml> for char {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>, deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
@ -239,6 +242,7 @@ impl<'xml> FromXml<'xml> for char {
Ok(()) Ok(())
} }
type Accumulator = Option<Self>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }
@ -253,7 +257,7 @@ impl<'xml> FromXml<'xml> for String {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>, deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
@ -267,6 +271,7 @@ impl<'xml> FromXml<'xml> for String {
Ok(()) Ok(())
} }
type Accumulator = Option<String>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }
@ -281,7 +286,7 @@ impl<'xml> FromXml<'xml> for &'xml str {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>, deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
@ -304,6 +309,7 @@ impl<'xml> FromXml<'xml> for &'xml str {
Ok(()) Ok(())
} }
type Accumulator = Option<&'xml str>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }
@ -322,21 +328,19 @@ where
fn deserialize( fn deserialize(
deserializer: &mut Deserializer<'_, 'xml>, deserializer: &mut Deserializer<'_, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
} }
let mut value = None; let mut value = <T::Owned as FromXml<'xml>>::Accumulator::default();
T::Owned::deserialize(deserializer, &mut value)?; T::Owned::deserialize(deserializer, &mut value)?;
if let Some(value) = value { *into = Some(Cow::Owned(value.try_done("Cow<T>")?));
*into = Some(Cow::Owned(value));
}
Ok(()) Ok(())
} }
type Accumulator = Option<Cow<'a, T>>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }
@ -348,31 +352,39 @@ impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Option<T> {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>, deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
match into.as_mut() { <T>::deserialize(deserializer, &mut into.value)?;
Some(value) => {
<T>::deserialize(deserializer, value)?;
}
None => {
let mut value = None;
<T>::deserialize(deserializer, &mut value)?;
if let Some(value) = value {
*into = Some(Some(value));
}
}
}
Ok(()) Ok(())
} }
fn missing(_: &'static str) -> Result<Self, Error> { type Accumulator = OptionAccumulator<T, T::Accumulator>;
Ok(None)
}
const KIND: Kind = <T>::KIND; const KIND: Kind = <T>::KIND;
} }
pub struct OptionAccumulator<T, A: Accumulate<T>> {
value: A,
marker: PhantomData<T>,
}
impl<T, A: Accumulate<T>> Default for OptionAccumulator<T, A> {
fn default() -> Self {
Self {
value: A::default(),
marker: PhantomData,
}
}
}
impl<T, A: Accumulate<T>> Accumulate<Option<T>> for OptionAccumulator<T, A> {
fn try_done(self, field: &'static str) -> Result<Option<T>, Error> {
match self.value.try_done(field) {
Ok(value) => Ok(Some(value)),
Err(_) => Ok(None),
}
}
}
to_xml_for_number!(i8); to_xml_for_number!(i8);
to_xml_for_number!(i16); to_xml_for_number!(i16);
to_xml_for_number!(i32); to_xml_for_number!(i32);
@ -556,22 +568,15 @@ impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Vec<T> {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>, deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut value = None; let mut value = T::Accumulator::default();
T::deserialize(deserializer, &mut value)?; T::deserialize(deserializer, &mut value)?;
let dst = into.get_or_insert(Vec::new()); into.push(value.try_done("Vec<T>")?);
if let Some(value) = value {
dst.push(value);
}
Ok(()) Ok(())
} }
fn missing(_: &'static str) -> Result<Self, Error> { type Accumulator = Vec<T>;
Ok(Vec::new())
}
const KIND: Kind = T::KIND; const KIND: Kind = T::KIND;
} }
@ -636,7 +641,7 @@ impl<'xml> FromXml<'xml> for DateTime<Utc> {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>, deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
@ -656,6 +661,7 @@ impl<'xml> FromXml<'xml> for DateTime<Utc> {
} }
} }
type Accumulator = Option<Self>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }
@ -696,7 +702,7 @@ impl<'xml> FromXml<'xml> for NaiveDate {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>, deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
@ -716,6 +722,7 @@ impl<'xml> FromXml<'xml> for NaiveDate {
} }
} }
type Accumulator = Option<Self>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }
@ -730,12 +737,13 @@ impl<'xml> FromXml<'xml> for () {
fn deserialize<'cx>( fn deserialize<'cx>(
_: &mut Deserializer<'cx, 'xml>, _: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
*into = Some(()); *into = Some(());
Ok(()) Ok(())
} }
type Accumulator = Option<Self>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }
@ -760,7 +768,7 @@ impl<'xml> FromXml<'xml> for IpAddr {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>, deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
@ -775,6 +783,7 @@ impl<'xml> FromXml<'xml> for IpAddr {
Ok(()) Ok(())
} }
type Accumulator = Option<Self>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }

View File

@ -41,18 +41,36 @@ pub trait FromXml<'xml>: Sized {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>, deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>, into: &mut Self::Accumulator,
) -> Result<(), Error>; ) -> Result<(), Error>;
// If the missing field is of type `Option<T>` then treat is as `None`, type Accumulator: Accumulate<Self>;
// otherwise it is an error.
fn missing(field: &'static str) -> Result<Self, Error> {
Err(Error::MissingValue(field))
}
const KIND: Kind; const KIND: Kind;
} }
/// A type implementing `Accumulate<T>` is used to accumulate a value of type `T`.
pub trait Accumulate<T>: Default {
fn try_done(self, field: &'static str) -> Result<T, Error>;
}
impl<T> Accumulate<T> for Option<T> {
fn try_done(self, field: &'static str) -> Result<T, Error> {
self.ok_or(Error::MissingValue(field))
}
}
impl<T> Accumulate<Vec<T>> for Vec<T> {
fn try_done(self, _: &'static str) -> Result<Vec<T>, Error> {
Ok(self)
}
}
impl<T> Accumulate<Option<T>> for Option<T> {
fn try_done(self, _: &'static str) -> Result<Option<T>, Error> {
Ok(self)
}
}
pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result<T, Error> { pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result<T, Error> {
let (mut context, root) = Context::new(input)?; let (mut context, root) = Context::new(input)?;
let id = context.element_id(&root)?; let id = context.element_id(&root)?;
@ -67,12 +85,9 @@ pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result<T, Error> {
})); }));
} }
let mut value = None; let mut value = T::Accumulator::default();
T::deserialize(&mut Deserializer::new(root, &mut context), &mut value)?; T::deserialize(&mut Deserializer::new(root, &mut context), &mut value)?;
match value { value.try_done("root element")
Some(value) => Ok(value),
None => T::missing("<root>"),
}
} }
pub fn to_string(value: &(impl ToXml + ?Sized)) -> Result<String, Error> { pub fn to_string(value: &(impl ToXml + ?Sized)) -> Result<String, Error> {