Add support for Cow<'_, [T]>

This commit is contained in:
Dirkjan Ochtman 2023-02-28 14:55:44 +01:00
parent 2e7a48a212
commit a5795b9b2d
4 changed files with 75 additions and 20 deletions

View File

@ -3,7 +3,7 @@ use std::collections::{BTreeMap, VecDeque};
use xmlparser::{ElementEnd, Token, Tokenizer}; use xmlparser::{ElementEnd, Token, Tokenizer};
use crate::impls::decode; use crate::impls::{decode, CowStrAccumulator};
use crate::{Error, Id}; use crate::{Error, Id};
pub struct Deserializer<'cx, 'xml> { pub struct Deserializer<'cx, 'xml> {
@ -344,19 +344,21 @@ impl<'xml> Iterator for Context<'xml> {
} }
} }
pub fn borrow_cow_str<'xml>( pub fn borrow_cow_str<'a, 'xml: 'a>(
into: &mut Option<Cow<'xml, str>>, into: &mut CowStrAccumulator<'xml, 'a>,
_: &'static str, _: &'static str,
deserializer: &mut Deserializer<'_, 'xml>, deserializer: &mut Deserializer<'_, 'xml>,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.inner.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
} }
if let Some(value) = deserializer.take_str()? { let value = match deserializer.take_str()? {
*into = Some(decode(value)?); Some(value) => value,
None => return Ok(()),
}; };
into.inner = Some(decode(value)?);
deserializer.ignore()?; deserializer.ignore()?;
Ok(()) Ok(())
} }

View File

@ -319,11 +319,7 @@ impl<'xml> FromXml<'xml> for &'xml str {
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }
impl<'xml, 'a, T: ?Sized> FromXml<'xml> for Cow<'a, T> impl<'xml, 'a> FromXml<'xml> for Cow<'a, str> {
where
T: ToOwned,
T::Owned: FromXml<'xml>,
{
#[inline] #[inline]
fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool { fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
match field { match field {
@ -334,20 +330,65 @@ where
fn deserialize( fn deserialize(
into: &mut Self::Accumulator, into: &mut Self::Accumulator,
field: &'static str, _: &'static str,
deserializer: &mut Deserializer<'_, 'xml>, deserializer: &mut Deserializer<'_, 'xml>,
) -> Result<(), Error> { ) -> Result<(), Error> {
if into.is_some() { if into.inner.is_some() {
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
} }
let mut value = <T::Owned as FromXml<'xml>>::Accumulator::default(); let value = match deserializer.take_str()? {
T::Owned::deserialize(&mut value, field, deserializer)?; Some(value) => value,
*into = Some(Cow::Owned(value.try_done(field)?)); None => return Ok(()),
};
into.inner = Some(decode(value)?.into_owned().into());
Ok(()) Ok(())
} }
type Accumulator = Option<Cow<'a, T>>; type Accumulator = CowStrAccumulator<'xml, 'a>;
const KIND: Kind = Kind::Scalar;
}
#[derive(Default)]
pub struct CowStrAccumulator<'xml, 'a> {
pub(crate) inner: Option<Cow<'a, str>>,
marker: PhantomData<&'xml str>,
}
impl<'xml, 'a> Accumulate<Cow<'a, str>> for CowStrAccumulator<'xml, 'a> {
fn try_done(self, field: &'static str) -> Result<Cow<'a, str>, Error> {
match self.inner {
Some(inner) => Ok(inner),
None => Err(Error::MissingValue(field)),
}
}
}
// The `FromXml` implementation for `Cow<'a, [T]>` always builds a `Cow::Owned`:
// it is not possible to deserialize into a `Cow::Borrowed` because there's no
// place to store the originating slice (length only known at run-time).
impl<'xml, 'a, T: FromXml<'xml>> FromXml<'xml> for Cow<'a, [T]>
where
[T]: ToOwned<Owned = Vec<T>>,
{
#[inline]
fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
T::matches(id, field)
}
fn deserialize(
into: &mut Self::Accumulator,
field: &'static str,
deserializer: &mut Deserializer<'_, 'xml>,
) -> Result<(), Error> {
let mut value = T::Accumulator::default();
T::deserialize(&mut value, field, deserializer)?;
into.push(value.try_done(field)?);
Ok(())
}
type Accumulator = Vec<T>;
const KIND: Kind = Kind::Scalar; const KIND: Kind = Kind::Scalar;
} }

View File

@ -1,4 +1,4 @@
use std::fmt; use std::{borrow::Cow, fmt};
use thiserror::Error; use thiserror::Error;
@ -66,6 +66,15 @@ impl<T> Accumulate<Vec<T>> for Vec<T> {
} }
} }
impl<'a, T> Accumulate<Cow<'a, [T]>> for Vec<T>
where
[T]: ToOwned<Owned = Vec<T>>,
{
fn try_done(self, _: &'static str) -> Result<Cow<'a, [T]>, Error> {
Ok(Cow::Owned(self))
}
}
impl<T> Accumulate<Option<T>> for Option<T> { impl<T> Accumulate<Option<T>> for Option<T> {
fn try_done(self, _: &'static str) -> Result<Option<T>, Error> { fn try_done(self, _: &'static str) -> Result<Option<T>, Error> {
Ok(self) Ok(self)

View File

@ -25,13 +25,14 @@ struct StructDeserializerScalars<'a, 'b> {
nested: NestedLifetimes<'a>, nested: NestedLifetimes<'a>,
cow: Cow<'a, str>, cow: Cow<'a, str>,
option: Option<&'a str>, option: Option<&'a str>,
slice: Cow<'a, [u8]>,
} }
#[test] #[test]
fn scalars() { fn scalars() {
assert_eq!( assert_eq!(
from_str( from_str(
"<StructDeserializerScalars xmlns=\"URI\"><bool_type>true</bool_type><i8_type>1</i8_type><u32_type>42</u32_type><string_type>string</string_type><str_type_a>lifetime a</str_type_a><str_type_b>lifetime b</str_type_b><char_type>c</char_type><f32_type>1.20</f32_type><NestedLifetimes><flag>true</flag><str_type_a>asd</str_type_a></NestedLifetimes><cow>123</cow></StructDeserializerScalars>" "<StructDeserializerScalars xmlns=\"URI\"><bool_type>true</bool_type><i8_type>1</i8_type><u32_type>42</u32_type><string_type>string</string_type><str_type_a>lifetime a</str_type_a><str_type_b>lifetime b</str_type_b><char_type>c</char_type><f32_type>1.20</f32_type><NestedLifetimes><flag>true</flag><str_type_a>asd</str_type_a></NestedLifetimes><cow>123</cow><slice>1</slice><slice>2</slice><slice>3</slice></StructDeserializerScalars>"
), ),
Ok(StructDeserializerScalars{ Ok(StructDeserializerScalars{
bool_type: true, bool_type: true,
@ -48,13 +49,14 @@ fn scalars() {
}, },
cow: Cow::from("123"), cow: Cow::from("123"),
option: None, option: None,
slice: Cow::Borrowed(&[1, 2, 3]),
}) })
); );
// Option none // Option none
assert_eq!( assert_eq!(
from_str( from_str(
"<StructDeserializerScalars xmlns=\"URI\"><bool_type>true</bool_type><i8_type>1</i8_type><u32_type>42</u32_type><string_type>string</string_type><str_type_a>lifetime a</str_type_a><str_type_b>lifetime b</str_type_b><char_type>c</char_type><f32_type>1.2</f32_type><NestedLifetimes><flag>true</flag><str_type_a>asd</str_type_a></NestedLifetimes><cow>123</cow><option>asd</option></StructDeserializerScalars>" "<StructDeserializerScalars xmlns=\"URI\"><bool_type>true</bool_type><i8_type>1</i8_type><u32_type>42</u32_type><string_type>string</string_type><str_type_a>lifetime a</str_type_a><str_type_b>lifetime b</str_type_b><char_type>c</char_type><f32_type>1.2</f32_type><NestedLifetimes><flag>true</flag><str_type_a>asd</str_type_a></NestedLifetimes><cow>123</cow><option>asd</option><slice>1</slice><slice>2</slice><slice>3</slice></StructDeserializerScalars>"
), ),
Ok(StructDeserializerScalars{ Ok(StructDeserializerScalars{
bool_type: true, bool_type: true,
@ -71,6 +73,7 @@ fn scalars() {
}, },
cow: Cow::from("123"), cow: Cow::from("123"),
option: Some("asd"), option: Some("asd"),
slice: Cow::Borrowed(&[1, 2, 3]),
}) })
); );
} }