xmpp-rs/parsers/src/macro_tests/mod.rs

1251 lines
45 KiB
Rust

use std::borrow::Cow;
use std::collections::BTreeMap;
use crate::core::{
error::{DynNamespaceError, Error},
Base64, DynNamespace, DynNamespaceEnum, ElementCodec, FromXml, IntoXml,
};
use crate::Element;
static TEST_NS1: &'static str = "urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2";
static TEST_NS2: &'static str = "urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f";
static XHTML: &'static str = "http://www.w3.org/1999/xhtml";
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "req-attribute")]
pub struct ReqAttribute {
#[xml(attribute)]
pub required: String,
}
#[test]
fn req_attribute_positive() {
crate::util::test::roundtrip_full::<ReqAttribute>(
"<req-attribute xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' required='foo'/>",
);
}
#[test]
fn req_attribute_missing() {
match crate::util::test::parse_str::<ReqAttribute>(
"<req-attribute xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
) {
Err(e) if e.to_string().find("required").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "opt-attribute")]
pub struct OptionalAttribute {
#[xml(attribute)]
pub optional: Option<String>,
}
#[test]
fn optional_attribute_absent() {
crate::util::test::roundtrip_full::<OptionalAttribute>(
"<opt-attribute xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
);
}
#[test]
fn optional_attribute_present() {
crate::util::test::roundtrip_full::<OptionalAttribute>(
"<opt-attribute xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' optional='foo'/>",
);
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "attribute-codec")]
pub struct AttributeCodec {
#[xml(attribute(codec = Base64))]
pub data: Vec<u8>,
#[xml(attribute(codec = Base64, name = "optional-1", default))]
pub optional_1: Option<Vec<u8>>,
#[xml(attribute(codec = Base64, name = "optional-2", default))]
pub optional_2: Option<Vec<u8>>,
}
#[test]
fn attribute_codec_positive() {
crate::util::test::roundtrip_full::<AttributeCodec>(
"<attribute-codec xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' data='aGVsbG8gd29ybGQ=' optional-1='cXVhaw=='/>",
);
}
#[test]
fn attribute_codec_decoding() {
let v = match crate::util::test::parse_str::<AttributeCodec>(
"<attribute-codec xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' data='aGVsbG8gd29ybGQ=' optional-1='cXVhaw=='/>",
) {
Ok(v) => v,
other => panic!("unexpected result: {:?}", other),
};
assert_eq!(v.data, b"hello world");
assert_eq!(v.optional_1.as_ref().map(|x| &x[..]), Some(&b"quak"[..]));
assert_eq!(v.optional_2, None);
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "collect-all-unknown")]
pub struct CollectAllUnknown {
#[xml(elements)]
pub unknown: Vec<Element>,
#[xml(children)]
pub known: Vec<ReqAttribute>,
}
#[test]
fn collect_all_unknown_positive() {
crate::util::test::roundtrip_full::<CollectAllUnknown>("<collect-all-unknown xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><other/><req-attribute required='a'/><req-attribute required='b'/></collect-all-unknown>");
}
#[test]
fn collect_all_unknown_collect_has_lower_priority() {
let v: CollectAllUnknown = crate::util::test::parse_str("<collect-all-unknown xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><req-attribute required='a'/><req-attribute required='b'/><other/></collect-all-unknown>").unwrap();
assert_eq!(v.unknown.len(), 1);
assert_eq!(v.known.len(), 2);
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "collect-known-ns")]
pub struct CollectSpecificNamespace {
#[xml(elements(namespace = self::TEST_NS2))]
pub known_ns: Vec<Element>,
#[xml(elements)]
pub unknown: Vec<Element>,
}
#[test]
fn collect_specific_namespace_positive() {
crate::util::test::roundtrip_full::<CollectSpecificNamespace>("<collect-known-ns xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><known-ns xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/><other-ns/></collect-known-ns>");
}
#[test]
fn collect_specific_namespace_distributes_according_to_namespace() {
let v: CollectSpecificNamespace = crate::util::test::parse_str("<collect-known-ns xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><known-ns xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/><other-ns/></collect-known-ns>").unwrap();
assert_eq!(v.known_ns.len(), 1);
assert!(v.known_ns[0].is("known-ns", TEST_NS2));
assert_eq!(v.unknown.len(), 1);
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "collect-grandchildren")]
pub struct CollectGrandchildren {
#[xml(child(namespace = self::TEST_NS1, name = "extensions", extract(elements)))]
pub extensions: Vec<Element>,
}
#[test]
fn collect_grandchildren_roundtrip() {
crate::util::test::roundtrip_full::<CollectGrandchildren>("<collect-grandchildren xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><extensions><ext1 xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'><some-child with-attr='foo'/></ext1><ext2><with/><more-children/></ext2><ext3 xmlns='urn:uuid:e3069e2e-0ea2-4d9a-8848-9f36d0e06585'><one-and-done/></ext3></extensions></collect-grandchildren>");
}
#[test]
fn collect_grandchildren_detail() {
let v: CollectGrandchildren = crate::util::test::parse_str("<collect-grandchildren xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><extensions><ext1 xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'><some-child with-attr='foo'/></ext1><ext2><with/><more-children/></ext2><ext3 xmlns='urn:uuid:e3069e2e-0ea2-4d9a-8848-9f36d0e06585'><one-and-done/></ext3></extensions></collect-grandchildren>").unwrap();
assert_eq!(v.extensions.len(), 3);
assert!(v.extensions[0].is("ext1", TEST_NS2));
assert!(v.extensions[1].is("ext2", TEST_NS1));
assert!(v.extensions[2].is("ext3", "urn:uuid:e3069e2e-0ea2-4d9a-8848-9f36d0e06585"));
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "collect-specific")]
pub struct CollectSpecificElements {
#[xml(elements(namespace = self::TEST_NS2, name = "foo"))]
pub known: Vec<Element>,
#[xml(elements)]
pub unknown: Vec<Element>,
}
#[test]
fn collect_specific_elements_positive() {
crate::util::test::roundtrip_full::<CollectSpecificElements>("<collect-specific xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><foo xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/><bar xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/><other-ns/></collect-specific>");
}
#[test]
fn collect_specific_elements_distributes_according_to_qname() {
let v: CollectSpecificElements = crate::util::test::parse_str("<collect-specific xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><foo xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/><bar xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/><other-ns/></collect-specific>").unwrap();
assert_eq!(v.known.len(), 1);
assert!(v.known[0].is("foo", TEST_NS2));
assert_eq!(v.unknown.len(), 2);
assert!(v.unknown[0].is("bar", TEST_NS2));
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "generic-child")]
pub struct GenericChild {
#[xml(element(namespace = self::XHTML, name = "html"))]
pub html: Element,
}
#[test]
fn generic_child_positive() {
crate::util::test::roundtrip_full::<GenericChild>("<generic-child xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><html xmlns='http://www.w3.org/1999/xhtml'><body><h1>Hello World!</h1></body></html></generic-child>");
}
#[test]
fn generic_child_error_if_absent() {
match crate::util::test::parse_str::<GenericChild>(
"<generic-child xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
) {
Err(e) if e.to_string().find("html").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "generic-optional-child")]
pub struct GenericOptionalChild {
#[xml(element(namespace = self::XHTML, name = "html", default))]
pub html: Option<Element>,
}
#[test]
fn generic_optional_child_positive_with_child() {
crate::util::test::roundtrip_full::<GenericOptionalChild>("<generic-optional-child xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><html xmlns='http://www.w3.org/1999/xhtml'><body><h1>Hello World!</h1></body></html></generic-optional-child>");
}
#[test]
fn generic_optional_child_positive_without_child() {
crate::util::test::roundtrip_full::<GenericOptionalChild>(
"<generic-optional-child xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
);
}
#[test]
fn generic_child_default_if_absent() {
let v = match crate::util::test::parse_str::<GenericOptionalChild>(
"<generic-optional-child xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
) {
Ok(v) => v,
other => panic!("unexpected result: {:?}", other),
};
assert!(v.html.is_none());
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "child-a")]
pub struct DemoChildA {
// text data can be captured in a field, converted using FromText/IntoText
#[xml(text)]
pub value: u32,
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "child-b")]
pub struct DemoChildB {
// text data can be captured in a field, converted using FromText/IntoText
#[xml(text)]
pub value: u32,
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "showcase")]
pub struct ShowcaseStruct {
// === ATTRIBUTES ===
// attributes can be captured into fields, converted using
// FromOptionalXmlText/IntoOptionalXmlText. Any type that implements
// FromXmlText automatically implements FromOptionalXmlText.
// attribute names default to the field name
#[xml(attribute)]
pub foo: usize,
// attributes can be renamed (short form)
#[xml(attribute = "type")]
pub type_: String,
// attributes can be renamed (long form)
#[xml(attribute(name = "super"))]
pub super_: i32,
// === CHILD ELEMENTS, TYPED ===
// children implementing IntoXml/FromXml can be captured in fields
#[xml(child)]
pub demo_a: DemoChildA,
// they can also be captured into a Vec (or other type implementing
// XmlCollection)
#[xml(children)]
pub demo_b: Vec<DemoChildB>,
// === CHILD ELEMENTS, UNTYPED ===
// child elements which cannot or should not be expressed as rust structs,
// such as entire XHTML documents, can be captured into minidom elements:
#[xml(element(namespace = self::XHTML, name = "body"))]
pub html_body: Element,
// they can be made optional, but note that they aren't just optional
// because they are specified as `Option<..>`. It is required to also set
// as `default`.
// you can even `default` the field if it is not an `Option<..>`, provided
// the type implements From<Element>, Default and Into<Option<Element>>.
#[xml(element(namespace = self::XHTML, name = "title", default))]
pub html_title: Option<Element>,
// if you need to capture many elements in an unstructured manner, we got
// you covered. the type is strict though, you need to use `Vec<Element>`.
#[xml(elements(namespace = self::XHTML, name = "li"))]
pub keywords: Vec<Element>,
// if you only know the namespace, but not anything else, you can do that,
// too. again, the type is fixed.
#[xml(elements(namespace = self::XHTML))]
pub other_xhtml_litter: Vec<Element>,
// as a last resort, you can capture everything which isn't known and
// would otherwise cause an error:
#[xml(elements)]
pub garbage: Vec<Element>,
// === EXTRACTED DATA ===
// sometimes, you don't want to make a type, just to extract the text
// value from a child element. you can do that:
#[xml(child(namespace = self::TEST_NS2, name = "lucky-number", extract(text)))]
pub lucky_number: u32,
// or if you have more of these:
#[xml(children(namespace = self::TEST_NS2, name = "number", extract(text(type = u32))))]
pub numbers: Vec<u32>,
// you can't just capture text values, attributes are good, too:
#[xml(children(namespace = self::TEST_NS2, name = "image", extract(attribute = "alt")))]
pub image_alts: Vec<String>,
// and you can even capture a child's children into a vec:
#[xml(child(namespace = self::TEST_NS1, name = "extensions", extract(elements)))]
pub grandchildren: Vec<Element>,
// if you need more than one part of the child, you can do that, but it's
// restricted to cases where you collect the data into a collection which
// implements [`XmlDataCollection`]. That is currently only the case for
// BTreeMap.
// XXX: namespaced attributes don't yet properly work due to limitations
// in minidom -> you have to hope that the prefix is ok.
#[xml(children(namespace = self::TEST_NS1, name = "body", extract(attribute = "xml:lang", text)))]
pub plaintext_bodies: BTreeMap<String, String>,
// === TEXT ===
// last but not least: you can capture text content, obviously. this is
// all text inside the element (but not its children) concatenated:
// can use any type which implements FromText/IntoText.
#[xml(text)]
pub text: String,
}
#[test]
fn showcase_struct_roundtrip() {
crate::util::test::roundtrip_full::<ShowcaseStruct>("<showcase
xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'
foo='12'
type='fnord'
super='-13'
><child-a>16</child-a><child-b>0</child-b><child-b>1</child-b><body
xmlns='http://www.w3.org/1999/xhtml'><h1>Hello World!</h1>
<h2>Q: Is xmpp-parsers awesome?</h2>
<p>The answer is: yes!</p>
</body><title xmlns='http://www.w3.org/1999/xhtml'>FAQ</title><li xmlns='http://www.w3.org/1999/xhtml'>Keyword 1</li><li xmlns='http://www.w3.org/1999/xhtml'><b>Important keyword</b></li><style xmlns='http://www.w3.org/1999/xhtml'>haha nope</style><random-element xmlns='urn:uuid:e3069e2e-0ea2-4d9a-8848-9f36d0e06585'>
<is-collected-as-garbage/>
</random-element><lucky-number xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'>13</lucky-number><number xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'>42</number><number xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'>23</number><image xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' alt='some text'/><image xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' alt='other text'/><extensions><a/><b xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/><c/></extensions><body xml:lang='de'>Hallo!</body><body xml:lang='en'>Hello!</body><body xml:lang='fi'>Terve!</body>Hello World!</showcase>");
}
#[test]
fn showcase_struct_decomposition() {
let v: ShowcaseStruct = crate::util::test::parse_str("<showcase
xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'
foo='12'
type='fnord'
super='-13'
><child-a>16</child-a><child-b>0</child-b><child-b>1</child-b><body
xmlns='http://www.w3.org/1999/xhtml'><h1>Hello World!</h1>
<h2>Q: Is xmpp-parsers awesome?</h2>
<p>The answer is: yes!</p>
</body><title xmlns='http://www.w3.org/1999/xhtml'>FAQ</title><li xmlns='http://www.w3.org/1999/xhtml'>Keyword 1</li><li xmlns='http://www.w3.org/1999/xhtml'><b>Important keyword</b></li><style xmlns='http://www.w3.org/1999/xhtml'>haha nope</style><random-element xmlns='urn:uuid:e3069e2e-0ea2-4d9a-8848-9f36d0e06585'>
<is-collected-as-garbage/>
</random-element><lucky-number xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'>13</lucky-number><number xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'>42</number><number xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'>23</number><image xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' alt='some text'/><image xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' alt='other text'/><extensions><a/><b xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/><c/></extensions><body xml:lang='de'>Hallo!</body><body xml:lang='en'>Hello!</body><body xml:lang='fi'>Terve!</body>Hello World!</showcase>").unwrap();
assert_eq!(v.foo, 12);
assert_eq!(v.type_, "fnord");
assert_eq!(v.super_, -13);
assert_eq!(v.demo_a.value, 16);
assert_eq!(v.demo_b[0].value, 0);
assert_eq!(v.demo_b[1].value, 1);
assert_eq!(
v.html_body.children().next().unwrap().text(),
"Hello World!"
);
assert_eq!(v.html_title.unwrap().text(), "FAQ");
assert_eq!(v.keywords[0].text(), "Keyword 1");
assert_eq!(
v.keywords[1].children().next().unwrap().text(),
"Important keyword"
);
assert_eq!(v.other_xhtml_litter[0].text(), "haha nope");
assert!(v.garbage[0].is(
"random-element",
"urn:uuid:e3069e2e-0ea2-4d9a-8848-9f36d0e06585"
));
assert_eq!(v.lucky_number, 13);
assert_eq!(v.numbers[0], 42);
assert_eq!(v.numbers[1], 23);
assert_eq!(v.image_alts[0], "some text");
assert_eq!(v.image_alts[1], "other text");
assert!(v.grandchildren[0].is("a", TEST_NS1));
assert!(v.grandchildren[1].is("b", TEST_NS2));
assert!(v.grandchildren[2].is("c", TEST_NS1));
assert_eq!(v.plaintext_bodies["de"], "Hallo!");
assert_eq!(v.plaintext_bodies["en"], "Hello!");
assert_eq!(v.plaintext_bodies["fi"], "Terve!");
assert_eq!(v.text, "Hello World!");
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1)]
pub enum EnumFallback {
// enum variants are basically treated as structs, with the difference
// that their `namespace` is defaulted to the enum's namespace, if it is
// set.
// variants are distinguished by their XML QName. It is (at this point in
// time) not possible to "switch" on anything else, though there are ideas
// floating around for matching on an attribute value instead.
#[xml(name = "variant-1")]
Variant1 {
#[xml(attribute)]
foo: String,
},
// unit variants can be marked as `fallback` (terms & conditions apply).
// that means that any element matching the namespace of the enum (which
// must be set) but none of the names of the enum variants will be treated
// as that fallback.
// this is obviously very lossy, because it must be (as pointed out) a
// unit variant.
// in the future, it may be possible to use a non-unit variant or so.
// probably, this isn't a particularly great thing to do in the first
// place except under very specific conditions, so maybe don't overuse
// this.
#[xml(name = "variant-2", fallback)]
Variant2,
}
#[test]
fn enum_fallback_roundtrip_non_fallback() {
crate::util::test::roundtrip_full::<EnumFallback>(
"<variant-1 xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' foo='bar'/>",
);
}
#[test]
fn enum_fallback_roundtrip_fallback() {
crate::util::test::roundtrip_full::<EnumFallback>(
"<variant-2 xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
);
}
#[test]
fn enum_fallback_parse_as_fallback() {
match crate::util::test::parse_str::<EnumFallback>(
"<variant-3 xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
) {
Ok(EnumFallback::Variant2) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[test]
fn enum_fallback_matching_namespace() {
match crate::util::test::parse_str::<EnumFallback>(
"<variant-3 xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/>",
) {
Err(Error::TypeMismatch(_, _, _)) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "flag-container")]
pub struct Flag {
#[xml(flag(namespace = self::TEST_NS1, name = "flag"))]
pub is_set: bool,
}
#[test]
fn flag_roundtrip_set() {
crate::util::test::roundtrip_full::<Flag>(
"<flag-container xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><flag/></flag-container>",
);
}
#[test]
fn flag_roundtrip_unset() {
crate::util::test::roundtrip_full::<Flag>(
"<flag-container xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
);
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, exhaustive)]
pub enum EnumExhaustive {
// enum variants are basically treated as structs, with the difference
// that their `namespace` is defaulted to the enum's namespace, if it is
// set.
// variants are distinguished by their XML QName. It is (at this point in
// time) not possible to "switch" on anything else, though there are ideas
// floating around for matching on an attribute value instead.
#[xml(name = "variant-1")]
Variant1,
#[xml(name = "variant-2")]
Variant2,
}
#[test]
fn enum_exhaustive_roundtrip() {
crate::util::test::roundtrip_full::<EnumExhaustive>(
"<variant-1 xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
);
}
#[test]
fn enum_exhaustive_parse_error() {
match crate::util::test::parse_str::<EnumExhaustive>(
"<variant-3 xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
) {
Err(Error::ParseError(string)) if string.find("EnumExhaustive").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "ignore-field")]
pub struct IgnoreField {
#[xml(attribute)]
pub foo: String,
#[xml(ignore)]
pub some_data: Option<std::sync::Arc<String>>, // <- some type which is definitely not (de-)serializable.
}
#[test]
fn ignore_field_positive() {
crate::util::test::roundtrip_full::<IgnoreField>(
"<ignore-field xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' foo='bar'/>",
);
}
fn validate_test(data: &mut ValidateAndPrepare) -> Result<(), Error> {
data.actual_foo = data.foo.take();
Ok(())
}
fn prepare_test(data: &mut ValidateAndPrepare) {
data.foo = data.actual_foo.take();
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "validate-and-prepare", validate = validate_test, prepare = prepare_test)]
pub struct ValidateAndPrepare {
#[xml(attribute)]
pub foo: Option<String>,
#[xml(ignore)]
pub actual_foo: Option<String>,
}
#[test]
fn validate_and_prepare_roundtrip() {
crate::util::test::roundtrip_full::<ValidateAndPrepare>(
"<validate-and-prepare xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' foo='bar'/>",
);
}
#[test]
fn validate_and_prepare_test_effects() {
let v = crate::util::test::parse_str::<ValidateAndPrepare>(
"<validate-and-prepare xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' foo='bar'/>",
)
.unwrap();
assert_eq!(v.actual_foo.as_ref().map(|x| x.as_str()), Some("bar"));
assert_eq!(v.foo, None);
}
#[derive(Debug, Clone, PartialEq)]
pub enum NamespaceEnum {
Ns1,
Ns2,
}
impl DynNamespaceEnum for NamespaceEnum {
fn from_xml_text(s: &str) -> Result<Self, DynNamespaceError> {
if s == TEST_NS1 {
Ok(Self::Ns1)
} else if s == TEST_NS2 {
Ok(Self::Ns2)
} else {
Err(DynNamespaceError::Mismatch)
}
}
fn into_xml_text(self) -> String {
match self {
Self::Ns1 => TEST_NS1.to_string(),
Self::Ns2 => TEST_NS2.to_string(),
}
}
}
impl PartialEq<str> for NamespaceEnum {
fn eq(&self, rhs: &str) -> bool {
match self {
Self::Ns1 => rhs == TEST_NS1,
Self::Ns2 => rhs == TEST_NS2,
}
}
}
#[derive(FromXml, IntoXml, DynNamespace, PartialEq, Clone, Debug)]
#[xml(namespace = dyn, name = "dynamic-namespace")]
pub struct WithDynamicNamespace {
#[xml(namespace)]
pub namespace: NamespaceEnum,
}
#[test]
fn with_dynamic_namespace_roundtrip_1() {
crate::util::test::roundtrip_full::<WithDynamicNamespace>(
"<dynamic-namespace xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
);
}
#[test]
fn with_dynamic_namespace_roundtrip_2() {
crate::util::test::roundtrip_full::<WithDynamicNamespace>(
"<dynamic-namespace xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/>",
);
}
#[test]
fn with_dynamic_namespace_negative_wrong_ns() {
match crate::util::test::parse_str::<WithDynamicNamespace>(
"<dynamic-namespace xmlns='urn:uuid:e3069e2e-0ea2-4d9a-8848-9f36d0e06585'/>",
) {
Err(Error::TypeMismatch(..)) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[test]
fn with_dynamic_namespace_negative_wrong_name() {
match crate::util::test::parse_str::<WithDynamicNamespace>(
"<quak xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
) {
Err(Error::TypeMismatch(..)) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml()]
pub enum EnumWithDynamicNamespace {
#[xml(namespace = dyn, name = "variant-1")]
Variant1 {
#[xml(namespace)]
namespace: NamespaceEnum,
},
#[xml(namespace = dyn, name = "variant-2")]
Variant2 {
#[xml(namespace)]
namespace: NamespaceEnum,
},
}
#[test]
fn enum_with_dynamic_namespace_roundtrip_1_1() {
crate::util::test::roundtrip_full::<EnumWithDynamicNamespace>(
"<variant-1 xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
);
}
#[test]
fn enum_with_dynamic_namespace_roundtrip_1_2() {
crate::util::test::roundtrip_full::<EnumWithDynamicNamespace>(
"<variant-1 xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/>",
);
}
#[test]
fn enum_with_dynamic_namespace_roundtrip_2_1() {
crate::util::test::roundtrip_full::<EnumWithDynamicNamespace>(
"<variant-2 xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
);
}
#[test]
fn enum_with_dynamic_namespace_roundtrip_2_2() {
crate::util::test::roundtrip_full::<EnumWithDynamicNamespace>(
"<variant-2 xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/>",
);
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = dyn, name = "dynamic-extract")]
pub struct ExtractFieldWithSuper {
#[xml(namespace)]
pub namespace: NamespaceEnum,
#[xml(child(namespace = super, name = "contents", extract(text)))]
pub contents: String,
}
#[test]
fn extract_field_with_super_roundtrip_1() {
crate::util::test::roundtrip_full::<ExtractFieldWithSuper>(
"<dynamic-extract xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><contents>foo</contents></dynamic-extract>",
);
}
#[test]
fn extract_field_with_super_roundtrip_2() {
crate::util::test::roundtrip_full::<ExtractFieldWithSuper>(
"<dynamic-extract xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'><contents>foo</contents></dynamic-extract>",
);
}
#[test]
fn extract_field_with_super_negative() {
match crate::util::test::parse_str::<ExtractFieldWithSuper>(
"<dynamic-extract xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'><contents xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'>foo</contents></dynamic-extract>",
) {
Err(Error::ParseError(msg)) if msg.find("Unknown child").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[test]
fn extract_field_with_super_negative_wrong_name() {
match crate::util::test::parse_str::<ExtractFieldWithSuper>(
"<dynamic-extract xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'><quak>foo</quak></dynamic-extract>",
) {
Err(Error::ParseError(msg)) if msg.find("Unknown child").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "attr-switched-enum", attribute = "key", exhaustive)]
pub enum AttributeSwitchedEnum {
#[xml(value = "variant-1")]
Variant1 {
#[xml(attribute(name = "variant-1-attr"))]
data: String,
},
#[xml(value = "variant-2")]
Variant2 {
#[xml(attribute(name = "variant-2-attr"))]
data: String,
},
}
#[test]
fn attribute_switched_enum_roundtrip_variant_1() {
crate::util::test::roundtrip_full::<AttributeSwitchedEnum>(
"<attr-switched-enum xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' key='variant-1' variant-1-attr='data'/>",
);
}
#[test]
fn attribute_switched_enum_roundtrip_variant_2() {
crate::util::test::roundtrip_full::<AttributeSwitchedEnum>(
"<attr-switched-enum xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' key='variant-2' variant-2-attr='data'/>",
);
}
#[test]
fn attribute_switched_enum_matches_namespace() {
match crate::util::test::parse_str::<AttributeSwitchedEnum>(
"<attr-switched-enum xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' key='variant-2' variant-2-attr='data'/>",
) {
Err(Error::TypeMismatch(_, _, _)) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[test]
fn attribute_switched_enum_matches_name() {
match crate::util::test::parse_str::<AttributeSwitchedEnum>(
"<other-switched-enum xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' key='variant-2' variant-2-attr='data'/>",
) {
Err(Error::TypeMismatch(_, _, _)) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[test]
fn attribute_switched_enum_rejects_unknown_value() {
match crate::util::test::parse_str::<AttributeSwitchedEnum>(
"<attr-switched-enum xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' key='quak'/>",
) {
Err(Error::ParseError(msg)) if msg.find("This is not a").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[test]
fn attribute_switched_enum_rejects_missing_attribute() {
match crate::util::test::parse_str::<AttributeSwitchedEnum>(
"<attr-switched-enum xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' fnord='variant-2'/>",
) {
Err(Error::ParseError(msg)) if msg.find("discriminator attribute").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "attr-switched-enum-fallback", attribute = "key")]
pub enum AttributeSwitchedEnumFallback {
#[xml(value = "variant-1")]
Variant1 {
#[xml(attribute(name = "variant-1-attr"))]
data: String,
},
#[xml(value = "variant-2", fallback)]
Variant2 {
#[xml(attribute(name = "variant-2-attr"))]
data: String,
},
}
#[test]
fn attribute_switched_enum_fallback_roundtrip_variant_1() {
crate::util::test::roundtrip_full::<AttributeSwitchedEnumFallback>(
"<attr-switched-enum-fallback xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' key='variant-1' variant-1-attr='data'/>",
);
}
#[test]
fn attribute_switched_enum_fallback_roundtrip_variant_2() {
crate::util::test::roundtrip_full::<AttributeSwitchedEnumFallback>(
"<attr-switched-enum-fallback xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' key='variant-2' variant-2-attr='data'/>",
);
}
#[test]
fn attribute_switched_enum_fallback() {
match crate::util::test::parse_str::<AttributeSwitchedEnumFallback>(
"<attr-switched-enum-fallback xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' key='variant-3' variant-2-attr='data'/>",
) {
Ok(v) => assert_eq!(v, AttributeSwitchedEnumFallback::Variant2 { data: "data".to_string() }),
other => panic!("unexpected result: {:?}", other),
}
}
fn to_lower(v: &str) -> Cow<'_, str> {
let mut v = v.to_owned();
v.make_ascii_lowercase();
v.into()
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "attr-switched-enum-norm", attribute = "key", exhaustive, normalize_with = to_lower)]
pub enum AttributeSwitchedEnumNormalized {
#[xml(value = "variant-1")]
Variant1,
#[xml(value = "variant-2")]
Variant2,
}
#[test]
fn attribute_switched_enum_normalized() {
match crate::util::test::parse_str::<AttributeSwitchedEnumNormalized>(
"<attr-switched-enum-norm xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' key='VarIaNT-2'/>",
) {
Ok(v) => assert_eq!(v, AttributeSwitchedEnumNormalized::Variant2),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(element(namespace = self::TEST_NS1))]
pub struct ElementByNamespace(Element);
#[test]
fn element_by_namespace_roundtrip_1() {
crate::util::test::roundtrip_full::<ElementByNamespace>(
"<quak xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></quak>",
);
}
#[test]
fn element_by_namespace_roundtrip_2() {
crate::util::test::roundtrip_full::<ElementByNamespace>(
"<fnord xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></fnord>",
);
}
#[test]
fn element_by_namespace_negative() {
match crate::util::test::parse_str::<ElementByNamespace>(
"<quak xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></quak>",
) {
Err(Error::TypeMismatch(_, _, _)) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(element(name = "quak"))]
pub struct ElementByName(Element);
#[test]
fn element_by_name_roundtrip_1() {
crate::util::test::roundtrip_full::<ElementByName>(
"<quak xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></quak>",
);
}
#[test]
fn element_by_name_roundtrip_2() {
crate::util::test::roundtrip_full::<ElementByName>(
"<quak xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></quak>",
);
}
#[test]
fn element_by_name_negative() {
match crate::util::test::parse_str::<ElementByName>(
"<no-quak xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></no-quak>",
) {
Err(Error::TypeMismatch(_, _, _)) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(element)]
pub struct ElementTransparent(Element);
#[test]
fn element_transparent_roundtrip_1() {
crate::util::test::roundtrip_full::<ElementTransparent>(
"<quak xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></quak>",
);
}
#[test]
fn element_transparent_roundtrip_2() {
crate::util::test::roundtrip_full::<ElementTransparent>(
"<quak xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></quak>",
);
}
#[test]
fn element_transparent_roundtrip_3() {
crate::util::test::roundtrip_full::<ElementTransparent>(
"<fnord xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></fnord>",
);
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(element(namespace = self::TEST_NS1, name = "quak"))]
pub struct ElementQualified(Element);
#[test]
fn element_qualified_roundtrip() {
crate::util::test::roundtrip_full::<ElementQualified>(
"<quak xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></quak>",
);
}
#[test]
fn element_qualified_negative_namespace() {
match crate::util::test::parse_str::<ElementQualified>(
"<quak xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></quak>",
) {
Err(Error::TypeMismatch(_, _, _)) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[test]
fn element_qualified_negative_name() {
match crate::util::test::parse_str::<ElementQualified>(
"<no-quak xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' a1='v1' a2='v2'><foo/><bar xmlns='quak'/></no-quak>",
) {
Err(Error::TypeMismatch(_, _, _)) => (),
other => panic!("unexpected result: {:?}", other),
}
}
fn is_zero(v: &i8) -> bool {
*v == 0
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "skip-if")]
pub struct ChildExtractSkipIf {
#[xml(child(namespace = self::TEST_NS1, name = "foo", extract(text), default, skip_if = is_zero))]
pub value: i8,
}
#[test]
fn child_extract_skip_if_roundtrip_absent() {
crate::util::test::roundtrip_full::<ChildExtractSkipIf>(
"<skip-if xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
);
}
#[test]
fn child_extract_skip_if_roundtrip_present() {
crate::util::test::roundtrip_full::<ChildExtractSkipIf>(
"<skip-if xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><foo>10</foo></skip-if>",
);
}
fn u8_255() -> u8 {
255
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "default-func")]
pub struct DefaultFunc {
#[xml(attribute(default = u8_255))]
pub attr: u8,
#[xml(child(namespace = self::TEST_NS1, name = "value", extract(text), default = u8_255))]
pub child: u8,
}
#[test]
fn default_func_generates_values() {
let v = crate::util::test::parse_str::<DefaultFunc>(
"<default-func xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
)
.expect("parse");
assert_eq!(v.attr, 255);
assert_eq!(v.child, 255);
}
#[derive(FromXml, IntoXml, DynNamespace, PartialEq, Clone, Debug)]
#[xml(namespace = dyn, name = "outer")]
pub struct NestedDynNamespace {
#[xml(namespace)]
pub namespace: NamespaceEnum,
#[xml(child(namespace = super))]
pub inner: WithDynamicNamespace,
}
#[test]
fn nested_dyn_namespace_roundtrip_1() {
crate::util::test::roundtrip_full::<NestedDynNamespace>(
"<outer xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><dynamic-namespace/></outer>",
);
}
#[test]
fn nested_dyn_namespace_roundtrip_2() {
crate::util::test::roundtrip_full::<NestedDynNamespace>(
"<outer xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'><dynamic-namespace/></outer>",
);
}
#[test]
fn nested_dyn_namespace_negative_inner_mismatch() {
match crate::util::test::parse_str::<NestedDynNamespace>(
"<outer xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><dynamic-namespace xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/></outer>",
) {
Err(Error::ParseError(msg)) if msg.find("Unknown").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[test]
fn nested_dyn_namespace_serializes_inner_with_correct_namespace() {
let mut v1 = crate::util::test::parse_str::<NestedDynNamespace>(
"<outer xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><dynamic-namespace/></outer>",
)
.unwrap();
assert_eq!(v1.namespace, NamespaceEnum::Ns1);
assert_eq!(v1.inner.namespace, NamespaceEnum::Ns1);
v1.set_namespace(NamespaceEnum::Ns2);
let el: Element = v1.into();
let v2: NestedDynNamespace = el.try_into().unwrap();
assert_eq!(v2.namespace, NamespaceEnum::Ns2);
assert_eq!(v2.inner.namespace, NamespaceEnum::Ns2);
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "ignore-unknown-children", on_unknown_child = Ignore)]
pub struct IgnoreUnknownChildren;
#[test]
fn ignore_unknown_children_ignores_children() {
match crate::util::test::parse_str::<IgnoreUnknownChildren>(
"<ignore-unknown-children xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><foo/></ignore-unknown-children>",
) {
Ok(_) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[test]
fn ignore_unknown_children_rejects_attributes() {
match crate::util::test::parse_str::<IgnoreUnknownChildren>(
"<ignore-unknown-children xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' foo='bar'/>",
) {
Err(Error::ParseError(msg)) if msg.find("Unknown attribute").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "ignore-unknown-attributes", on_unknown_attribute = Ignore)]
pub struct IgnoreUnknownAttributes;
#[test]
fn ignore_unknown_attributes_ignores_attributes() {
match crate::util::test::parse_str::<IgnoreUnknownAttributes>(
"<ignore-unknown-attributes xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' foo='bar'/>",
) {
Ok(_) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[test]
fn ignore_unknown_attributes_rejects_children() {
match crate::util::test::parse_str::<IgnoreUnknownAttributes>(
"<ignore-unknown-attributes xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><foo/></ignore-unknown-attributes>",
) {
Err(Error::ParseError(msg)) if msg.find("Unknown child").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "single")]
pub struct SingleEl {
#[xml(text)]
pub value: String,
}
impl ElementCodec<String> for SingleEl {
fn decode(value: Self) -> String {
value.value
}
fn encode(value: String) -> Self {
Self { value }
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "multi")]
pub struct MultiEl {
#[xml(text)]
pub value: String,
}
impl ElementCodec<String> for MultiEl {
fn decode(value: Self) -> String {
value.value
}
fn encode(value: String) -> Self {
Self { value }
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "element-codec")]
pub struct ElementCodecTest {
#[xml(child(codec = SingleEl))]
pub single: String,
#[xml(children(codec = MultiEl))]
pub multi: Vec<String>,
}
#[test]
fn element_codec_roundtrip() {
match crate::util::test::parse_str::<ElementCodecTest>(
"<element-codec xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><single>text 1</single><multi>text 2</multi><multi>text 3</multi></element-codec>",
) {
Ok(_) => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS2, wrapped_with(namespace = self::TEST_NS1, name = "child-switched"))]
pub enum WrappedEnum {
#[xml(name = "variant-1")]
Variant1 {
#[xml(text)]
data: String,
},
#[xml(name = "variant-2")]
Variant2 {
#[xml(attribute)]
data: String,
},
}
#[test]
fn wrapped_enum_roundtrip_1() {
crate::util::test::roundtrip_full::<WrappedEnum>(
"<child-switched xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><variant-1 xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'>some data</variant-1></child-switched>",
);
}
#[test]
fn wrapped_enum_enum_roundtrip_2() {
crate::util::test::roundtrip_full::<WrappedEnum>(
"<child-switched xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><variant-2 xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' data='other data'/></child-switched>",
);
}
#[test]
fn wrapped_enum_does_not_leak_inner_type_mismatch_1() {
match crate::util::test::parse_str::<WrappedEnum>(
"<child-switched xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><foo/></child-switched>",
) {
Err(Error::ParseError(msg)) if msg.find("Unknown child").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[test]
fn wrapped_enum_does_not_leak_inner_type_mismatch_2() {
match crate::util::test::parse_str::<WrappedEnum>(
"<child-switched xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><foo xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'/></child-switched>",
) {
Err(Error::ParseError(msg)) if msg.find("Unknown child").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS2, name = "inner", wrapped_with(namespace = self::TEST_NS1, name = "outer"))]
pub struct WrappedStruct {
#[xml(text)]
pub data: String,
}
#[test]
fn wrapped_struct_roundtrip() {
crate::util::test::roundtrip_full::<WrappedStruct>(
"<outer xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><inner xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'>some data</inner></outer>",
);
}
#[test]
fn wrapped_struct_does_not_leak_inner_type_mismatch() {
match crate::util::test::parse_str::<WrappedEnum>(
"<child-switched xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><foo/></child-switched>",
) {
Err(Error::ParseError(msg)) if msg.find("Unknown child").is_some() => (),
other => panic!("unexpected result: {:?}", other),
}
}