Escape entities in context iterator

This commit is contained in:
Dirkjan Ochtman 2023-10-25 17:35:19 +02:00
parent efdf9334c7
commit edad589294
3 changed files with 32 additions and 25 deletions

View File

@ -63,7 +63,7 @@ fn deserialize_scalar_enum(
}; };
let serialize_as = meta.serialize_as; let serialize_as = meta.serialize_as;
variants.extend(quote!(Some(#serialize_as) => #ident::#v_ident,)); variants.extend(quote!(#serialize_as => #ident::#v_ident,));
} }
let generics = meta.xml_generics(BTreeSet::new()); let generics = meta.xml_generics(BTreeSet::new());
@ -92,14 +92,18 @@ fn deserialize_scalar_enum(
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
} }
let value = match deserializer.take_str()? { let cow_str = match deserializer.take_str()? {
#variants Some(val) => val,
Some(val) => return Err(Error::UnexpectedValue(
format!("enum variant not found for '{}'", val)
)),
None => return Err(Error::MissingValue(#type_str)), None => return Err(Error::MissingValue(#type_str)),
}; };
let value = match cow_str.as_ref() {
#variants
_ => return Err(Error::UnexpectedValue(
format!("enum variant not found for '{}'", cow_str),
)),
};
*into = Some(value); *into = Some(value);
Ok(()) Ok(())
} }

View File

@ -31,7 +31,7 @@ impl<'cx, 'xml> Deserializer<'cx, 'xml> {
} }
} }
pub fn take_str(&mut self) -> Result<Option<&'xml str>, Error> { pub fn take_str(&mut self) -> Result<Option<Cow<'xml, str>>, Error> {
loop { loop {
match self.next() { match self.next() {
Some(Ok(Node::AttributeValue(s))) => return Ok(Some(s)), Some(Ok(Node::AttributeValue(s))) => return Ok(Some(s)),
@ -321,18 +321,23 @@ impl<'xml> Iterator for Context<'xml> {
} }
} }
} else { } else {
let value = match decode(value.as_str()) {
Ok(value) => value,
Err(e) => return Some(Err(e)),
};
self.records.push_back(Node::Attribute(Attribute { self.records.push_back(Node::Attribute(Attribute {
prefix: match prefix.is_empty() { prefix: match prefix.is_empty() {
true => None, true => None,
false => Some(prefix.as_str()), false => Some(prefix.as_str()),
}, },
local: local.as_str(), local: local.as_str(),
value: value.as_str(), value,
})); }));
} }
} }
Ok(Token::Text { text }) => { Ok(Token::Text { text }) => {
return Some(Ok(Node::Text(text.as_str()))); return Some(decode(text.as_str()).map(Node::Text));
} }
Ok(Token::Declaration { .. }) => match self.stack.is_empty() { Ok(Token::Declaration { .. }) => match self.stack.is_empty() {
false => return Some(Err(Error::UnexpectedToken(format!("{token:?}")))), false => return Some(Err(Error::UnexpectedToken(format!("{token:?}")))),
@ -354,12 +359,11 @@ pub fn borrow_cow_str<'a, 'xml: 'a>(
return Err(Error::DuplicateValue); return Err(Error::DuplicateValue);
} }
let value = match deserializer.take_str()? { match deserializer.take_str()? {
Some(value) => value, Some(value) => into.inner = Some(value),
None => return Ok(()), None => return Ok(()),
}; };
into.inner = Some(decode(value)?);
deserializer.ignore()?; deserializer.ignore()?;
Ok(()) Ok(())
} }
@ -374,7 +378,7 @@ pub fn borrow_cow_slice_u8<'xml>(
} }
if let Some(value) = deserializer.take_str()? { if let Some(value) = deserializer.take_str()? {
*into = Some(match decode(value)? { *into = Some(match value {
Cow::Borrowed(v) => Cow::Borrowed(v.as_bytes()), Cow::Borrowed(v) => Cow::Borrowed(v.as_bytes()),
Cow::Owned(v) => Cow::Owned(v.into_bytes()), Cow::Owned(v) => Cow::Owned(v.into_bytes()),
}); });
@ -384,7 +388,7 @@ pub fn borrow_cow_slice_u8<'xml>(
Ok(()) Ok(())
} }
pub(crate) fn decode(input: &str) -> Result<Cow<'_, str>, Error> { fn decode(input: &str) -> Result<Cow<'_, str>, Error> {
let mut result = String::with_capacity(input.len()); let mut result = String::with_capacity(input.len());
let (mut state, mut last_end) = (DecodeState::Normal, 0); let (mut state, mut last_end) = (DecodeState::Normal, 0);
for (i, &b) in input.as_bytes().iter().enumerate() { for (i, &b) in input.as_bytes().iter().enumerate() {
@ -489,12 +493,12 @@ fn valid_xml_character(c: &char) -> bool {
#[derive(Debug)] #[derive(Debug)]
pub enum Node<'xml> { pub enum Node<'xml> {
Attribute(Attribute<'xml>), Attribute(Attribute<'xml>),
AttributeValue(&'xml str), AttributeValue(Cow<'xml, str>),
Close { Close {
prefix: Option<&'xml str>, prefix: Option<&'xml str>,
local: &'xml str, local: &'xml str,
}, },
Text(&'xml str), Text(Cow<'xml, str>),
Open(Element<'xml>), Open(Element<'xml>),
} }
@ -519,7 +523,7 @@ struct Level<'xml> {
pub struct Attribute<'xml> { pub struct Attribute<'xml> {
pub prefix: Option<&'xml str>, pub prefix: Option<&'xml str>,
pub local: &'xml str, pub local: &'xml str,
pub value: &'xml str, pub value: Cow<'xml, str>,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -8,7 +8,6 @@ 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::de::decode;
use crate::{Accumulate, Deserializer, Error, FromXml, Id, Kind, Serializer, ToXml}; use crate::{Accumulate, Deserializer, Error, FromXml, Id, Kind, Serializer, ToXml};
// Deserializer // Deserializer
@ -26,7 +25,7 @@ pub fn from_xml_str<T: FromStr>(
None => return Ok(()), None => return Ok(()),
}; };
match T::from_str(value) { match T::from_str(value.as_ref()) {
Ok(value) => { Ok(value) => {
*into = Some(value); *into = Some(value);
Ok(()) Ok(())
@ -63,7 +62,7 @@ impl<'xml, T: FromStr> FromXml<'xml> for FromXmlStr<T> {
None => return Ok(()), None => return Ok(()),
}; };
match T::from_str(value) { match T::from_str(value.as_ref()) {
Ok(value) => { Ok(value) => {
*into = Some(FromXmlStr(value)); *into = Some(FromXmlStr(value));
Ok(()) Ok(())
@ -102,7 +101,7 @@ impl<'xml> FromXml<'xml> for bool {
None => return Ok(()), None => return Ok(()),
}; };
let value = match value { let value = match value.as_ref() {
"true" | "1" => true, "true" | "1" => true,
"false" | "0" => false, "false" | "0" => false,
val => { val => {
@ -271,7 +270,7 @@ impl<'xml> FromXml<'xml> for String {
} }
match deserializer.take_str()? { match deserializer.take_str()? {
Some(value) => *into = Some(decode(value)?.into_owned()), Some(value) => *into = Some(value.into_owned()),
None => return Ok(()), None => return Ok(()),
} }
@ -305,7 +304,7 @@ impl<'xml, 'a> FromXml<'xml> for Cow<'a, str> {
None => return Ok(()), None => return Ok(()),
}; };
into.inner = Some(decode(value)?.into_owned().into()); into.inner = Some(value.into_owned().into());
Ok(()) Ok(())
} }
@ -608,7 +607,7 @@ impl<'xml> FromXml<'xml> for DateTime<Utc> {
None => return Ok(()), None => return Ok(()),
}; };
match DateTime::parse_from_rfc3339(value) { match DateTime::parse_from_rfc3339(value.as_ref()) {
Ok(dt) if dt.timezone().utc_minus_local() == 0 => { Ok(dt) if dt.timezone().utc_minus_local() == 0 => {
*into = Some(dt.with_timezone(&Utc)); *into = Some(dt.with_timezone(&Utc));
Ok(()) Ok(())
@ -670,7 +669,7 @@ impl<'xml> FromXml<'xml> for NaiveDate {
None => return Ok(()), None => return Ok(()),
}; };
match NaiveDate::parse_from_str(value, "%Y-%m-%d") { match NaiveDate::parse_from_str(value.as_ref(), "%Y-%m-%d") {
Ok(d) => { Ok(d) => {
*into = Some(d); *into = Some(d);
Ok(()) Ok(())