Move user-facing deserialization interface out of trait

This commit is contained in:
Dirkjan Ochtman 2022-09-05 12:59:30 +02:00
parent 34ab6121ff
commit bdacf27171
6 changed files with 77 additions and 97 deletions

View File

@ -30,11 +30,6 @@ pub enum FieldAttribute<'xml> {
} }
pub trait FromXml<'xml>: Sized { pub trait FromXml<'xml>: Sized {
fn from_xml(input: &'xml str) -> Result<Self, Error> {
let mut deserializer = Deserializer::new(input);
Self::deserialize(&mut deserializer)
}
fn deserialize(deserializer: &mut Deserializer<'xml>) -> Result<Self, Error>; fn deserialize(deserializer: &mut Deserializer<'xml>) -> Result<Self, 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`,
@ -46,6 +41,10 @@ pub trait FromXml<'xml>: Sized {
const KIND: Kind; const KIND: Kind;
} }
pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result<T, Error> {
T::deserialize(&mut Deserializer::new(input))
}
pub enum Kind { pub enum Kind {
Scalar, Scalar,
Element(Id<'static>), Element(Id<'static>),

View File

@ -1,6 +1,6 @@
use similar_asserts::assert_eq; use similar_asserts::assert_eq;
use instant_xml::{Error, FromXml}; use instant_xml::{from_str, Error, FromXml};
#[derive(Debug, Eq, PartialEq, FromXml)] #[derive(Debug, Eq, PartialEq, FromXml)]
#[xml(ns("URI"))] #[xml(ns("URI"))]
@ -13,28 +13,23 @@ struct StructDirectNamespace {
fn direct_namespaces() { fn direct_namespaces() {
// Correct direct namespace // Correct direct namespace
assert_eq!( assert_eq!(
StructDirectNamespace::from_xml( from_str(
"<StructDirectNamespace xmlns=\"URI\"><flag xmlns=\"BAZ\">true</flag></StructDirectNamespace>" "<StructDirectNamespace xmlns=\"URI\"><flag xmlns=\"BAZ\">true</flag></StructDirectNamespace>"
) ),
.unwrap(), Ok(StructDirectNamespace { flag: true })
StructDirectNamespace { flag: true }
); );
// Wrong direct namespace // Wrong direct namespace
assert_eq!( assert_eq!(
StructDirectNamespace::from_xml( from_str(
"<StructDirectNamespace xmlns=\"URI\"><flag xmlns=\"WRONG\">true</flag></StructDirectNamespace>" "<StructDirectNamespace xmlns=\"URI\"><flag xmlns=\"WRONG\">true</flag></StructDirectNamespace>"
) ),
.unwrap_err(), Err::<StructDirectNamespace, _>(Error::MissingValue)
Error::MissingValue
); );
// Wrong direct namespace - missing namespace // Wrong direct namespace - missing namespace
assert_eq!( assert_eq!(
StructDirectNamespace::from_xml( from_str("<StructDirectNamespace xmlns=\"URI\"><flag>true</flag></StructDirectNamespace>"),
"<StructDirectNamespace xmlns=\"URI\"><flag>true</flag></StructDirectNamespace>" Err::<StructDirectNamespace, _>(Error::MissingValue)
)
.unwrap_err(),
Error::MissingValue
); );
} }

View File

@ -1,6 +1,6 @@
use similar_asserts::assert_eq; use similar_asserts::assert_eq;
use instant_xml::FromXml; use instant_xml::{from_str, FromXml};
#[derive(Debug, Eq, PartialEq, FromXml)] #[derive(Debug, Eq, PartialEq, FromXml)]
#[xml(ns("URI", bar = "BAZ"))] #[xml(ns("URI", bar = "BAZ"))]
@ -22,7 +22,7 @@ struct StructWithCustomFieldFromXml {
#[test] #[test]
fn struct_with_custom_field_from_xml() { fn struct_with_custom_field_from_xml() {
assert_eq!( assert_eq!(
StructWithCustomFieldFromXml::from_xml("<StructWithCustomFieldFromXml flag_attribute=\"true\" xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\"><bar:flag>false</bar:flag><NestedDe><bar:flag>true</bar:flag></NestedDe></StructWithCustomFieldFromXml>").unwrap(), from_str::<StructWithCustomFieldFromXml>("<StructWithCustomFieldFromXml flag_attribute=\"true\" xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\"><bar:flag>false</bar:flag><NestedDe><bar:flag>true</bar:flag></NestedDe></StructWithCustomFieldFromXml>").unwrap(),
StructWithCustomFieldFromXml { StructWithCustomFieldFromXml {
flag: false, flag: false,
flag_attribute: true, flag_attribute: true,
@ -31,7 +31,7 @@ fn struct_with_custom_field_from_xml() {
); );
// Different order // Different order
assert_eq!( assert_eq!(
StructWithCustomFieldFromXml::from_xml("<StructWithCustomFieldFromXml xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\" flag_attribute=\"true\"><NestedDe><bar:flag>true</bar:flag></NestedDe><bar:flag>false</bar:flag></StructWithCustomFieldFromXml>").unwrap(), from_str::<StructWithCustomFieldFromXml>("<StructWithCustomFieldFromXml xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\" flag_attribute=\"true\"><NestedDe><bar:flag>true</bar:flag></NestedDe><bar:flag>false</bar:flag></StructWithCustomFieldFromXml>").unwrap(),
StructWithCustomFieldFromXml { StructWithCustomFieldFromXml {
flag: false, flag: false,
flag_attribute: true, flag_attribute: true,
@ -41,7 +41,7 @@ fn struct_with_custom_field_from_xml() {
// Different prefixes then in definition // Different prefixes then in definition
assert_eq!( assert_eq!(
StructWithCustomFieldFromXml::from_xml("<StructWithCustomFieldFromXml flag_attribute=\"true\" xmlns=\"URI\" xmlns:grr=\"BAZ\" xmlns:foo=\"BAR\"><grr:flag>false</grr:flag><NestedDe><grr:flag>true</grr:flag></NestedDe></StructWithCustomFieldFromXml>").unwrap(), from_str::<StructWithCustomFieldFromXml>("<StructWithCustomFieldFromXml flag_attribute=\"true\" xmlns=\"URI\" xmlns:grr=\"BAZ\" xmlns:foo=\"BAR\"><grr:flag>false</grr:flag><NestedDe><grr:flag>true</grr:flag></NestedDe></StructWithCustomFieldFromXml>").unwrap(),
StructWithCustomFieldFromXml { StructWithCustomFieldFromXml {
flag: false, flag: false,
flag_attribute: true, flag_attribute: true,
@ -50,7 +50,7 @@ fn struct_with_custom_field_from_xml() {
); );
assert_eq!( assert_eq!(
NestedDe::from_xml( from_str::<NestedDe>(
"<NestedDe xmlns=\"URI\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedDe>" "<NestedDe xmlns=\"URI\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedDe>"
) )
.unwrap(), .unwrap(),

View File

@ -1,6 +1,6 @@
use similar_asserts::assert_eq; use similar_asserts::assert_eq;
use instant_xml::{Error, FromXml}; use instant_xml::{from_str, Error, FromXml};
#[derive(Debug, Eq, PartialEq, FromXml)] #[derive(Debug, Eq, PartialEq, FromXml)]
struct NestedWrongNamespace { struct NestedWrongNamespace {
@ -30,52 +30,48 @@ struct StructWithWrongNestedNamespace {
fn default_namespaces() { fn default_namespaces() {
// Default namespace not-nested // Default namespace not-nested
assert_eq!( assert_eq!(
NestedDe::from_xml( from_str("<NestedDe xmlns=\"URI\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedDe>"),
"<NestedDe xmlns=\"URI\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedDe>" Ok(NestedDe { flag: true })
)
.unwrap(),
NestedDe { flag: true }
); );
// Default namespace not-nested - wrong namespace // Default namespace not-nested - wrong namespace
assert_eq!( assert_eq!(
NestedDe::from_xml( from_str(
"<NestedDe xmlns=\"WRONG\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedDe>" "<NestedDe xmlns=\"WRONG\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedDe>"
) ),
.unwrap_err(), Err::<NestedDe, _>(Error::WrongNamespace)
Error::WrongNamespace
); );
// Correct child namespace // Correct child namespace
assert_eq!( assert_eq!(
StructWithCorrectNestedNamespace::from_xml("<StructWithCorrectNestedNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><NestedDe xmlns=\"URI\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedDe></StructWithCorrectNestedNamespace>").unwrap(), from_str("<StructWithCorrectNestedNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><NestedDe xmlns=\"URI\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedDe></StructWithCorrectNestedNamespace>"),
StructWithCorrectNestedNamespace { Ok(StructWithCorrectNestedNamespace {
test: NestedDe { flag: true } test: NestedDe { flag: true }
} })
); );
// Correct child namespace - without child redefinition // Correct child namespace - without child redefinition
assert_eq!( assert_eq!(
StructWithCorrectNestedNamespace::from_xml("<StructWithCorrectNestedNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><NestedDe><bar:flag>true</bar:flag></NestedDe></StructWithCorrectNestedNamespace>").unwrap(), from_str("<StructWithCorrectNestedNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><NestedDe><bar:flag>true</bar:flag></NestedDe></StructWithCorrectNestedNamespace>"),
StructWithCorrectNestedNamespace { Ok(StructWithCorrectNestedNamespace {
test: NestedDe { flag: true } test: NestedDe { flag: true }
} })
); );
// Different child namespace // Different child namespace
assert_eq!( assert_eq!(
StructWithWrongNestedNamespace::from_xml("<StructWithWrongNestedNamespace xmlns=\"URI\" xmlns:dar=\"BAZ\"><NestedWrongNamespace xmlns=\"\"><flag>true</flag></NestedWrongNamespace></StructWithWrongNestedNamespace>").unwrap(), from_str("<StructWithWrongNestedNamespace xmlns=\"URI\" xmlns:dar=\"BAZ\"><NestedWrongNamespace xmlns=\"\"><flag>true</flag></NestedWrongNamespace></StructWithWrongNestedNamespace>"),
StructWithWrongNestedNamespace { Ok(StructWithWrongNestedNamespace {
test: NestedWrongNamespace { test: NestedWrongNamespace {
flag: true flag: true
} }
} })
); );
// Wrong child namespace // Wrong child namespace
assert_eq!( assert_eq!(
StructWithWrongNestedNamespace::from_xml("<StructWithWrongNestedNamespace xmlns=\"URI\" xmlns:dar=\"BAZ\"><NestedWrongNamespace><flag>true</flag></NestedWrongNamespace></StructWithWrongNestedNamespace>").unwrap_err(), from_str("<StructWithWrongNestedNamespace xmlns=\"URI\" xmlns:dar=\"BAZ\"><NestedWrongNamespace><flag>true</flag></NestedWrongNamespace></StructWithWrongNestedNamespace>"),
Error::MissingValue Err::<StructWithWrongNestedNamespace, _>(Error::MissingValue)
); );
} }
@ -96,72 +92,65 @@ struct StructOtherNamespace {
fn other_namespaces() { fn other_namespaces() {
// Other namespace not-nested // Other namespace not-nested
assert_eq!( assert_eq!(
NestedOtherNamespace::from_xml( from_str(
"<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedOtherNamespace>" "<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedOtherNamespace>"
) ),
.unwrap(), Ok(NestedOtherNamespace { flag: true })
NestedOtherNamespace { flag: true }
); );
// Other namespace not-nested - wrong defined namespace // Other namespace not-nested - wrong defined namespace
assert_eq!( assert_eq!(
NestedOtherNamespace::from_xml( from_str(
"<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><wrong:flag>true</wrong:flag></NestedOtherNamespace>" "<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><wrong:flag>true</wrong:flag></NestedOtherNamespace>"
) ),
.unwrap_err(), Err::<NestedOtherNamespace, _>(Error::WrongNamespace)
Error::WrongNamespace
); );
// Other namespace not-nested - wrong parser namespace // Other namespace not-nested - wrong parser namespace
assert_eq!( assert_eq!(
NestedOtherNamespace::from_xml( from_str(
"<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"WRONG\"><bar:flag>true</bar:flag></NestedOtherNamespace>" "<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"WRONG\"><bar:flag>true</bar:flag></NestedOtherNamespace>"
) ),
.unwrap_err(), Err::<NestedOtherNamespace, _>(Error::MissingValue)
Error::MissingValue
); );
// Other namespace not-nested - missing parser prefix // Other namespace not-nested - missing parser prefix
assert_eq!( assert_eq!(
NestedOtherNamespace::from_xml( from_str(
"<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAR\"><flag>true</flag></NestedOtherNamespace>" "<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAR\"><flag>true</flag></NestedOtherNamespace>"
) ),
.unwrap_err(), Err::<NestedOtherNamespace, _>(Error::MissingValue)
Error::MissingValue
); );
// Correct child other namespace // Correct child other namespace
assert_eq!( assert_eq!(
StructOtherNamespace::from_xml( from_str(
"<StructOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedOtherNamespace></StructOtherNamespace>" "<StructOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><bar:flag>true</bar:flag></NestedOtherNamespace></StructOtherNamespace>"
) ),
.unwrap(), Ok(StructOtherNamespace {
StructOtherNamespace {
test: NestedOtherNamespace { test: NestedOtherNamespace {
flag: true, flag: true,
} }
} })
); );
// Correct child other namespace - without child redefinition // Correct child other namespace - without child redefinition
assert_eq!( assert_eq!(
StructOtherNamespace::from_xml( from_str(
"<StructOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><NestedOtherNamespace><bar:flag>true</bar:flag></NestedOtherNamespace></StructOtherNamespace>" "<StructOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><NestedOtherNamespace><bar:flag>true</bar:flag></NestedOtherNamespace></StructOtherNamespace>"
) ),
.unwrap(), Ok(StructOtherNamespace {
StructOtherNamespace {
test: NestedOtherNamespace { test: NestedOtherNamespace {
flag: true, flag: true,
} }
} })
); );
// Wrong child other namespace - without child redefinition // Wrong child other namespace - without child redefinition
assert_eq!( assert_eq!(
StructOtherNamespace::from_xml( from_str(
"<StructOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><NestedOtherNamespace><wrong:flag>true</wrong:flag></NestedOtherNamespace></StructOtherNamespace>" "<StructOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAZ\"><NestedOtherNamespace><wrong:flag>true</wrong:flag></NestedOtherNamespace></StructOtherNamespace>"
) ),
.unwrap_err(), Err::<StructOtherNamespace, _>(Error::WrongNamespace)
Error::WrongNamespace
); );
} }

View File

@ -2,7 +2,7 @@ use std::borrow::Cow;
use similar_asserts::assert_eq; use similar_asserts::assert_eq;
use instant_xml::{Error, FromXml, ToXml}; use instant_xml::{from_str, Error, FromXml, ToXml};
#[derive(Debug, PartialEq, Eq, FromXml, ToXml)] #[derive(Debug, PartialEq, Eq, FromXml, ToXml)]
#[xml(ns("URI"))] #[xml(ns("URI"))]
@ -15,28 +15,26 @@ struct StructSpecialEntities<'a> {
#[test] #[test]
fn escape_back() { fn escape_back() {
assert_eq!( assert_eq!(
StructSpecialEntities::from_xml( from_str(
"<StructSpecialEntities xmlns=\"URI\"><string>&lt;&gt;&amp;&quot;&apos;adsad&quot;</string><str>str</str><cow>str&amp;</cow></StructSpecialEntities>" "<StructSpecialEntities xmlns=\"URI\"><string>&lt;&gt;&amp;&quot;&apos;adsad&quot;</string><str>str</str><cow>str&amp;</cow></StructSpecialEntities>"
) ),
.unwrap(), Ok(StructSpecialEntities {
StructSpecialEntities {
string: String::from("<>&\"'adsad\""), string: String::from("<>&\"'adsad\""),
str: "str", str: "str",
cow: Cow::Owned("str&".to_string()), cow: Cow::Owned("str&".to_string()),
} })
); );
// Wrong str char // Wrong str char
assert_eq!( assert_eq!(
StructSpecialEntities::from_xml( from_str(
"<StructSpecialEntities xmlns=\"URI\"><string>&lt;&gt;&amp;&quot;&apos;adsad&quot;</string><str>str&amp;</str></StructSpecialEntities>" "<StructSpecialEntities xmlns=\"URI\"><string>&lt;&gt;&amp;&quot;&apos;adsad&quot;</string><str>str&amp;</str></StructSpecialEntities>"
) ),
.unwrap_err(), Err::<StructSpecialEntities, _>(Error::Other("Unsupported char: str&".to_string()))
Error::Other("Unsupported char: str&".to_string())
); );
// Borrowed // Borrowed
let escape_back = StructSpecialEntities::from_xml( let escape_back = from_str::<StructSpecialEntities>(
"<StructSpecialEntities xmlns=\"URI\"><string>&lt;&gt;&amp;&quot;&apos;adsad&quot;</string><str>str</str><cow>str</cow></StructSpecialEntities>" "<StructSpecialEntities xmlns=\"URI\"><string>&lt;&gt;&amp;&quot;&apos;adsad&quot;</string><str>str</str><cow>str</cow></StructSpecialEntities>"
) )
.unwrap(); .unwrap();
@ -46,7 +44,7 @@ fn escape_back() {
} }
// Owned // Owned
let escape_back = StructSpecialEntities::from_xml( let escape_back = from_str::<StructSpecialEntities>(
"<StructSpecialEntities xmlns=\"URI\"><string>&lt;&gt;&amp;&quot;&apos;adsad&quot;</string><str>str</str><cow>str&amp;</cow></StructSpecialEntities>" "<StructSpecialEntities xmlns=\"URI\"><string>&lt;&gt;&amp;&quot;&apos;adsad&quot;</string><str>str</str><cow>str&amp;</cow></StructSpecialEntities>"
) )
.unwrap(); .unwrap();

View File

@ -2,7 +2,7 @@ use std::borrow::Cow;
use similar_asserts::assert_eq; use similar_asserts::assert_eq;
use instant_xml::{FromXml, ToXml}; use instant_xml::{from_str, FromXml, ToXml};
#[derive(Debug, PartialEq, Eq, FromXml, ToXml)] #[derive(Debug, PartialEq, Eq, FromXml, ToXml)]
#[xml(ns("URI"))] #[xml(ns("URI"))]
@ -30,11 +30,10 @@ struct StructDeserializerScalars<'a, 'b> {
#[test] #[test]
fn scalars() { fn scalars() {
assert_eq!( assert_eq!(
StructDeserializerScalars::from_xml( 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></StructDeserializerScalars>"
) ),
.unwrap(), Ok(StructDeserializerScalars{
StructDeserializerScalars{
bool_type: true, bool_type: true,
i8_type: 1, i8_type: 1,
u32_type: 42, u32_type: 42,
@ -49,15 +48,15 @@ fn scalars() {
}, },
cow: Cow::from("123"), cow: Cow::from("123"),
option: None, option: None,
} })
); );
// Option none // Option none
assert_eq!( assert_eq!(
StructDeserializerScalars::from_xml( 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></StructDeserializerScalars>"
).unwrap(), ),
StructDeserializerScalars{ Ok(StructDeserializerScalars{
bool_type: true, bool_type: true,
i8_type: 1, i8_type: 1,
u32_type: 42, u32_type: 42,
@ -72,6 +71,6 @@ fn scalars() {
}, },
cow: Cow::from("123"), cow: Cow::from("123"),
option: Some("asd"), option: Some("asd"),
} })
); );
} }