Remove impl FromXml for &str
This is a bit opinionated, but it helps avoid run-time panics from doing the simple thing (&str) which is wrong in the face of escaping strings. The overhead from `Cow` (both in terms of developer experience and run-time performance) seems limited enough. This also makes the next part a little easier.
This commit is contained in:
parent
4c04d71224
commit
6ea31b721f
|
@ -281,45 +281,6 @@ impl<'xml> FromXml<'xml> for String {
|
||||||
const KIND: Kind = Kind::Scalar;
|
const KIND: Kind = Kind::Scalar;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'xml> FromXml<'xml> for &'xml str {
|
|
||||||
#[inline]
|
|
||||||
fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
|
|
||||||
match field {
|
|
||||||
Some(field) => id == field,
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize<'cx>(
|
|
||||||
into: &mut Self::Accumulator,
|
|
||||||
field: &'static str,
|
|
||||||
deserializer: &mut Deserializer<'cx, 'xml>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
if into.is_some() {
|
|
||||||
return Err(Error::DuplicateValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
let value = match deserializer.take_str()? {
|
|
||||||
Some(value) => value,
|
|
||||||
None => return Ok(()),
|
|
||||||
};
|
|
||||||
|
|
||||||
match decode(value)? {
|
|
||||||
Cow::Borrowed(str) => *into = Some(str),
|
|
||||||
Cow::Owned(_) => {
|
|
||||||
return Err(Error::UnexpectedValue(format!(
|
|
||||||
"string with escape characters cannot be deserialized as &str for {field}: '{value}'",
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
type Accumulator = Option<&'xml str>;
|
|
||||||
const KIND: Kind = Kind::Scalar;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'xml, 'a> FromXml<'xml> for Cow<'a, str> {
|
impl<'xml, 'a> FromXml<'xml> for Cow<'a, str> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
|
fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
|
||||||
|
|
|
@ -2,13 +2,12 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
use similar_asserts::assert_eq;
|
use similar_asserts::assert_eq;
|
||||||
|
|
||||||
use instant_xml::{from_str, to_string, Error, FromXml, ToXml};
|
use instant_xml::{from_str, to_string, FromXml, ToXml};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, FromXml, ToXml)]
|
#[derive(Debug, PartialEq, Eq, FromXml, ToXml)]
|
||||||
#[xml(ns("URI"))]
|
#[xml(ns("URI"))]
|
||||||
struct StructSpecialEntities<'a> {
|
struct StructSpecialEntities<'a> {
|
||||||
string: String,
|
string: String,
|
||||||
str: &'a str,
|
|
||||||
#[xml(borrow)]
|
#[xml(borrow)]
|
||||||
cow: Cow<'a, str>,
|
cow: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
@ -17,26 +16,17 @@ struct StructSpecialEntities<'a> {
|
||||||
fn escape_back() {
|
fn escape_back() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
from_str(
|
from_str(
|
||||||
"<StructSpecialEntities xmlns=\"URI\"><string><>&"'adsad"</string><str>str</str><cow>str&</cow></StructSpecialEntities>"
|
"<StructSpecialEntities xmlns=\"URI\"><string><>&"'adsad"</string><cow>str&</cow></StructSpecialEntities>"
|
||||||
),
|
),
|
||||||
Ok(StructSpecialEntities {
|
Ok(StructSpecialEntities {
|
||||||
string: String::from("<>&\"'adsad\""),
|
string: String::from("<>&\"'adsad\""),
|
||||||
str: "str",
|
|
||||||
cow: Cow::Owned("str&".to_string()),
|
cow: Cow::Owned("str&".to_string()),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Wrong str char
|
|
||||||
assert_eq!(
|
|
||||||
from_str(
|
|
||||||
"<StructSpecialEntities xmlns=\"URI\"><string><>&"'adsad"</string><str>str&</str></StructSpecialEntities>"
|
|
||||||
),
|
|
||||||
Err::<StructSpecialEntities, _>(Error::UnexpectedValue("string with escape characters cannot be deserialized as &str for StructSpecialEntities::str: 'str&'".to_owned()))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Borrowed
|
// Borrowed
|
||||||
let escape_back = from_str::<StructSpecialEntities>(
|
let escape_back = from_str::<StructSpecialEntities>(
|
||||||
"<StructSpecialEntities xmlns=\"URI\"><string><>&"'adsad"</string><str>str</str><cow>str</cow></StructSpecialEntities>"
|
"<StructSpecialEntities xmlns=\"URI\"><string><>&"'adsad"</string><cow>str</cow></StructSpecialEntities>"
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -46,7 +36,7 @@ fn escape_back() {
|
||||||
|
|
||||||
// Owned
|
// Owned
|
||||||
let escape_back = from_str::<StructSpecialEntities>(
|
let escape_back = from_str::<StructSpecialEntities>(
|
||||||
"<StructSpecialEntities xmlns=\"URI\"><string><>&"'adsad"</string><str>str</str><cow>str&</cow></StructSpecialEntities>"
|
"<StructSpecialEntities xmlns=\"URI\"><string><>&"'adsad"</string><cow>str&</cow></StructSpecialEntities>"
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -60,9 +50,8 @@ fn special_entities() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
to_string(&StructSpecialEntities{
|
to_string(&StructSpecialEntities{
|
||||||
string: "&\"<>\'aa".to_string(),
|
string: "&\"<>\'aa".to_string(),
|
||||||
str: "&\"<>\'bb",
|
|
||||||
cow: Cow::from("&\"<>\'cc"),
|
cow: Cow::from("&\"<>\'cc"),
|
||||||
}).unwrap(),
|
}).unwrap(),
|
||||||
"<StructSpecialEntities xmlns=\"URI\"><string>&"<>'aa</string><str>&"<>'bb</str><cow>&"<>'cc</cow></StructSpecialEntities>",
|
"<StructSpecialEntities xmlns=\"URI\"><string>&"<>'aa</string><cow>&"<>'cc</cow></StructSpecialEntities>",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use similar_asserts::assert_eq;
|
use similar_asserts::assert_eq;
|
||||||
|
|
||||||
use instant_xml::{from_str, to_string, FromXml, ToXml};
|
use instant_xml::{from_str, to_string, FromXml, ToXml};
|
||||||
|
@ -21,12 +23,14 @@ fn option_vec() {
|
||||||
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
|
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||||
struct Bar<'a> {
|
struct Bar<'a> {
|
||||||
#[xml(attribute, borrow)]
|
#[xml(attribute, borrow)]
|
||||||
maybe: Option<&'a str>,
|
maybe: Option<Cow<'a, str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn option_borrow() {
|
fn option_borrow() {
|
||||||
let v = Bar { maybe: Some("a") };
|
let v = Bar {
|
||||||
|
maybe: Some("a".into()),
|
||||||
|
};
|
||||||
let xml = r#"<Bar maybe="a"></Bar>"#;
|
let xml = r#"<Bar maybe="a"></Bar>"#;
|
||||||
|
|
||||||
assert_eq!(xml, to_string(&v).unwrap());
|
assert_eq!(xml, to_string(&v).unwrap());
|
||||||
|
|
|
@ -8,7 +8,7 @@ use instant_xml::{from_str, FromXml, ToXml};
|
||||||
#[xml(ns("URI"))]
|
#[xml(ns("URI"))]
|
||||||
struct NestedLifetimes<'a> {
|
struct NestedLifetimes<'a> {
|
||||||
flag: bool,
|
flag: bool,
|
||||||
str_type_a: &'a str,
|
str_type_a: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, FromXml, ToXml)]
|
#[derive(Debug, PartialEq, FromXml, ToXml)]
|
||||||
|
@ -18,13 +18,13 @@ struct StructDeserializerScalars<'a, 'b> {
|
||||||
i8_type: i8,
|
i8_type: i8,
|
||||||
u32_type: u32,
|
u32_type: u32,
|
||||||
string_type: String,
|
string_type: String,
|
||||||
str_type_a: &'a str,
|
str_type_a: Cow<'a, str>,
|
||||||
str_type_b: &'b str,
|
str_type_b: Cow<'b, str>,
|
||||||
char_type: char,
|
char_type: char,
|
||||||
f32_type: f32,
|
f32_type: f32,
|
||||||
nested: NestedLifetimes<'a>,
|
nested: NestedLifetimes<'a>,
|
||||||
cow: Cow<'a, str>,
|
cow: Cow<'a, str>,
|
||||||
option: Option<&'a str>,
|
option: Option<Cow<'a, str>>,
|
||||||
slice: Cow<'a, [u8]>,
|
slice: Cow<'a, [u8]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,13 +39,13 @@ fn scalars() {
|
||||||
i8_type: 1,
|
i8_type: 1,
|
||||||
u32_type: 42,
|
u32_type: 42,
|
||||||
string_type: "string".to_string(),
|
string_type: "string".to_string(),
|
||||||
str_type_a: "lifetime a",
|
str_type_a: "lifetime a".into(),
|
||||||
str_type_b: "lifetime b",
|
str_type_b: "lifetime b".into(),
|
||||||
char_type: 'c',
|
char_type: 'c',
|
||||||
f32_type: 1.20,
|
f32_type: 1.20,
|
||||||
nested: NestedLifetimes {
|
nested: NestedLifetimes {
|
||||||
flag: true,
|
flag: true,
|
||||||
str_type_a: "asd"
|
str_type_a: "asd".into()
|
||||||
},
|
},
|
||||||
cow: Cow::from("123"),
|
cow: Cow::from("123"),
|
||||||
option: None,
|
option: None,
|
||||||
|
@ -63,16 +63,16 @@ fn scalars() {
|
||||||
i8_type: 1,
|
i8_type: 1,
|
||||||
u32_type: 42,
|
u32_type: 42,
|
||||||
string_type: "string".to_string(),
|
string_type: "string".to_string(),
|
||||||
str_type_a: "lifetime a",
|
str_type_a: "lifetime a".into(),
|
||||||
str_type_b: "lifetime b",
|
str_type_b: "lifetime b".into(),
|
||||||
char_type: 'c',
|
char_type: 'c',
|
||||||
f32_type: 1.20,
|
f32_type: 1.20,
|
||||||
nested: NestedLifetimes {
|
nested: NestedLifetimes {
|
||||||
flag: true,
|
flag: true,
|
||||||
str_type_a: "asd"
|
str_type_a: "asd".into(),
|
||||||
},
|
},
|
||||||
cow: Cow::from("123"),
|
cow: Cow::from("123"),
|
||||||
option: Some("asd"),
|
option: Some("asd".into()),
|
||||||
slice: Cow::Borrowed(&[1, 2, 3]),
|
slice: Cow::Borrowed(&[1, 2, 3]),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue