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>(
deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
into: &mut Self::Accumulator,
) -> Result<(), ::instant_xml::Error> {
use ::instant_xml::Error;
@ -88,6 +88,7 @@ fn deserialize_scalar_enum(
Ok(())
}
type Accumulator = Option<Self>;
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<Self>,
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<Self>;
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<Self>,
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<Self>;
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<Self>,
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<Self>;
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<Self>,
into: &mut Self::Accumulator,
) -> Result<(), ::instant_xml::Error> {
deserializer.ignore()?;
*into = Some(Self);
Ok(())
}
type Accumulator = Option<Self>;
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::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<T> {
fn deserialize(
deserializer: &mut Deserializer<'_, 'xml>,
into: &mut Option<Self>,
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<T> {
}
}
type Accumulator = Option<FromXmlStr<T>>;
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<Self>,
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<bool>;
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<Self>,
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<Self>;
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<Self>,
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<Self>;
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<Self>,
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<String>;
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<Self>,
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<Self>,
into: &mut Self::Accumulator,
) -> Result<(), Error> {
if into.is_some() {
return Err(Error::DuplicateValue);
}
let mut value = None;
let mut value = <T::Owned as FromXml<'xml>>::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<T>")?));
Ok(())
}
type Accumulator = Option<Cow<'a, T>>;
const KIND: Kind = Kind::Scalar;
}
@ -348,31 +352,39 @@ impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Option<T> {
fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
into: &mut Self::Accumulator,
) -> Result<(), Error> {
match into.as_mut() {
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));
}
}
}
<T>::deserialize(deserializer, &mut into.value)?;
Ok(())
}
fn missing(_: &'static str) -> Result<Self, Error> {
Ok(None)
}
type Accumulator = OptionAccumulator<T, T::Accumulator>;
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!(i16);
to_xml_for_number!(i32);
@ -556,22 +568,15 @@ impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Vec<T> {
fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
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<T>")?);
Ok(())
}
fn missing(_: &'static str) -> Result<Self, Error> {
Ok(Vec::new())
}
type Accumulator = Vec<T>;
const KIND: Kind = T::KIND;
}
@ -636,7 +641,7 @@ impl<'xml> FromXml<'xml> for DateTime<Utc> {
fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
into: &mut Self::Accumulator,
) -> Result<(), Error> {
if into.is_some() {
return Err(Error::DuplicateValue);
@ -656,6 +661,7 @@ impl<'xml> FromXml<'xml> for DateTime<Utc> {
}
}
type Accumulator = Option<Self>;
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<Self>,
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<Self>;
const KIND: Kind = Kind::Scalar;
}
@ -730,12 +737,13 @@ impl<'xml> FromXml<'xml> for () {
fn deserialize<'cx>(
_: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
into: &mut Self::Accumulator,
) -> Result<(), Error> {
*into = Some(());
Ok(())
}
type Accumulator = Option<Self>;
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<Self>,
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<Self>;
const KIND: Kind = Kind::Scalar;
}

View File

@ -41,18 +41,36 @@ pub trait FromXml<'xml>: Sized {
fn deserialize<'cx>(
deserializer: &mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
into: &mut Self::Accumulator,
) -> Result<(), Error>;
// If the missing field is of type `Option<T>` then treat is as `None`,
// otherwise it is an error.
fn missing(field: &'static str) -> Result<Self, Error> {
Err(Error::MissingValue(field))
}
type Accumulator: Accumulate<Self>;
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> {
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<T, Error> {
}));
}
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("<root>"),
}
value.try_done("root element")
}
pub fn to_string(value: &(impl ToXml + ?Sized)) -> Result<String, Error> {