// Copyright (c) 2024 Jonas Schäfer // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #![deny( non_camel_case_types, non_snake_case, unsafe_code, unused_variables, unused_mut, dead_code )] mod helpers { // we isolate the helpers into a module, because we do not want to have // them in scope below. // this is to ensure that the macros do not have hidden dependencies on // any specific names being imported. use minidom::Element; use xso::{error::FromElementError, transform, try_from_element, AsXml, FromXml}; pub(super) fn roundtrip_full( s: &str, ) { let initial: Element = s.parse().unwrap(); let structural: T = match try_from_element(initial.clone()) { Ok(v) => v, Err(e) => panic!("failed to parse from {:?}: {}", s, e), }; let recovered = transform(structural.clone()).expect("roundtrip did not produce an element"); assert_eq!(initial, recovered); let structural2: T = match try_from_element(recovered) { Ok(v) => v, Err(e) => panic!("failed to parse from serialisation of {:?}: {}", s, e), }; assert_eq!(structural, structural2); } pub(super) fn parse_str(s: &str) -> Result { let initial: Element = s.parse().unwrap(); try_from_element(initial) } } use self::helpers::{parse_str, roundtrip_full}; use xso::{AsXml, FromXml}; // these are adverserial local names in order to trigger any issues with // unqualified names in the macro expansions. #[allow(dead_code, non_snake_case)] fn Err() {} #[allow(dead_code, non_snake_case)] fn Ok() {} #[allow(dead_code, non_snake_case)] fn Some() {} #[allow(dead_code, non_snake_case)] fn None() {} #[allow(dead_code)] type Option = ((),); #[allow(dead_code)] type Result = ((),); static NS1: &str = "urn:example:ns1"; static NS2: &str = "urn:example:ns2"; static FOO_NAME: &::xso::exports::rxml::strings::NcNameStr = { #[allow(unsafe_code)] unsafe { ::xso::exports::rxml::strings::NcNameStr::from_str_unchecked("foo") } }; static BAR_NAME: &::xso::exports::rxml::strings::NcNameStr = { #[allow(unsafe_code)] unsafe { ::xso::exports::rxml::strings::NcNameStr::from_str_unchecked("bar") } }; #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "foo")] struct Empty; #[test] fn empty_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[test] fn empty_name_mismatch() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Err(xso::error::FromElementError::Mismatch(..)) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn empty_namespace_mismatch() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Err(xso::error::FromElementError::Mismatch(..)) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn empty_unexpected_attribute() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Err(xso::error::FromElementError::Invalid(xso::error::Error::Other(e))) => { assert_eq!(e, "Unknown attribute in Empty element."); } other => panic!("unexpected result: {:?}", other), } } #[test] fn empty_unexpected_child() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Err(xso::error::FromElementError::Invalid(xso::error::Error::Other(e))) => { assert_eq!(e, "Unknown child in Empty element."); } other => panic!("unexpected result: {:?}", other), } } #[test] fn empty_qname_check_has_precedence_over_attr_check() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Err(xso::error::FromElementError::Mismatch(..)) => (), other => panic!("unexpected result: {:?}", other), } } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = BAR_NAME)] struct NamePath; #[test] fn name_path_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = "urn:example:ns2", name = "baz")] struct NamespaceLit; #[test] fn namespace_lit_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct RequiredAttribute { #[xml(attribute)] foo: String, } #[test] fn required_attribute_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[test] fn required_attribute_positive() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; let data = parse_str::("").unwrap(); assert_eq!(data.foo, "bar"); } #[test] fn required_attribute_missing() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Err(::xso::error::FromElementError::Invalid(::xso::error::Error::Other(e))) if e.contains("Required attribute field") && e.contains("missing") => { () } other => panic!("unexpected result: {:?}", other), } } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct RenamedAttribute { #[xml(attribute = "a1")] foo: String, #[xml(attribute = BAR_NAME)] bar: String, } #[test] fn renamed_attribute_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct NamespacedAttribute { #[xml(attribute(namespace = "urn:example:ns1", name = FOO_NAME))] foo_1: String, #[xml(attribute(namespace = NS2, name = "foo"))] foo_2: String, #[xml(attribute(namespace = NS1, name = BAR_NAME))] bar_1: String, #[xml(attribute(namespace = "urn:example:ns2", name = "bar"))] bar_2: String, } #[test] fn namespaced_attribute_roundtrip_a() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ); } #[test] fn namespaced_attribute_roundtrip_b() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct PrefixedAttribute { #[xml(attribute = "xml:lang")] lang: String, } #[test] fn prefixed_attribute_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct RequiredNonStringAttribute { #[xml(attribute)] foo: i32, } #[test] fn required_non_string_attribute_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct DefaultAttribute { #[xml(attribute(default))] foo: std::option::Option, #[xml(attribute(default))] bar: std::option::Option, } #[test] fn default_attribute_roundtrip_aa() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[test] fn default_attribute_roundtrip_pa() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[test] fn default_attribute_roundtrip_ap() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[test] fn default_attribute_roundtrip_pp() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "text")] struct TextString { #[xml(text)] text: String, } #[test] fn text_string_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("hello world!"); } #[test] fn text_string_positive_preserves_whitespace() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; let el = parse_str::(" \t\n").unwrap(); assert_eq!(el.text, " \t\n"); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "text")] struct TextNonString { #[xml(text)] text: u32, } #[test] fn text_non_string_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("123456"); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "elem")] struct IgnoresWhitespaceWithoutTextConsumer; #[test] fn ignores_whitespace_without_text_consumer_positive() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; let _ = parse_str::( " \t\r\n", ) .unwrap(); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "elem")] struct FailsTextWithoutTextConsumer; #[test] fn fails_text_without_text_consumer_positive() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::(" quak ") { Err(::xso::error::FromElementError::Invalid(::xso::error::Error::Other(e))) if e.contains("Unexpected text") => { () } other => panic!("unexpected result: {:?}", other), } } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "text")] struct TextWithCodec { #[xml(text(codec = xso::text::EmptyAsNone))] text: std::option::Option, } #[test] fn text_with_codec_roundtrip_empty() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::(""); } #[test] fn text_with_codec_roundtrip_non_empty() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("hello"); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct Parent { #[xml(child)] child: RequiredAttribute, } #[test] fn parent_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("") } #[test] fn parent_positive() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; let v = parse_str::("") .unwrap(); assert_eq!(v.child.foo, "hello world!"); } #[test] fn parent_negative_duplicate_child() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Err(::xso::error::FromElementError::Invalid(::xso::error::Error::Other(e))) if e.contains("must not have more than one") => (), other => panic!("unexpected result: {:?}", other), } } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct OptionalChild { #[xml(child(default))] child: std::option::Option, } #[test] fn optional_child_roundtrip_present() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) } #[test] fn optional_child_roundtrip_absent() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("") } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "elem")] struct BoxedChild { #[xml(child(default))] child: std::option::Option>, } #[test] fn boxed_child_roundtrip_absent() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("") } #[test] fn boxed_child_roundtrip_nested_1() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("") } #[test] fn boxed_child_roundtrip_nested_2() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("") } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "elem", builder = RenamedBuilder, iterator = RenamedIter)] struct RenamedTypes; #[test] fn renamed_types_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("") } #[test] #[allow(unused_comparisons)] fn renamed_types_get_renamed() { // these merely serve as a test that the types are declared with the names // given in the attributes. assert!(std::mem::size_of::() >= 0); assert!(std::mem::size_of::() >= 0); } // What is this, you may wonder? // This is a test that any generated type names won't trigger // the `non_camel_case_types` lint. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "elem")] struct LintTest_; #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1)] enum NameSwitchedEnum { #[xml(name = "a")] Variant1 { #[xml(attribute)] foo: String, }, #[xml(name = "b")] Variant2 { #[xml(text)] foo: String, }, } #[test] fn name_switched_enum_positive_variant_1() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Ok(NameSwitchedEnum::Variant1 { foo }) => { assert_eq!(foo, "hello"); } other => panic!("unexpected result: {:?}", other), } } #[test] fn name_switched_enum_positive_variant_2() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("hello") { Ok(NameSwitchedEnum::Variant2 { foo }) => { assert_eq!(foo, "hello"); } other => panic!("unexpected result: {:?}", other), } } #[test] fn name_switched_enum_negative_name_mismatch() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("hello") { Err(xso::error::FromElementError::Mismatch { .. }) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn name_switched_enum_negative_namespace_mismatch() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("hello") { Err(xso::error::FromElementError::Mismatch { .. }) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn name_switched_enum_roundtrip_variant_1() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("") } #[test] fn name_switched_enum_roundtrip_variant_2() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("hello") } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, builder = RenamedEnumBuilder, iterator = RenamedEnumIter)] enum RenamedEnumTypes { #[xml(name = "elem")] A, } #[test] fn renamed_enum_types_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("") } #[test] #[allow(unused_comparisons)] fn renamed_enum_types_get_renamed() { // these merely serve as a test that the types are declared with the names // given in the attributes. assert!(std::mem::size_of::() >= 0); assert!(std::mem::size_of::() >= 0); } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, exhaustive)] enum ExhaustiveNameSwitchedEnum { #[xml(name = "a")] Variant1 { #[xml(attribute)] foo: String, }, #[xml(name = "b")] Variant2 { #[xml(text)] foo: String, }, } #[test] fn exhaustive_name_switched_enum_negative_name_mismatch() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("hello") { Err(xso::error::FromElementError::Invalid { .. }) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn exhaustive_name_switched_enum_negative_namespace_mismatch() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("hello") { Err(xso::error::FromElementError::Mismatch { .. }) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn exhaustive_name_switched_enum_roundtrip_variant_1() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("") } #[test] fn exhaustive_name_switched_enum_roundtrip_variant_2() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("hello") } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct Children { #[xml(child(n = ..))] foo: Vec, } #[test] fn children_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct TextExtract { #[xml(extract(namespace = NS1, name = "child", fields(text)))] contents: String, } #[test] fn text_extract_positive() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "hello world", ) { Ok(TextExtract { contents }) => { assert_eq!(contents, "hello world"); } other => panic!("unexpected result: {:?}", other), } } #[test] fn text_extract_negative_absent_child() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Err(xso::error::FromElementError::Invalid(xso::error::Error::Other(e))) if e.contains("Missing child field") => { () } other => panic!("unexpected result: {:?}", other), } } #[test] fn text_extract_negative_unexpected_attribute_in_child() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Err(xso::error::FromElementError::Invalid(xso::error::Error::Other(e))) if e.contains("Unknown attribute") => { () } other => panic!("unexpected result: {:?}", other), } } #[test] fn text_extract_negative_unexpected_child_in_child() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "", ) { Err(xso::error::FromElementError::Invalid(xso::error::Error::Other(e))) if e.contains("Unknown child in extraction") => { () } other => panic!("unexpected result: {:?}", other), } } #[test] fn text_extract_negative_duplicate_child() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "hello worldmore", ) { Err(xso::error::FromElementError::Invalid(xso::error::Error::Other(e))) if e.contains("must not have more than one") => { () } other => panic!("unexpected result: {:?}", other), } } #[test] fn text_extract_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "hello world!", ) } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct AttributeExtract { #[xml(extract(namespace = NS1, name = "child", fields(attribute = "foo")))] contents: String, } #[test] fn attribute_extract_positive() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "", ) { Ok(AttributeExtract { contents }) => { assert_eq!(contents, "hello world"); } other => panic!("unexpected result: {:?}", other), } } #[test] fn attribute_extract_negative_absent_attribute() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Err(xso::error::FromElementError::Invalid(xso::error::Error::Other(e))) if e.contains("Required attribute") => { () } other => panic!("unexpected result: {:?}", other), } } #[test] fn attribute_extract_negative_unexpected_text_in_child() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "fnord", ) { Err(xso::error::FromElementError::Invalid(xso::error::Error::Other(e))) if e.contains("Unexpected text") => { () } other => panic!("unexpected result: {:?}", other), } } #[test] fn attribute_extract_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct OptionalAttributeExtract { #[xml(extract(namespace = NS1, name = "child", fields(attribute(name = "foo", default))))] contents: ::std::option::Option, } #[test] fn optional_attribute_extract_positive_present() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "", ) { Ok(OptionalAttributeExtract { contents: Some(contents), }) => { assert_eq!(contents, "hello world"); } other => panic!("unexpected result: {:?}", other), } } #[test] fn optional_attribute_extract_positive_present_empty() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "", ) { Ok(OptionalAttributeExtract { contents: Some(contents), }) => { assert_eq!(contents, ""); } other => panic!("unexpected result: {:?}", other), } } #[test] fn optional_attribute_extract_positive_absent() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Ok(OptionalAttributeExtract { contents: None }) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn optional_attribute_extract_roundtrip_present() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) } #[test] fn optional_attribute_extract_roundtrip_present_empty() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) } #[test] fn optional_attribute_extract_roundtrip_absent() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("") } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct ChildExtract { #[xml(extract(namespace = NS1, name = "child", fields(child)))] contents: RequiredAttribute, } #[test] fn child_extract_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct NestedExtract { #[xml(extract(namespace = NS1, name = "child", fields( extract(namespace = NS1, name = "grandchild", fields(text)) )))] contents: String, } #[test] fn nested_extract_positive() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "hello world", ) { Ok(NestedExtract { contents }) => { assert_eq!(contents, "hello world"); } other => panic!("unexpected result: {:?}", other), } } #[test] fn nested_extract_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("hello world") } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct ExtractOmitNamespace { #[xml(extract(name = "child", fields(text)))] contents: String, } #[test] fn extract_omit_namespace_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "hello world!", ) } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct ExtractOmitName { #[xml(extract(namespace = NS1, fields(text)))] contents: String, } #[test] fn extract_omit_name_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "hello world!", ) } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct ExtractOmitNameAndNamespace { #[xml(extract(fields(text)))] contents: String, } #[test] fn extract_omit_name_and_namespace_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "hello world!", ) } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct TextExtractVec { #[xml(extract(n = .., namespace = NS1, name = "child", fields(text(type_ = String))))] contents: Vec, } #[test] fn text_extract_vec_positive_nonempty() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "helloworld", ) { Ok(TextExtractVec { contents }) => { assert_eq!(contents[0], "hello"); assert_eq!(contents[1], "world"); assert_eq!(contents.len(), 2); } other => panic!("unexpected result: {:?}", other), } } #[test] fn text_extract_vec_positive_empty() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Ok(TextExtractVec { contents }) => { assert_eq!(contents.len(), 0); } other => panic!("unexpected result: {:?}", other), } } #[test] fn text_extract_vec_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "helloworld", ) } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct AttributeExtractVec { #[xml(extract(n = .., namespace = NS1, name = "child", fields(attribute(type_ = String, name = "attr"))))] contents: Vec, } #[test] fn text_extract_attribute_vec_positive_nonempty() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "", ) { Ok(AttributeExtractVec { contents }) => { assert_eq!(contents[0], "hello"); assert_eq!(contents[1], "world"); assert_eq!(contents.len(), 2); } other => panic!("unexpected result: {:?}", other), } } #[test] fn text_extract_attribute_vec_positive_empty() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Ok(AttributeExtractVec { contents }) => { assert_eq!(contents.len(), 0); } other => panic!("unexpected result: {:?}", other), } } #[test] fn text_extract_attribute_vec_roundtrip() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct TextOptionalExtract { #[xml(extract(namespace = NS1, name = "child", default, fields(text(type_ = String))))] contents: ::std::option::Option, } #[test] fn text_optional_extract_positive_present() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "hello world", ) { Ok(TextOptionalExtract { contents: Some(contents), }) => { assert_eq!(contents, "hello world"); } other => panic!("unexpected result: {:?}", other), } } #[test] fn text_optional_extract_positive_absent_child() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Ok(TextOptionalExtract { contents: None }) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn text_optional_extract_roundtrip_present() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "hello world!", ) } #[test] fn text_optional_extract_roundtrip_absent() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::("") } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct OptionalAttributeOptionalExtract { #[xml(extract(namespace = NS1, name = "child", default, fields(attribute(name = "foo", default))))] contents: ::std::option::Option, } #[test] fn optional_attribute_optional_extract_positive_present() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "", ) { Ok(OptionalAttributeOptionalExtract { contents: Some(contents), }) => { assert_eq!(contents, "hello world"); } other => panic!("unexpected result: {:?}", other), } } #[test] fn optional_attribute_optional_extract_positive_absent_attribute() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "", ) { Ok(OptionalAttributeOptionalExtract { contents: None }) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn optional_attribute_optional_extract_positive_absent_element() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::("") { Ok(OptionalAttributeOptionalExtract { contents: None }) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn optional_attribute_optional_extract_roundtrip_present() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) } #[test] fn optional_attribute_optional_extract_roundtrip_absent_attribute() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) } #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "parent")] struct OptionalAttributeOptionalExtractDoubleOption { #[xml(extract(namespace = NS1, name = "child", default, fields(attribute(name = "foo", type_ = ::std::option::Option, default))))] contents: ::std::option::Option<::std::option::Option>, } #[test] fn optional_attribute_optional_extract_double_option_positive_present() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "", ) { Ok(OptionalAttributeOptionalExtractDoubleOption { contents: Some(Some(contents)), }) => { assert_eq!(contents, "hello world"); } other => panic!("unexpected result: {:?}", other), } } #[test] fn optional_attribute_optional_extract_double_option_positive_absent_attribute() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "", ) { Ok(OptionalAttributeOptionalExtractDoubleOption { contents: Some(None), }) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn optional_attribute_optional_extract_double_option_positive_absent_element() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; match parse_str::( "", ) { Ok(OptionalAttributeOptionalExtractDoubleOption { contents: None }) => (), other => panic!("unexpected result: {:?}", other), } } #[test] fn optional_attribute_optional_extract_double_option_roundtrip_present() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) } #[test] fn optional_attribute_optional_extract_double_option_roundtrip_absent_attribute() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) } #[test] fn optional_attribute_optional_extract_double_option_roundtrip_absent_child() { #[allow(unused_imports)] use std::{ option::Option::{None, Some}, result::Result::{Err, Ok}, }; roundtrip_full::( "", ) }