Improve support for Vec fields

This commit is contained in:
Dirkjan Ochtman 2022-11-24 21:28:21 -08:00
parent 284a015bf8
commit aace036ea9
5 changed files with 254 additions and 127 deletions

View File

@ -42,7 +42,7 @@ fn deserialize_scalar_enum(
}; };
let serialize_as = meta.serialize_as; 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(); let generics = meta.xml_generics();
@ -52,12 +52,22 @@ fn deserialize_scalar_enum(
quote!( quote!(
impl #impl_generics FromXml<'xml> for #ident #ty_generics #where_clause { impl #impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml> deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>,
) -> Result<Self, ::instant_xml::Error> { into: &mut Option<Self>,
match deserializer.take_str() { ) -> Result<(), ::instant_xml::Error> {
#variants use ::instant_xml::Error;
_ => Err(::instant_xml::Error::UnexpectedValue)
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; const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Scalar;
@ -106,14 +116,14 @@ fn deserialize_wrapped_enum(
} }
let v_ident = &variant.ident; let v_ident = &variant.ident;
variants.extend( variants.extend(quote!(if <#no_lifetime_type as FromXml>::KIND.matches(
quote!(if <#no_lifetime_type as FromXml>::KIND.matches(
id, ::instant_xml::Id { ns: "", name: "" } id, ::instant_xml::Id { ns: "", name: "" }
) { ) {
let mut nested = deserializer.nested(data); let mut nested = deserializer.nested(data);
#ident::#v_ident(#no_lifetime_type::deserialize(&mut nested)?) let mut value = None;
}), #no_lifetime_type::deserialize(&mut nested, &mut value)?;
); *into = value.map(#ident::#v_ident);
}));
} }
let name = meta.tag(); let name = meta.tag();
@ -123,7 +133,10 @@ fn deserialize_wrapped_enum(
let (_, ty_generics, where_clause) = input.generics.split_for_impl(); let (_, ty_generics, where_clause) = input.generics.split_for_impl();
quote!( quote!(
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause { impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
fn deserialize<'cx>(deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>) -> Result<Self, ::instant_xml::Error> { fn deserialize<'cx>(
deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
) -> Result<(), ::instant_xml::Error> {
use ::instant_xml::de::Node; use ::instant_xml::de::Node;
use ::instant_xml::Error; use ::instant_xml::Error;
@ -138,7 +151,7 @@ fn deserialize_wrapped_enum(
}; };
let id = deserializer.element_id(&data)?; let id = deserializer.element_id(&data)?;
let value = #variants else { #variants else {
return Err(Error::UnexpectedTag); return Err(Error::UnexpectedTag);
}; };
@ -146,7 +159,7 @@ fn deserialize_wrapped_enum(
return Err(Error::UnexpectedState("unexpected node after wrapped enum variant")); 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 { const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Element(::instant_xml::Id {
@ -236,10 +249,12 @@ fn deserialize_struct(
quote!( quote!(
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause { impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
fn deserialize<'cx>(deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>) -> Result<Self, ::instant_xml::Error> { fn deserialize<'cx>(
deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
) -> Result<(), ::instant_xml::Error> {
use ::instant_xml::de::Node; use ::instant_xml::de::Node;
use ::instant_xml::{Error, Id}; use ::instant_xml::{Error, Id};
use ::core::marker::PhantomData;
enum __Elements { enum __Elements {
#elements_enum #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 { const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Element(::instant_xml::Id {
@ -343,23 +359,15 @@ fn process_field(
if !field_meta.attribute { if !field_meta.attribute {
tokens.r#match.extend(quote!( tokens.r#match.extend(quote!(
__Elements::#enum_name => { __Elements::#enum_name => {
if #enum_name.is_some() {
return Err(Error::DuplicateValue);
}
let mut nested = deserializer.nested(data); 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 { } else {
tokens.r#match.extend(quote!( tokens.r#match.extend(quote!(
__Attributes::#enum_name => { __Attributes::#enum_name => {
if #enum_name.is_some() {
return Err(Error::DuplicateValue);
}
let mut nested = deserializer.for_attr(attr); 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)?;
}, },
)); ));
} }

View File

@ -319,10 +319,10 @@ fn process_named_field(
discard_lifetimes(&mut no_lifetime_type); discard_lifetimes(&mut no_lifetime_type);
body.extend(quote!( body.extend(quote!(
match <#no_lifetime_type as ToXml>::KIND { match <#no_lifetime_type as ToXml>::KIND {
::instant_xml::Kind::Element(_) => { ::instant_xml::Kind::Element(_) | ::instant_xml::Kind::Vec(_) => {
self.#field_name.serialize(serializer)?; 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)?; let prefix = serializer.write_start(#tag, #ns, true)?;
serializer.end_start()?; serializer.end_start()?;
self.#field_name.serialize(serializer)?; self.#field_name.serialize(serializer)?;

View File

@ -5,16 +5,26 @@ use std::str::FromStr;
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use crate::{de::Node, Deserializer, Error, FromXml, Kind, Serializer, ToXml}; use crate::{Deserializer, Error, FromXml, Kind, Serializer, ToXml};
// Deserializer // Deserializer
struct FromXmlStr<T: FromStr>(Option<T>); struct FromXmlStr<T: FromStr>(T);
impl<'xml, T: FromStr> FromXml<'xml> for FromXmlStr<T> { impl<'xml, T: FromStr> FromXml<'xml> for FromXmlStr<T> {
fn deserialize(deserializer: &mut Deserializer<'_, 'xml>) -> Result<Self, Error> { fn deserialize(
deserializer: &mut Deserializer<'_, 'xml>,
into: &mut Option<Self>,
) -> Result<(), Error> {
if into.is_some() {
return Err(Error::DuplicateValue);
}
let value = deserializer.take_str()?; let value = deserializer.take_str()?;
match T::from_str(value) { match T::from_str(value) {
Ok(value) => Ok(Self(Some(value))), Ok(value) => {
*into = Some(FromXmlStr(value));
Ok(())
}
Err(_) => Err(Error::UnexpectedValue), Err(_) => Err(Error::UnexpectedValue),
} }
} }
@ -23,10 +33,23 @@ impl<'xml, T: FromStr> FromXml<'xml> for FromXmlStr<T> {
} }
impl<'xml> FromXml<'xml> for bool { impl<'xml> FromXml<'xml> for bool {
fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result<Self, Error> { fn deserialize<'cx>(
FromXmlStr::<Self>::deserialize(deserializer)? deserializer: &'cx mut Deserializer<'cx, 'xml>,
.0 into: &mut Option<Self>,
.ok_or(Error::MissingValue(&Kind::Scalar)) ) -> Result<(), Error> {
if into.is_some() {
return Err(Error::DuplicateValue);
}
let mut value = None;
FromXmlStr::<Self>::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; const KIND: Kind<'static> = Kind::Scalar;
@ -69,10 +92,21 @@ macro_rules! from_xml_for_number {
impl<'xml> FromXml<'xml> for $typ { impl<'xml> FromXml<'xml> for $typ {
fn deserialize<'cx>( fn deserialize<'cx>(
deserializer: &'cx mut Deserializer<'cx, 'xml>, deserializer: &'cx mut Deserializer<'cx, 'xml>,
) -> Result<Self, Error> { into: &mut Option<Self>,
FromXmlStr::<Self>::deserialize(deserializer)? ) -> Result<(), Error> {
.0 if into.is_some() {
.ok_or(Error::MissingValue(&Kind::Scalar)) return Err(Error::DuplicateValue);
}
let mut value = None;
FromXmlStr::<Self>::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; const KIND: Kind<'static> = Kind::Scalar;
@ -94,53 +128,109 @@ from_xml_for_number!(f32);
from_xml_for_number!(f64); from_xml_for_number!(f64);
impl<'xml> FromXml<'xml> for char { impl<'xml> FromXml<'xml> for char {
fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result<Self, Error> { fn deserialize<'cx>(
FromXmlStr::<Self>::deserialize(deserializer)? deserializer: &'cx mut Deserializer<'cx, 'xml>,
.0 into: &mut Option<Self>,
.ok_or(Error::MissingValue(&Kind::Scalar)) ) -> Result<(), Error> {
if into.is_some() {
return Err(Error::DuplicateValue);
}
let mut value = None;
FromXmlStr::<Self>::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; const KIND: Kind<'static> = Kind::Scalar;
} }
impl<'xml> FromXml<'xml> for String { impl<'xml> FromXml<'xml> for String {
fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result<Self, Error> { fn deserialize<'cx>(
Ok(<Cow<'xml, str> as FromXml<'xml>>::deserialize(deserializer)?.into_owned()) deserializer: &'cx mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
) -> Result<(), Error> {
if into.is_some() {
return Err(Error::DuplicateValue);
}
let mut value = None;
<Cow<'xml, str> 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; const KIND: Kind<'static> = Kind::Scalar;
} }
impl<'xml> FromXml<'xml> for &'xml str { impl<'xml> FromXml<'xml> for &'xml str {
fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result<Self, Error> { fn deserialize<'cx>(
Ok( deserializer: &'cx mut Deserializer<'cx, 'xml>,
match <Cow<'xml, str> as FromXml<'xml>>::deserialize(deserializer)? { into: &mut Option<Self>,
Cow::Borrowed(s) => s, ) -> Result<(), Error> {
Cow::Owned(_) => return Err(Error::UnexpectedValue), if into.is_some() {
}, return Err(Error::DuplicateValue);
) }
let mut value = None;
<Cow<'xml, str> 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; const KIND: Kind<'static> = Kind::Scalar;
} }
impl<'xml> FromXml<'xml> for Cow<'xml, str> { impl<'xml> FromXml<'xml> for Cow<'xml, str> {
fn deserialize(deserializer: &mut Deserializer<'_, 'xml>) -> Result<Self, Error> { fn deserialize(
deserializer: &mut Deserializer<'_, 'xml>,
into: &mut Option<Self>,
) -> Result<(), Error> {
if into.is_some() {
return Err(Error::DuplicateValue);
}
let value = deserializer.take_str()?; let value = deserializer.take_str()?;
Ok(decode(value)) *into = Some(decode(value));
Ok(())
} }
const KIND: Kind<'static> = Kind::Scalar; const KIND: Kind<'static> = Kind::Scalar;
} }
impl<'xml, T> FromXml<'xml> for Option<T> impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Option<T> {
where fn deserialize<'cx>(
T: FromXml<'xml>, deserializer: &'cx mut Deserializer<'cx, 'xml>,
{ into: &mut Option<Self>,
fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result<Self, Error> { ) -> Result<(), Error> {
match <T>::deserialize(deserializer) { if into.is_some() {
Ok(v) => Ok(Some(v)), return Err(Error::DuplicateValue);
Err(e) => Err(e), }
let mut value = None;
<T>::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) Cow::Owned(result)
} }
const VEC_LIST_TAG: &str = "list";
const VEC_ELEMENT_TAG: &str = "element";
impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Vec<T> { impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Vec<T> {
fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result<Self, Error> { fn deserialize<'cx>(
let mut result = Self::new(); deserializer: &'cx mut Deserializer<'cx, 'xml>,
let kind = <T as FromXml<'xml>>::KIND; into: &mut Option<Self>,
) -> Result<(), Error> {
while let Some(Ok(node)) = deserializer.next() { let mut value = None;
match (&kind, node) { T::deserialize(deserializer, &mut value)?;
(Kind::Scalar, Node::Open(data)) => { let dst = into.get_or_insert(Vec::new());
let id = deserializer.element_id(&data)?; if let Some(value) = value {
dst.push(value);
match id.name {
VEC_ELEMENT_TAG => {
let mut nested = deserializer.nested(data);
result.push(<T as FromXml<'xml>>::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(<T as FromXml<'xml>>::deserialize(&mut nested)?)
}
_ => return Err(Error::UnexpectedState("unexpected node for list")),
}
} }
Ok(result) Ok(())
} }
const KIND: Kind<'static> = Kind::Vec; fn missing_value() -> Result<Self, Error> {
Ok(Vec::new())
}
const KIND: Kind<'static> = Kind::Vec(T::KIND.element());
} }
impl<T: ToXml> ToXml for Vec<T> { impl<T: ToXml> ToXml for Vec<T> {
@ -360,35 +437,19 @@ impl<T: ToXml> ToXml for Vec<T> {
&self, &self,
serializer: &mut Serializer<W>, serializer: &mut Serializer<W>,
) -> Result<(), Error> { ) -> Result<(), Error> {
match <T as ToXml>::KIND { match T::KIND {
Kind::Element(_) => { Kind::Element(_) => {}
_ => return Err(Error::UnexpectedState("only elements allowed in `Vec`")),
}
for i in self { for i in self {
i.serialize(serializer)?; 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<Vec<T>>, 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)?;
}
}
}
Ok(()) Ok(())
} }
const KIND: Kind<'static> = Kind::Vec; const KIND: Kind<'static> = Kind::Vec(T::KIND.element());
} }
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
@ -405,10 +466,20 @@ impl ToXml for DateTime<Utc> {
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
impl<'xml> FromXml<'xml> for DateTime<Utc> { impl<'xml> FromXml<'xml> for DateTime<Utc> {
fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result<Self, Error> { fn deserialize<'cx>(
deserializer: &'cx mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
) -> Result<(), Error> {
if into.is_some() {
return Err(Error::DuplicateValue);
}
let data = deserializer.take_str()?; let data = deserializer.take_str()?;
match DateTime::parse_from_rfc3339(data) { 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())), _ => Err(Error::Other("invalid date/time".into())),
} }
} }

View File

@ -34,7 +34,10 @@ impl<'a, T: ToXml + ?Sized> ToXml for &'a T {
} }
pub trait FromXml<'xml>: Sized { pub trait FromXml<'xml>: Sized {
fn deserialize<'cx>(deserializer: &'cx mut Deserializer<'cx, 'xml>) -> Result<Self, Error>; fn deserialize<'cx>(
deserializer: &'cx mut Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
) -> Result<(), Error>;
// If the missing field is of type `Option<T>` then treat is as `None`, // If the missing field is of type `Option<T>` then treat is as `None`,
// otherwise it is an error. // otherwise it is an error.
@ -50,7 +53,7 @@ pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result<T, Error> {
let id = context.element_id(&root)?; let id = context.element_id(&root)?;
let expected = match T::KIND { let expected = match T::KIND {
Kind::Scalar => return Err(Error::UnexpectedState("found scalar as root")), 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, Kind::Element(expected) => expected,
}; };
@ -58,7 +61,12 @@ pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result<T, Error> {
return Err(Error::UnexpectedValue); 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<String, Error> { pub fn to_string(value: &(impl ToXml + ?Sized)) -> Result<String, Error> {
@ -116,15 +124,22 @@ pub enum Error {
pub enum Kind<'a> { pub enum Kind<'a> {
Scalar, Scalar,
Element(Id<'a>), Element(Id<'a>),
Vec, Vec(Id<'a>),
} }
impl<'a> Kind<'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> { pub const fn name(&self, field: Id<'a>) -> Id<'a> {
match self { match self {
Kind::Scalar => field, Kind::Scalar => field,
Kind::Element(name) => *name, Kind::Element(name) => *name,
Kind::Vec => field, Kind::Vec(inner) => *inner,
} }
} }
@ -133,7 +148,7 @@ impl<'a> Kind<'a> {
match self { match self {
Kind::Scalar => id == field, Kind::Scalar => id == field,
Kind::Element(name) => id == *name, Kind::Element(name) => id == *name,
Kind::Vec => id == field, Kind::Vec(inner) => id == *inner,
} }
} }
} }

33
instant-xml/tests/vec.rs Normal file
View File

@ -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<Foo>,
}
#[test]
fn vec() {
let val = Bar { foo: vec![] };
let xml = "<Bar></Bar>";
assert_eq!(xml, to_string(&val).unwrap());
assert_eq!(val, from_str(xml).unwrap());
let val = Bar {
foo: vec![Foo { bar: 42 }],
};
let xml = "<Bar><Foo><bar>42</bar></Foo></Bar>";
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 = "<Bar><Foo><bar>42</bar></Foo><Foo><bar>73</bar></Foo></Bar>";
assert_eq!(xml, to_string(&val).unwrap());
assert_eq!(val, from_str(xml).unwrap());
}