From 21cfd6bb1c92489c1471ec1c7cda80ef5274314b Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 17 Sep 2023 17:53:01 +0500 Subject: [PATCH 1/5] Split deserializer tests into several files to speed up compilation Tests for sequences occupies over 4000 lines of code. Tests for enums occupies about 1000 lines of code. Since these tests are loosely related, it makes sense to separate them into separate files to speed up compilation --- Cargo.toml | 10 + tests/helpers/mod.rs | 27 + tests/serde-de-enum.rs | 1034 +++++++ tests/serde-de-seq.rs | 4083 +++++++++++++++++++++++++++ tests/serde-de.rs | 5950 +++------------------------------------- 5 files changed, 5566 insertions(+), 5538 deletions(-) create mode 100644 tests/helpers/mod.rs create mode 100644 tests/serde-de-enum.rs create mode 100644 tests/serde-de-seq.rs diff --git a/Cargo.toml b/Cargo.toml index 17087ca0..d3c10bb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -209,6 +209,16 @@ name = "serde-de" required-features = ["serialize"] path = "tests/serde-de.rs" +[[test]] +name = "serde-de-enum" +required-features = ["serialize"] +path = "tests/serde-de-enum.rs" + +[[test]] +name = "serde-de-seq" +required-features = ["serialize"] +path = "tests/serde-de-seq.rs" + [[test]] name = "serde-se" required-features = ["serialize"] diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs new file mode 100644 index 00000000..6d956768 --- /dev/null +++ b/tests/helpers/mod.rs @@ -0,0 +1,27 @@ +//! Utility functions for integration tests + +use quick_xml::de::Deserializer; +use quick_xml::DeError; +use serde::Deserialize; + +/// Deserialize an instance of type T from a string of XML text. +/// If deserialization was succeeded checks that all XML events was consumed +pub fn from_str<'de, T>(source: &'de str) -> Result +where + T: Deserialize<'de>, +{ + // Log XML that we try to deserialize to see it in the failed tests output + dbg!(source); + let mut de = Deserializer::from_str(source); + let result = T::deserialize(&mut de); + + // If type was deserialized, the whole XML document should be consumed + if let Ok(_) = result { + match <()>::deserialize(&mut de) { + Err(DeError::UnexpectedEof) => (), + e => panic!("Expected end `UnexpectedEof`, but got {:?}", e), + } + } + + result +} diff --git a/tests/serde-de-enum.rs b/tests/serde-de-enum.rs new file mode 100644 index 00000000..cc721e5a --- /dev/null +++ b/tests/serde-de-enum.rs @@ -0,0 +1,1034 @@ +//! Tests of deserialization of XML documents into various enum types + +use quick_xml::DeError; +use serde::Deserialize; + +mod helpers; +use helpers::from_str; + +#[derive(Debug, Deserialize, PartialEq)] +struct Nested { + //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 + float: String, +} + +/// Type where all struct fields represented by attributes +#[derive(Debug, Deserialize, PartialEq)] +struct NestedAttr { + //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 + #[serde(rename = "@float")] + float: String, +} + +/// Enum tag selector is a name of the `` or text / CDATA content +/// for a `$text` variant +mod externally_tagged { + use super::*; + use pretty_assertions::assert_eq; + + /// Type where all fields of struct variants represented by elements + #[derive(Debug, Deserialize, PartialEq)] + enum Node { + Unit, + Newtype(bool), + //TODO: serde bug https://github.com/serde-rs/serde/issues/1904 + // Tuple(f64, String), + Struct { + float: f64, + string: String, + }, + Holder { + nested: Nested, + string: String, + }, + Flatten { + #[serde(flatten)] + nested: Nested, + string: String, + }, + } + + /// Type where all fields of struct variants represented by attributes + #[derive(Debug, Deserialize, PartialEq)] + enum NodeAttr { + Struct { + #[serde(rename = "@float")] + float: f64, + #[serde(rename = "@string")] + string: String, + }, + Holder { + nested: NestedAttr, + #[serde(rename = "@string")] + string: String, + }, + Flatten { + #[serde(flatten)] + nested: NestedAttr, + #[serde(rename = "@string")] + string: String, + }, + } + + /// Workaround for serde bug https://github.com/serde-rs/serde/issues/1904 + #[derive(Debug, Deserialize, PartialEq)] + enum Workaround { + Tuple(f64, String), + } + + #[test] + fn unit() { + let data: Node = from_str("").unwrap(); + assert_eq!(data, Node::Unit); + } + + #[test] + fn newtype() { + let data: Node = from_str("true").unwrap(); + assert_eq!(data, Node::Newtype(true)); + } + + #[test] + fn tuple_struct() { + let data: Workaround = from_str("42answer").unwrap(); + assert_eq!(data, Workaround::Tuple(42.0, "answer".into())); + } + + mod struct_ { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn elements() { + let data: Node = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"42answer"#, + ) + .unwrap(); + assert_eq!( + data, + Node::Struct { + float: 42.0, + string: "answer".into() + } + ); + } + + #[test] + fn attributes() { + let data: NodeAttr = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + NodeAttr::Struct { + float: 42.0, + string: "answer".into() + } + ); + } + + #[test] + fn namespaces() { + let data: Node = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"42answer"#, + ) + .unwrap(); + assert_eq!( + data, + Node::Struct { + float: 42.0, + string: "answer".into() + } + ); + } + } + + mod nested_struct { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn elements() { + let data: Node = from_str( + r#"answer42"#, + ) + .unwrap(); + assert_eq!( + data, + Node::Holder { + nested: Nested { float: "42".into() }, + string: "answer".into() + } + ); + } + + #[test] + fn attributes() { + let data: NodeAttr = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + NodeAttr::Holder { + nested: NestedAttr { float: "42".into() }, + string: "answer".into() + } + ); + } + } + + mod flatten_struct { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn elements() { + let data: Node = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"42answer"#, + ) + .unwrap(); + assert_eq!( + data, + Node::Flatten { + nested: Nested { float: "42".into() }, + string: "answer".into() + } + ); + } + + #[test] + fn attributes() { + let data: NodeAttr = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + NodeAttr::Flatten { + nested: NestedAttr { float: "42".into() }, + string: "answer".into() + } + ); + } + } + + /// Test deserialization of the specially named variant `$text` + mod text { + use super::*; + + mod unit { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, Deserialize, PartialEq)] + enum Text { + #[serde(rename = "$text")] + Unit, + } + + #[test] + fn text() { + let data: Text = from_str(" text ").unwrap(); + assert_eq!(data, Text::Unit); + } + + #[test] + fn cdata() { + let data: Text = from_str("").unwrap(); + assert_eq!(data, Text::Unit); + } + + #[test] + #[ignore = "awaiting fix of https://github.com/tafia/quick-xml/issues/474"] + fn mixed() { + let data: Text = from_str(" te xt ").unwrap(); + assert_eq!(data, Text::Unit); + } + } + + mod newtype { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, Deserialize, PartialEq)] + enum Text { + #[serde(rename = "$text")] + Newtype(String), + } + + #[test] + fn text() { + let data: Text = from_str(" text ").unwrap(); + assert_eq!(data, Text::Newtype("text".into())); + } + + #[test] + fn cdata() { + let data: Text = from_str("").unwrap(); + assert_eq!(data, Text::Newtype(" cdata ".into())); + } + + #[test] + #[ignore = "awaiting fix of https://github.com/tafia/quick-xml/issues/474"] + fn mixed() { + let data: Text = from_str(" te xt ").unwrap(); + assert_eq!(data, Text::Newtype("te cdata xt".into())); + } + } + + /// Tuple variant deserialized as an `xs:list`, that is why spaces + /// are trimmed even in CDATA sections + mod tuple { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, Deserialize, PartialEq)] + enum Text { + #[serde(rename = "$text")] + Tuple(f64, String), + } + + #[test] + fn text() { + let data: Text = from_str(" 4.2 text ").unwrap(); + assert_eq!(data, Text::Tuple(4.2, "text".into())); + } + + #[test] + fn cdata() { + let data: Text = from_str("").unwrap(); + assert_eq!(data, Text::Tuple(4.2, "cdata".into())); + } + + #[test] + #[ignore = "awaiting fix of https://github.com/tafia/quick-xml/issues/474"] + fn mixed() { + let data: Text = from_str(" 4.2 ").unwrap(); + assert_eq!(data, Text::Tuple(4.2, "cdata".into())); + } + } + + /// Struct variant cannot be directly deserialized from `Text` / `CData` events + mod struct_ { + use super::*; + + #[derive(Debug, Deserialize, PartialEq)] + enum Text { + #[serde(rename = "$text")] + Struct { float: f64, string: String }, + } + + #[test] + fn text() { + match from_str::(" text ") { + Err(DeError::Unsupported(_)) => {} + x => panic!("Expected `Err(Unsupported(_))`, but found {:?}", x), + } + } + + #[test] + fn cdata() { + match from_str::("") { + Err(DeError::Unsupported(_)) => {} + x => panic!("Expected `Err(Unsupported(_))`, but found {:?}", x), + } + } + + #[test] + fn mixed() { + match from_str::(" te xt ") { + Err(DeError::Unsupported(_)) => {} + x => panic!("Expected `Err(Unsupported(_))`, but found {:?}", x), + } + } + } + } +} + +/// Enum tag selector either an attribute "tag", or a tag "tag". +/// `$text` variant could be defined, but that name has no special meaning +mod internally_tagged { + use super::*; + + /// Type where all fields of struct variants and a tag represented by elements + #[derive(Debug, Deserialize, PartialEq)] + #[serde(tag = "tag")] + enum Node { + Unit, + /// Primitives (such as `bool`) are not supported by serde in the internally tagged mode + Newtype(NewtypeContent), + // Tuple(f64, String),// Tuples are not supported in the internally tagged mode + Struct { + //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 + float: String, + string: String, + }, + Holder { + nested: Nested, + string: String, + }, + Flatten { + #[serde(flatten)] + nested: Nested, + string: String, + }, + } + + /// Type where all fields of struct variants and a tag represented by attributes + #[derive(Debug, Deserialize, PartialEq)] + #[serde(tag = "@tag")] + enum NodeAttr { + Unit, + /// Primitives (such as `bool`) are not supported by serde in the internally tagged mode + Newtype(NewtypeContent), + // Tuple(f64, String),// Tuples are not supported in the internally tagged mode + Struct { + //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 + #[serde(rename = "@float")] + float: String, + #[serde(rename = "@string")] + string: String, + }, + Holder { + nested: NestedAttr, + #[serde(rename = "@string")] + string: String, + }, + Flatten { + #[serde(flatten)] + nested: NestedAttr, + #[serde(rename = "@string")] + string: String, + }, + } + + #[derive(Debug, Deserialize, PartialEq)] + struct NewtypeContent { + value: bool, + } + + mod unit { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn elements() { + let data: Node = from_str(r#"Unit"#).unwrap(); + assert_eq!(data, Node::Unit); + } + + #[test] + fn attributes() { + let data: NodeAttr = from_str(r#""#).unwrap(); + assert_eq!(data, NodeAttr::Unit); + } + } + + mod newtype { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn elements() { + let data: Node = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"Newtypetrue"#, + ) + .unwrap(); + assert_eq!(data, Node::Newtype(NewtypeContent { value: true })); + } + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn attributes() { + let data: NodeAttr = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"true"#, + ) + .unwrap(); + assert_eq!(data, NodeAttr::Newtype(NewtypeContent { value: true })); + } + } + + mod struct_ { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn elements() { + let data: Node = from_str( + r#"Struct42answer"#, + ) + .unwrap(); + assert_eq!( + data, + Node::Struct { + float: "42".into(), + string: "answer".into() + } + ); + } + + #[test] + fn attributes() { + let data: NodeAttr = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + NodeAttr::Struct { + float: "42".into(), + string: "answer".into() + } + ); + } + } + + mod nested_struct { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn elements() { + let data: Node = from_str( + r#"Holderanswer42"#, + ).unwrap(); + assert_eq!( + data, + Node::Holder { + nested: Nested { float: "42".into() }, + string: "answer".into() + } + ); + } + + #[test] + fn attributes() { + let data: NodeAttr = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + NodeAttr::Holder { + nested: NestedAttr { float: "42".into() }, + string: "answer".into() + } + ); + } + } + + mod flatten_struct { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn elements() { + let data: Node = from_str( + r#"Flatten42answer"#, + ) + .unwrap(); + assert_eq!( + data, + Node::Flatten { + nested: Nested { float: "42".into() }, + string: "answer".into() + } + ); + } + + #[test] + fn attributes() { + let data: NodeAttr = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + NodeAttr::Flatten { + nested: NestedAttr { float: "42".into() }, + string: "answer".into() + } + ); + } + } +} + +/// Enum tag selector either an attribute "tag", or a tag "tag". +/// `$text` variant could be defined, but that name has no special meaning +mod adjacently_tagged { + use super::*; + + /// Type where all fields of struct variants, tag and content fields + /// represented by elements + #[derive(Debug, Deserialize, PartialEq)] + #[serde(tag = "tag", content = "content")] + enum Node { + Unit, + Newtype(bool), + //TODO: serde bug https://github.com/serde-rs/serde/issues/1904 + // Tuple(f64, String), + Struct { + float: f64, + string: String, + }, + Holder { + nested: Nested, + string: String, + }, + Flatten { + #[serde(flatten)] + nested: Nested, + string: String, + }, + } + + /// Type where all fields of struct variants, tag and content fields + /// represented by attributes + #[derive(Debug, Deserialize, PartialEq)] + #[serde(tag = "@tag", content = "@content")] + enum NodeAttrSimple { + Unit, + Newtype(bool), + } + + /// Type where all fields of struct variants and a tag represented by attributes + /// content cannot be represented by attribute because this is a complex struct + #[derive(Debug, Deserialize, PartialEq)] + #[serde(tag = "@tag", content = "content")] + enum NodeAttrComplex { + //TODO: serde bug https://github.com/serde-rs/serde/issues/1904 + // Tuple(f64, String), + Struct { + #[serde(rename = "@float")] + float: f64, + #[serde(rename = "@string")] + string: String, + }, + Holder { + nested: NestedAttr, + #[serde(rename = "@string")] + string: String, + }, + Flatten { + #[serde(flatten)] + nested: NestedAttr, + #[serde(rename = "@string")] + string: String, + }, + } + + /// Workaround for serde bug https://github.com/serde-rs/serde/issues/1904 + #[derive(Debug, Deserialize, PartialEq)] + #[serde(tag = "tag", content = "content")] + enum Workaround { + Tuple(f64, String), + } + + /// Workaround for serde bug https://github.com/serde-rs/serde/issues/1904 + #[derive(Debug, Deserialize, PartialEq)] + #[serde(tag = "@tag", content = "@content")] + enum WorkaroundAttr { + Tuple(f64, String), + } + + mod unit { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn elements() { + let data: Node = from_str(r#"Unit"#).unwrap(); + assert_eq!(data, Node::Unit); + } + + #[test] + fn attributes() { + let data: NodeAttrSimple = from_str(r#""#).unwrap(); + assert_eq!(data, NodeAttrSimple::Unit); + } + } + + mod newtype { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn elements() { + let data: Node = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"Newtypetrue"#, + ) + .unwrap(); + assert_eq!(data, Node::Newtype(true)); + } + + #[test] + fn attributes() { + let data: NodeAttrSimple = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!(data, NodeAttrSimple::Newtype(true)); + } + } + + mod tuple_struct { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn elements() { + let data: Workaround = from_str( + r#"Tuple42answer"#, + ).unwrap(); + assert_eq!(data, Workaround::Tuple(42.0, "answer".into())); + } + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn attributes() { + let data: WorkaroundAttr = from_str( + // We cannot have two attributes with the same name, so both values stored in one attribute + r#""#, + ) + .unwrap(); + assert_eq!(data, WorkaroundAttr::Tuple(42.0, "answer".into())); + } + } + + mod struct_ { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn elements() { + let data: Node = from_str( + r#"Struct42answer"#, + ) + .unwrap(); + assert_eq!( + data, + Node::Struct { + float: 42.0, + string: "answer".into() + } + ); + } + + #[test] + fn attributes() { + let data: NodeAttrComplex = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + NodeAttrComplex::Struct { + float: 42.0, + string: "answer".into() + } + ); + } + } + + mod nested_struct { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn elements() { + let data: Node = from_str( + r#" + Holder + + answer + + 42 + + + "#, + ) + .unwrap(); + assert_eq!( + data, + Node::Holder { + nested: Nested { float: "42".into() }, + string: "answer".into() + } + ); + } + + #[test] + fn attributes() { + let data: NodeAttrComplex = from_str( + r#""#, + ).unwrap(); + assert_eq!( + data, + NodeAttrComplex::Holder { + nested: NestedAttr { float: "42".into() }, + string: "answer".into() + } + ); + } + } + + mod flatten_struct { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn elements() { + let data: Node = from_str( + r#"Flatten42answer"#, + ).unwrap(); + assert_eq!( + data, + Node::Flatten { + nested: Nested { float: "42".into() }, + string: "answer".into() + } + ); + } + + #[test] + fn attributes() { + let data: NodeAttrComplex = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + NodeAttrComplex::Flatten { + nested: NestedAttr { float: "42".into() }, + string: "answer".into() + } + ); + } + } +} + +/// Enum tags does not exist. +/// `$text` variant could be defined, but that name has no special meaning +mod untagged { + use super::*; + use pretty_assertions::assert_eq; + + /// Type where all fields of struct variants represented by elements + #[derive(Debug, Deserialize, PartialEq)] + #[serde(untagged)] + enum Node { + Unit, + Newtype(bool), + // serde bug https://github.com/serde-rs/serde/issues/1904 + // Tuple(f64, String), + Struct { + float: f64, + string: String, + }, + Holder { + nested: Nested, + string: String, + }, + Flatten { + #[serde(flatten)] + nested: Nested, + // Can't use "string" as name because in that case this variant + // will have no difference from `Struct` variant + string2: String, + }, + } + + /// Type where all fields of struct variants represented by attributes + #[derive(Debug, Deserialize, PartialEq)] + #[serde(untagged)] + enum NodeAttr { + // serde bug https://github.com/serde-rs/serde/issues/1904 + // Tuple(f64, String), + Struct { + #[serde(rename = "@float")] + float: f64, + #[serde(rename = "@string")] + string: String, + }, + Holder { + nested: NestedAttr, + #[serde(rename = "@string")] + string: String, + }, + Flatten { + #[serde(flatten)] + nested: NestedAttr, + // Can't use "string" as name because in that case this variant + // will have no difference from `Struct` variant + #[serde(rename = "@string2")] + string2: String, + }, + } + + /// Workaround for serde bug https://github.com/serde-rs/serde/issues/1904 + #[derive(Debug, Deserialize, PartialEq)] + #[serde(untagged)] + enum Workaround { + Tuple(f64, String), + } + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn unit() { + // Unit variant consists just from the tag, and because tags + // are not written, nothing is written + let data: Node = from_str("").unwrap(); + assert_eq!(data, Node::Unit); + } + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn newtype() { + let data: Node = from_str("true").unwrap(); + assert_eq!(data, Node::Newtype(true)); + } + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn tuple_struct() { + let data: Workaround = from_str("42answer").unwrap(); + assert_eq!(data, Workaround::Tuple(42.0, "answer".into())); + } + + mod struct_ { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn elements() { + let data: Node = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"42answer"#, + ) + .unwrap(); + assert_eq!( + data, + Node::Struct { + float: 42.0, + string: "answer".into() + } + ); + } + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn attributes() { + let data: NodeAttr = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + NodeAttr::Struct { + float: 42.0, + string: "answer".into() + } + ); + } + } + + mod nested_struct { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn elements() { + let data: Node = from_str( + r#"answer42"#, + ) + .unwrap(); + assert_eq!( + data, + Node::Holder { + nested: Nested { float: "42".into() }, + string: "answer".into() + } + ); + } + + #[test] + fn attributes() { + let data: NodeAttr = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + NodeAttr::Holder { + nested: NestedAttr { float: "42".into() }, + string: "answer".into() + } + ); + } + } + + mod flatten_struct { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn elements() { + let data: Node = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"42answer"#, + ) + .unwrap(); + assert_eq!( + data, + Node::Flatten { + nested: Nested { float: "42".into() }, + string2: "answer".into() + } + ); + } + + #[test] + fn attributes() { + let data: NodeAttr = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + NodeAttr::Flatten { + nested: NestedAttr { float: "42".into() }, + string2: "answer".into() + } + ); + } + } +} diff --git a/tests/serde-de-seq.rs b/tests/serde-de-seq.rs new file mode 100644 index 00000000..1a3ffaf3 --- /dev/null +++ b/tests/serde-de-seq.rs @@ -0,0 +1,4083 @@ +//! Tests of deserialization of XML documents into various sequential types + +use quick_xml::DeError; +use serde::Deserialize; + +mod helpers; +use helpers::from_str; + +/// Check that top-level sequences can be deserialized from the multi-root XML documents +mod top_level { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn simple() { + from_str::<[(); 3]>("42answer").unwrap(); + + let data: Vec<()> = from_str("42answer").unwrap(); + assert_eq!(data, vec![(), (), ()]); + } + + /// Special case: empty sequence + #[test] + fn empty() { + from_str::<[(); 0]>("").unwrap(); + + let data: Vec<()> = from_str("").unwrap(); + assert_eq!(data, vec![]); + } + + /// Special case: one-element sequence + #[test] + fn one_element() { + from_str::<[(); 1]>("").unwrap(); + from_str::<[(); 1]>("42").unwrap(); + from_str::<[(); 1]>("text").unwrap(); + from_str::<[(); 1]>("").unwrap(); + + let data: Vec<()> = from_str("").unwrap(); + assert_eq!(data, vec![()]); + + let data: Vec<()> = from_str("42").unwrap(); + assert_eq!(data, vec![()]); + + let data: Vec<()> = from_str("text").unwrap(); + assert_eq!(data, vec![()]); + + let data: Vec<()> = from_str("").unwrap(); + assert_eq!(data, vec![()]); + } + + #[test] + fn excess_attribute() { + from_str::<[(); 3]>(r#"42answer"#) + .unwrap(); + + let data: Vec<()> = + from_str(r#"42answer"#) + .unwrap(); + assert_eq!(data, vec![(), (), ()]); + } + + #[test] + fn mixed_content() { + // Text and CDATA represents a one logical text item + from_str::<[(); 2]>( + r#" + + text + + "#, + ) + .unwrap(); + + let data: Vec<()> = from_str( + r#" + + text + + "#, + ) + .unwrap(); + // Text and CDATA represents a one logical text item + assert_eq!(data, vec![(), ()]); + } + + /// This test ensures that composition of deserializer building blocks plays well + #[test] + fn list_of_struct() { + #[derive(Debug, PartialEq, Default, Deserialize)] + #[serde(default)] + struct Struct { + #[serde(rename = "@attribute")] + attribute: Option, + element: Option, + } + + let data: Vec = from_str( + r#" + + + + value + + + value + + "#, + ) + .unwrap(); + assert_eq!( + data, + vec![ + Struct { + attribute: None, + element: None, + }, + Struct { + attribute: Some("value".to_string()), + element: None, + }, + Struct { + attribute: None, + element: Some("value".to_string()), + }, + Struct { + attribute: Some("value".to_string()), + element: Some("value".to_string()), + }, + ] + ); + } + + /// Test for https://github.com/tafia/quick-xml/issues/500 + #[test] + fn list_of_enum() { + #[derive(Debug, PartialEq, Deserialize)] + enum Enum { + One, + Two, + } + + let data: Vec = from_str( + r#" + + + + "#, + ) + .unwrap(); + assert_eq!(data, vec![Enum::One, Enum::Two, Enum::One]); + } +} + +/// Tests where each sequence item have an identical name in an XML. +/// That explicitly means that `enum`s as list elements are not supported +/// in that case, because enum requires different tags. +/// +/// (by `enums` we mean [externally tagged enums] is serde terminology) +/// +/// [externally tagged enums]: https://serde.rs/enum-representations.html#externally-tagged +mod fixed_name { + use super::*; + + /// This module contains tests where size of the list have a compile-time size + mod fixed_size { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct List { + item: [(); 3], + } + + /// Simple case: count of elements matches expected size of sequence, + /// each element has the same name. Successful deserialization expected + #[test] + fn simple() { + from_str::( + r#" + + + + + + "#, + ) + .unwrap(); + } + + /// Special case: empty sequence + #[test] + #[ignore = "it is impossible to distinguish between missed field and empty list: use `Option<>` or #[serde(default)]"] + fn empty() { + #[derive(Debug, PartialEq, Deserialize)] + struct List { + item: [(); 0], + } + + from_str::(r#""#).unwrap(); + from_str::(r#""#).unwrap(); + } + + /// Special case: one-element sequence + #[test] + fn one_element() { + #[derive(Debug, PartialEq, Deserialize)] + struct List { + item: [(); 1], + } + + from_str::( + r#" + + + + "#, + ) + .unwrap(); + } + + /// Fever elements than expected size of sequence, each element has + /// the same name. Failure expected + #[test] + fn fever_elements() { + let data = from_str::( + r#" + + + + + "#, + ); + + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 2, expected an array of length 3") + } + e => panic!( + r#"Expected `Err(Custom("invalid length 2, expected an array of length 3"))`, but found {:?}"#, + e + ), + } + } + + /// More elements than expected size of sequence, each element has + /// the same name. Failure expected. If you wish to ignore excess + /// elements, use the special type, that consume as much elements + /// as possible, but ignores excess elements + #[test] + fn excess_elements() { + let data = from_str::( + r#" + + + + + + + "#, + ); + + match data { + Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `item`"), + e => panic!( + r#"Expected `Err(Custom("duplicate field `item`"))`, but found {:?}"#, + e + ), + } + } + + /// Mixed content assumes, that some elements will have an internal + /// name `$text` or `$value`, so, unless field named the same, it is expected + /// to fail + #[test] + fn mixed_content() { + let data = from_str::( + r#" + + + text + + + "#, + ); + + match data { + Err(DeError::Custom(e)) => assert_eq!(e, "missing field `item`"), + e => panic!( + r#"Expected `Err(Custom("missing field `item`"))`, but found {:?}"#, + e + ), + } + } + + /// In those tests sequence should be deserialized from an XML + /// with additional elements that is not defined in the struct. + /// That fields should be skipped during deserialization + mod unknown_items { + use super::*; + #[cfg(not(feature = "overlapped-lists"))] + use pretty_assertions::assert_eq; + + #[test] + fn before() { + from_str::( + r#" + + + + + + + "#, + ) + .unwrap(); + } + + #[test] + fn after() { + from_str::( + r#" + + + + + + + "#, + ) + .unwrap(); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + data.unwrap(); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + outer: [List; 3], + } + + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + data.unwrap(); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + } + + /// In those tests non-sequential field is defined in the struct + /// before sequential, so it will be deserialized before the list. + /// That struct should be deserialized from an XML where these + /// fields comes in an arbitrary order + mod field_before_list { + use super::*; + #[cfg(not(feature = "overlapped-lists"))] + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + node: (), + item: [(); 3], + } + + #[test] + fn before() { + from_str::( + r#" + + + + + + + "#, + ) + .unwrap(); + } + + #[test] + fn after() { + from_str::( + r#" + + + + + + + "#, + ) + .unwrap(); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + data.unwrap(); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + node: (), + outer: [List; 3], + } + + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + data.unwrap(); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + } + + /// In those tests non-sequential field is defined in the struct + /// after sequential, so it will be deserialized after the list. + /// That struct should be deserialized from an XML where these + /// fields comes in an arbitrary order + mod field_after_list { + use super::*; + #[cfg(not(feature = "overlapped-lists"))] + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + item: [(); 3], + node: (), + } + + #[test] + fn before() { + from_str::( + r#" + + + + + + + "#, + ) + .unwrap(); + } + + #[test] + fn after() { + from_str::( + r#" + + + + + + + "#, + ) + .unwrap(); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + data.unwrap(); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + outer: [List; 3], + node: (), + } + + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + data.unwrap(); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + } + + /// In those tests two lists are deserialized simultaneously. + /// Lists should be deserialized even when them overlaps + mod two_lists { + use super::*; + #[cfg(not(feature = "overlapped-lists"))] + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Pair { + item: [(); 3], + element: [(); 2], + } + + #[test] + fn splitted() { + from_str::( + r#" + + + + + + + + "#, + ) + .unwrap(); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + data.unwrap(); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Pair { + outer: [List; 3], + element: [(); 2], + } + + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + data.unwrap(); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + } + + /// Deserialization of primitives slightly differs from deserialization + /// of complex types, so need to check this separately + #[test] + fn primitives() { + #[derive(Debug, PartialEq, Deserialize)] + struct List { + item: [usize; 3], + } + + let data: List = from_str( + r#" + + 41 + 42 + 43 + + "#, + ) + .unwrap(); + assert_eq!(data, List { item: [41, 42, 43] }); + + from_str::( + r#" + + 41 + 42 + 43 + + "#, + ) + .unwrap_err(); + } + + /// This test ensures that composition of deserializer building blocks + /// plays well + #[test] + fn list_of_struct() { + #[derive(Debug, PartialEq, Default, Deserialize)] + #[serde(default)] + struct Struct { + #[serde(rename = "@attribute")] + attribute: Option, + element: Option, + } + + #[derive(Debug, PartialEq, Deserialize)] + struct List { + item: [Struct; 4], + } + + let data: List = from_str( + r#" + + + + + value + + + value + + + "#, + ) + .unwrap(); + assert_eq!( + data, + List { + item: [ + Struct { + attribute: None, + element: None, + }, + Struct { + attribute: Some("value".to_string()), + element: None, + }, + Struct { + attribute: None, + element: Some("value".to_string()), + }, + Struct { + attribute: Some("value".to_string()), + element: Some("value".to_string()), + }, + ], + } + ); + } + + /// Checks that sequences represented by elements can contain sequences, + /// represented by [`xs:list`s](https://www.w3schools.com/xml/el_list.asp) + mod xs_list { + use super::*; + use pretty_assertions::assert_eq; + + /// Special case: zero elements + #[test] + fn zero() { + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// Outer list mapped to elements, inner -- to `xs:list`. + /// + /// `#[serde(default)]` is required to correctly deserialize + /// empty sequence, because without elements the field + /// also is missing and derived `Deserialize` implementation + /// would complain about that unless field is marked as + /// `default`. + #[serde(default)] + item: [Vec; 0], + } + + let data: List = from_str( + r#" + + + "#, + ) + .unwrap(); + + assert_eq!(data, List { item: [] }); + } + + /// Special case: one element + #[test] + fn one() { + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// Outer list mapped to elements, inner -- to `xs:list`. + /// + /// `#[serde(default)]` is not required, because correct + /// XML will always contains at least 1 element. + item: [Vec; 1], + } + + let data: List = from_str( + r#" + + first list + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: [vec!["first".to_string(), "list".to_string()]] + } + ); + } + + /// Special case: outer list is always mapped to an elements sequence, + /// not to an `xs:list` + #[test] + fn element() { + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// Outer list mapped to elements, inner -- to `xs:list`. + /// + /// `#[serde(default)]` is not required, because correct + /// XML will always contains at least 1 element. + item: [String; 1], + } + + let data: List = from_str( + r#" + + first item + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: ["first item".to_string()] + } + ); + } + + /// This tests demonstrates, that for `$value` field (`list`) actual + /// name of XML element (`item`) does not matter. That allows list + /// item to be an enum, where tag name determines enum variant + #[test] + fn many() { + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// Outer list mapped to elements, inner -- to `xs:list`. + /// + /// `#[serde(default)]` is not required, because correct + /// XML will always contains at least 1 element. + item: [Vec; 2], + } + + let data: List = from_str( + r#" + + first list + second list + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: [ + vec!["first".to_string(), "list".to_string()], + vec!["second".to_string(), "list".to_string()], + ] + } + ); + } + } + } + + /// This module contains tests where size of the list have an unspecified size + mod variable_size { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct List { + item: Vec<()>, + } + + /// Simple case: count of elements matches expected size of sequence, + /// each element has the same name. Successful deserialization expected + #[test] + fn simple() { + let data: List = from_str( + r#" + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: vec![(), (), ()], + } + ); + } + + /// Special case: empty sequence + #[test] + #[ignore = "it is impossible to distinguish between missed field and empty list: use `Option<>` or #[serde(default)]"] + fn empty() { + let data: List = from_str(r#""#).unwrap(); + assert_eq!(data, List { item: vec![] }); + + let data: List = from_str(r#""#).unwrap(); + assert_eq!(data, List { item: vec![] }); + } + + /// Special case: one-element sequence + #[test] + fn one_element() { + let data: List = from_str( + r#" + + + + "#, + ) + .unwrap(); + + assert_eq!(data, List { item: vec![()] }); + } + + /// Mixed content assumes, that some elements will have an internal + /// name `$text` or `$value`, so, unless field named the same, it is expected + /// to fail + #[test] + fn mixed_content() { + let data = from_str::( + r#" + + + text + + + "#, + ); + + match data { + Err(DeError::Custom(e)) => assert_eq!(e, "missing field `item`"), + e => panic!( + r#"Expected `Err(Custom("missing field `item`"))`, but found {:?}"#, + e + ), + } + } + + /// In those tests sequence should be deserialized from the XML + /// with additional elements that is not defined in the struct. + /// That fields should be skipped during deserialization + mod unknown_items { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn before() { + let data: List = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: vec![(), (), ()], + } + ); + } + + #[test] + fn after() { + let data: List = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: vec![(), (), ()], + } + ); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + List { + item: vec![(), (), ()], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `item`"), + e => panic!( + r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + outer: Vec, + } + + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + outer: vec![ + List { item: vec![()] }, + List { item: vec![()] }, + List { item: vec![()] }, + ], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), + e => panic!( + r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, + e + ), + } + } + } + + /// In those tests non-sequential field is defined in the struct + /// before sequential, so it will be deserialized before the list. + /// That struct should be deserialized from the XML where these + /// fields comes in an arbitrary order + mod field_before_list { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Default, Deserialize)] + struct Root { + node: (), + item: Vec<()>, + } + + #[test] + fn before() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + node: (), + item: vec![(), (), ()], + } + ); + } + + #[test] + fn after() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + node: (), + item: vec![(), (), ()], + } + ); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + node: (), + item: vec![(), (), ()], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `item`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + node: (), + outer: Vec, + } + + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + node: (), + outer: vec![ + List { item: vec![()] }, + List { item: vec![()] }, + List { item: vec![()] }, + ], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), + e => panic!( + r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, + e + ), + } + } + } + + /// In those tests non-sequential field is defined in the struct + /// after sequential, so it will be deserialized after the list. + /// That struct should be deserialized from the XML where these + /// fields comes in an arbitrary order + mod field_after_list { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Default, Deserialize)] + struct Root { + item: Vec<()>, + node: (), + } + + #[test] + fn before() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + item: vec![(), (), ()], + node: (), + } + ); + } + + #[test] + fn after() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + item: vec![(), (), ()], + node: (), + } + ); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + item: vec![(), (), ()], + node: (), + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `item`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + outer: Vec, + node: (), + } + + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + outer: vec![ + List { item: vec![()] }, + List { item: vec![()] }, + List { item: vec![()] }, + ], + node: (), + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), + e => panic!( + r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, + e + ), + } + } + } + + /// In those tests two lists are deserialized simultaneously. + /// Lists should be deserialized even when them overlaps + mod two_lists { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Pair { + item: Vec<()>, + element: Vec<()>, + } + + #[test] + fn splitted() { + let data: Pair = from_str( + r#" + + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Pair { + item: vec![(), (), ()], + element: vec![(), ()], + } + ); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + item: vec![(), (), ()], + element: vec![(), ()], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `item`"), + e => panic!( + r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, + e + ), + } + } + + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Pair { + outer: Vec, + element: Vec<()>, + } + + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + outer: vec![ + List { item: vec![()] }, + List { item: vec![()] }, + List { item: vec![()] }, + ], + element: vec![()], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), + e => panic!( + r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, + e + ), + } + } + } + + /// Deserialization of primitives slightly differs from deserialization + /// of complex types, so need to check this separately + #[test] + fn primitives() { + #[derive(Debug, PartialEq, Deserialize)] + struct List { + item: Vec, + } + + let data: List = from_str( + r#" + + 41 + 42 + 43 + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: vec![41, 42, 43], + } + ); + + from_str::( + r#" + + 41 + 42 + 43 + + "#, + ) + .unwrap_err(); + } + + /// This test ensures that composition of deserializer building blocks + /// plays well + #[test] + fn list_of_struct() { + #[derive(Debug, PartialEq, Default, Deserialize)] + #[serde(default)] + struct Struct { + #[serde(rename = "@attribute")] + attribute: Option, + element: Option, + } + + #[derive(Debug, PartialEq, Deserialize)] + struct List { + item: Vec, + } + + let data: List = from_str( + r#" + + + + + value + + + value + + + "#, + ) + .unwrap(); + assert_eq!( + data, + List { + item: vec![ + Struct { + attribute: None, + element: None, + }, + Struct { + attribute: Some("value".to_string()), + element: None, + }, + Struct { + attribute: None, + element: Some("value".to_string()), + }, + Struct { + attribute: Some("value".to_string()), + element: Some("value".to_string()), + }, + ], + } + ); + } + + /// Checks that sequences represented by elements can contain sequences, + /// represented by `xs:list`s + mod xs_list { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// Outer list mapped to elements, inner -- to `xs:list`. + /// + /// `#[serde(default)]` is required to correctly deserialize + /// empty sequence, because without elements the field + /// also is missing and derived `Deserialize` implementation + /// would complain about that unless field is marked as + /// `default`. + #[serde(default)] + item: Vec>, + } + + /// Special case: zero elements + #[test] + fn zero() { + let data: List = from_str( + r#" + + + "#, + ) + .unwrap(); + + assert_eq!(data, List { item: vec![] }); + } + + /// Special case: one element + #[test] + fn one() { + let data: List = from_str( + r#" + + first list + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: vec![vec!["first".to_string(), "list".to_string()]] + } + ); + } + + /// Special case: outer list is always mapped to an elements sequence, + /// not to an `xs:list` + #[test] + fn element() { + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// List mapped to elements, inner -- to `xs:list`. + /// + /// `#[serde(default)]` is not required, because correct + /// XML will always contains at least 1 element. + item: Vec, + } + + let data: List = from_str( + r#" + + first item + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: vec!["first item".to_string()] + } + ); + } + + /// This tests demonstrates, that for `$value` field (`list`) actual + /// name of XML element (`item`) does not matter. That allows list + /// item to be an enum, where tag name determines enum variant + #[test] + fn many() { + let data: List = from_str( + r#" + + first list + second list + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: vec![ + vec!["first".to_string(), "list".to_string()], + vec!["second".to_string(), "list".to_string()], + ] + } + ); + } + } + } +} + +/// Check that sequences inside element can be deserialized. +/// In terms of serde this is a sequence flatten into the struct: +/// +/// ```ignore +/// struct Root { +/// #[serde(flatten)] +/// items: Vec, +/// } +/// ``` +/// except that fact that this is not supported nowadays +/// (https://github.com/serde-rs/serde/issues/1905) +/// +/// Because this is very frequently used pattern in the XML, quick-xml +/// have a workaround for this. If a field will have a special name `$value` +/// then any `xs:element`s in the `xs:sequence` / `xs:all`, except that +/// which name matches the struct name, will be associated with this field: +/// +/// ```ignore +/// struct Root { +/// field: U, +/// #[serde(rename = "$value")] +/// items: Vec, +/// } +/// ``` +/// In this example `` tag will be associated with a `field` field, +/// but all other tags will be associated with an `items` field. Disadvantages +/// of this approach that you can have only one field, but usually you don't +/// want more +mod variable_name { + use super::*; + use serde::de::{Deserializer, EnumAccess, VariantAccess, Visitor}; + use std::fmt::{self, Formatter}; + + // NOTE: Derive could be possible once https://github.com/serde-rs/serde/issues/2126 is resolved + macro_rules! impl_deserialize_choice { + ($name:ident : $(($field:ident, $field_name:literal)),*) => { + impl<'de> Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(field_identifier)] + #[serde(rename_all = "kebab-case")] + enum Tag { + $($field,)* + Other(String), + } + + struct EnumVisitor; + impl<'de> Visitor<'de> for EnumVisitor { + type Value = $name; + + fn expecting(&self, f: &mut Formatter) -> fmt::Result { + f.write_str("enum ")?; + f.write_str(stringify!($name)) + } + + fn visit_enum(self, data: A) -> Result + where + A: EnumAccess<'de>, + { + match data.variant()? { + $( + (Tag::$field, variant) => variant.unit_variant().map(|_| $name::$field), + )* + (Tag::Other(t), v) => v.unit_variant().map(|_| $name::Other(t)), + } + } + } + + const VARIANTS: &'static [&'static str] = &[ + $($field_name,)* + "" + ]; + deserializer.deserialize_enum(stringify!($name), VARIANTS, EnumVisitor) + } + } + }; + } + + /// Type that can be deserialized from ``, ``, or any other element + #[derive(Debug, PartialEq)] + enum Choice { + One, + Two, + /// Any other tag name except `One` or `Two`, name of tag stored inside variant + Other(String), + } + impl_deserialize_choice!(Choice: (One, "one"), (Two, "two")); + + /// Type that can be deserialized from ``, ``, or any other element + #[derive(Debug, PartialEq)] + enum Choice2 { + First, + Second, + /// Any other tag name except `First` or `Second`, name of tag stored inside variant + Other(String), + } + impl_deserialize_choice!(Choice2: (First, "first"), (Second, "second")); + + /// Type that can be deserialized from ``, ``, or any other element. + /// Used for `primitives` tests + #[derive(Debug, PartialEq, Deserialize)] + #[serde(rename_all = "kebab-case")] + enum Choice3 { + One(usize), + Two(String), + #[serde(other)] + Other, + } + + #[derive(Debug, PartialEq, Deserialize)] + #[serde(rename_all = "kebab-case")] + enum Choice4 { + One { + inner: [(); 1], + }, + Two { + inner: [(); 1], + }, + #[serde(other)] + Other, + } + + /// This module contains tests where size of the list have a compile-time size + mod fixed_size { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct List { + #[serde(rename = "$value")] + item: [Choice; 3], + } + + /// Simple case: count of elements matches expected size of sequence, + /// each element has the same name. Successful deserialization expected + #[test] + fn simple() { + let data: List = from_str( + r#" + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + } + ); + } + + /// Special case: empty sequence + #[test] + #[ignore = "it is impossible to distinguish between missed field and empty list: use `Option<>` or #[serde(default)]"] + fn empty() { + #[derive(Debug, PartialEq, Deserialize)] + struct List { + #[serde(rename = "$value")] + item: [Choice; 0], + } + + from_str::(r#""#).unwrap(); + from_str::(r#""#).unwrap(); + } + + /// Special case: one-element sequence + #[test] + fn one_element() { + #[derive(Debug, PartialEq, Deserialize)] + struct List { + #[serde(rename = "$value")] + item: [Choice; 1], + } + + let data: List = from_str( + r#" + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: [Choice::One], + } + ); + } + + /// Fever elements than expected size of sequence, each element has + /// the same name. Failure expected + #[test] + fn fever_elements() { + from_str::( + r#" + + + + + "#, + ) + .unwrap_err(); + } + + /// More elements than expected size of sequence, each element has + /// the same name. Failure expected. If you wish to ignore excess + /// elements, use the special type, that consume as much elements + /// as possible, but ignores excess elements + #[test] + fn excess_elements() { + from_str::( + r#" + + + + + + + "#, + ) + .unwrap_err(); + } + + #[test] + fn mixed_content() { + #[derive(Debug, PartialEq, Deserialize)] + struct List { + /// Text and CDATA represents a one logical text item + #[serde(rename = "$value")] + item: [(); 2], + } + + from_str::( + r#" + + + text + + + "#, + ) + .unwrap(); + } + + // There cannot be unknown items, because any tag name is accepted + + /// In those tests non-sequential field is defined in the struct + /// before sequential, so it will be deserialized before the list. + /// That struct should be deserialized from the XML where these + /// fields comes in an arbitrary order + mod field_before_list { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + node: (), + #[serde(rename = "$value")] + item: [Choice; 3], + } + + #[test] + fn before() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + node: (), + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + } + ); + } + + #[test] + fn after() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + node: (), + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + } + ); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + node: (), + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + node: (), + #[serde(rename = "$value")] + item: [Choice4; 3], + } + + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + node: (), + item: [ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + } + + /// In those tests non-sequential field is defined in the struct + /// after sequential, so it will be deserialized after the list. + /// That struct should be deserialized from the XML where these + /// fields comes in an arbitrary order + mod field_after_list { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + #[serde(rename = "$value")] + item: [Choice; 3], + node: (), + } + + #[test] + fn before() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + node: (), + } + ); + } + + #[test] + fn after() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + node: (), + } + ); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + node: (), + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + #[serde(rename = "$value")] + item: [Choice4; 3], + node: (), + } + + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + item: [ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + node: (), + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + } + + /// In those tests two lists are deserialized simultaneously. + /// Lists should be deserialized even when them overlaps + mod two_lists { + use super::*; + + /// A field with a variable-name items defined before a field with a fixed-name + /// items + mod choice_and_fixed { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Pair { + #[serde(rename = "$value")] + item: [Choice; 3], + element: [(); 2], + } + + /// A list with fixed-name elements located before a list with variable-name + /// elements in an XML + #[test] + fn fixed_before() { + let data: Pair = from_str( + r#" + + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Pair { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + element: [(), ()], + } + ); + } + + /// A list with fixed-name elements located after a list with variable-name + /// elements in an XML + #[test] + fn fixed_after() { + let data: Pair = from_str( + r#" + + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Pair { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + element: [(), ()], + } + ); + } + + mod overlapped { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + #[serde(rename = "$value")] + item: [Choice4; 3], + element: [(); 2], + } + + /// A list with fixed-name elements are mixed with a list with variable-name + /// elements in an XML, and the first element is a fixed-name one + #[test] + fn fixed_before() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + element: [(), ()], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 2") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, + e + ), + } + } + + /// A list with fixed-name elements are mixed with a list with variable-name + /// elements in an XML, and the first element is a variable-name one + #[test] + fn fixed_after() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + element: [(), ()], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn with_nested_list_fixed_before() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + item: [ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + element: [(); 2], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 2") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn with_nested_list_fixed_after() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + item: [ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + element: [(); 2], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + } + } + + /// A field with a variable-name items defined after a field with a fixed-name + /// items + mod fixed_and_choice { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Pair { + element: [(); 2], + #[serde(rename = "$value")] + item: [Choice; 3], + } + + /// A list with fixed-name elements located before a list with variable-name + /// elements in an XML + #[test] + fn fixed_before() { + let data: Pair = from_str( + r#" + + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Pair { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + element: [(), ()], + } + ); + } + + /// A list with fixed-name elements located after a list with variable-name + /// elements in an XML + #[test] + fn fixed_after() { + let data: Pair = from_str( + r#" + + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Pair { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + element: [(), ()], + } + ); + } + + mod overlapped { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + element: [(); 2], + #[serde(rename = "$value")] + item: [Choice4; 3], + } + + /// A list with fixed-name elements are mixed with a list with variable-name + /// elements in an XML, and the first element is a fixed-name one + #[test] + fn fixed_before() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + element: [(), ()], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 2") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, + e + ), + } + } + + /// A list with fixed-name elements are mixed with a list with variable-name + /// elements in an XML, and the first element is a variable-name one + #[test] + fn fixed_after() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + element: [(), ()], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn with_nested_list_fixed_before() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + element: [(); 2], + item: [ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 2") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn with_nested_list_fixed_after() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + element: [(); 2], + item: [ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + } + } + + /// Tests are ignored, but exists to show a problem. + /// May be it will be solved in the future + mod choice_and_choice { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Pair { + #[serde(rename = "$value")] + item: [Choice; 3], + // Actually, we cannot rename both fields to `$value`, which is now + // required to indicate, that field accepts elements with any name + #[serde(rename = "$value")] + element: [Choice2; 2], + } + + #[test] + #[ignore = "There is no way to associate XML elements with `item` or `element` without extra knowledge from type"] + fn splitted() { + let data: Pair = from_str( + r#" + + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Pair { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + element: [Choice2::First, Choice2::Second], + } + ); + } + + #[test] + #[ignore = "There is no way to associate XML elements with `item` or `element` without extra knowledge from type"] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + item: [Choice::One, Choice::Two, Choice::Other("three".into())], + element: [Choice2::First, Choice2::Second], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + } + } + + /// Deserialization of primitives slightly differs from deserialization + /// of complex types, so need to check this separately + #[test] + fn primitives() { + #[derive(Debug, PartialEq, Deserialize)] + struct List { + #[serde(rename = "$value")] + item: [Choice3; 3], + } + + let data: List = from_str( + r#" + + 41 + 42 + 43 + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: [ + Choice3::One(41), + Choice3::Two("42".to_string()), + Choice3::Other, + ], + } + ); + + from_str::( + r#" + + 41 + 42 + 43 + + "#, + ) + .unwrap_err(); + } + + /// Checks that sequences represented by elements can contain sequences, + /// represented by `xs:list`s + mod xs_list { + use super::*; + use pretty_assertions::assert_eq; + + /// Special case: zero elements + #[test] + fn zero() { + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// Outer list mapped to elements, inner -- to `xs:list`. + /// + /// `#[serde(default)]` is required to correctly deserialize + /// empty sequence, because without elements the field + /// also is missing and derived `Deserialize` implementation + /// would complain about that unless field is marked as + /// `default`. + #[serde(default)] + #[serde(rename = "$value")] + element: [Vec; 0], + } + + let data: List = from_str( + r#" + + + "#, + ) + .unwrap(); + + assert_eq!(data, List { element: [] }); + } + + /// Special case: one element + #[test] + fn one() { + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// Outer list mapped to elements, inner -- to `xs:list`. + /// + /// `#[serde(default)]` is not required, because correct + /// XML will always contains at least 1 element. + #[serde(rename = "$value")] + element: [Vec; 1], + } + + let data: List = from_str( + r#" + + first list + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + element: [vec!["first".to_string(), "list".to_string()]] + } + ); + } + + /// Special case: outer list is always mapped to an elements sequence, + /// not to an `xs:list` + #[test] + fn element() { + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// List mapped to elements, String -- to `xs:list`. + /// + /// `#[serde(default)]` is not required, because correct + /// XML will always contains at least 1 element. + #[serde(rename = "$value")] + element: [String; 1], + } + + let data: List = from_str( + r#" + + first item + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + element: ["first item".to_string()] + } + ); + } + + #[test] + fn many() { + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// Outer list mapped to elements, inner -- to `xs:list` + #[serde(rename = "$value")] + element: [Vec; 2], + } + + let data: List = from_str( + r#" + + first list + second list + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + element: [ + vec!["first".to_string(), "list".to_string()], + vec!["second".to_string(), "list".to_string()], + ] + } + ); + } + } + } + + /// This module contains tests where size of the list have an unspecified size + mod variable_size { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct List { + #[serde(rename = "$value")] + item: Vec, + } + + /// Simple case: count of elements matches expected size of sequence, + /// each element has the same name. Successful deserialization expected + #[test] + fn simple() { + let data: List = from_str( + r#" + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + } + ); + } + + /// Special case: empty sequence + #[test] + #[ignore = "it is impossible to distinguish between missed field and empty list: use `Option<>` or #[serde(default)]"] + fn empty() { + let data = from_str::(r#""#).unwrap(); + assert_eq!(data, List { item: vec![] }); + + let data = from_str::(r#""#).unwrap(); + assert_eq!(data, List { item: vec![] }); + } + + /// Special case: one-element sequence + #[test] + fn one_element() { + let data: List = from_str( + r#" + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: vec![Choice::One], + } + ); + } + + #[test] + fn mixed_content() { + #[derive(Debug, PartialEq, Deserialize)] + struct List { + #[serde(rename = "$value")] + item: Vec<()>, + } + + let data: List = from_str( + r#" + + + text + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + // Text and CDATA represents a one logical text item + item: vec![(), ()], + } + ); + } + + // There cannot be unknown items, because any tag name is accepted + + /// In those tests non-sequential field is defined in the struct + /// before sequential, so it will be deserialized before the list. + /// That struct should be deserialized from the XML where these + /// fields comes in an arbitrary order + mod field_before_list { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + node: (), + #[serde(rename = "$value")] + item: Vec, + } + + #[test] + fn before() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + node: (), + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + } + ); + } + + #[test] + fn after() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + node: (), + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + } + ); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + node: (), + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `$value`"), + e => panic!( + r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + node: (), + #[serde(rename = "$value")] + item: Vec, + } + + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + node: (), + item: vec![ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `$value`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + e + ), + } + } + } + + /// In those tests non-sequential field is defined in the struct + /// after sequential, so it will be deserialized after the list. + /// That struct should be deserialized from the XML where these + /// fields comes in an arbitrary order + mod field_after_list { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + #[serde(rename = "$value")] + item: Vec, + node: (), + } + + #[test] + fn before() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + node: (), + } + ); + } + + #[test] + fn after() { + let data: Root = from_str( + r#" + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Root { + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + node: (), + } + ); + } + + #[test] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + node: (), + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `$value`"), + e => panic!( + r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn overlapped_with_nested_list() { + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + #[serde(rename = "$value")] + item: Vec, + node: (), + } + + let data = from_str::( + r#" + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + item: vec![ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + node: (), + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `$value`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + e + ), + } + } + } + + /// In those tests two lists are deserialized simultaneously. + /// Lists should be deserialized even when them overlaps + mod two_lists { + use super::*; + + /// A field with a variable-name items defined before a field with a fixed-name + /// items + mod choice_and_fixed { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Pair { + #[serde(rename = "$value")] + item: Vec, + element: Vec<()>, + } + + /// A list with fixed-name elements located before a list with variable-name + /// elements in an XML + #[test] + fn fixed_before() { + let data: Pair = from_str( + r#" + + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Pair { + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + element: vec![(), ()], + } + ); + } + + /// A list with fixed-name elements located after a list with variable-name + /// elements in an XML + #[test] + fn fixed_after() { + let data: Pair = from_str( + r#" + + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Pair { + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + element: vec![(), ()], + } + ); + } + + mod overlapped { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + #[serde(rename = "$value")] + item: Vec, + element: Vec<()>, + } + + /// A list with fixed-name elements are mixed with a list with variable-name + /// elements in an XML, and the first element is a fixed-name one + #[test] + fn fixed_before() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + item: vec![ + Choice::One, + Choice::Two, + Choice::Other("three".into()) + ], + element: vec![(), ()], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `element`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, + e + ), + } + } + + /// A list with fixed-name elements are mixed with a list with variable-name + /// elements in an XML, and the first element is a variable-name one + #[test] + fn fixed_after() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + item: vec![ + Choice::One, + Choice::Two, + Choice::Other("three".into()) + ], + element: vec![(), ()], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `$value`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn with_nested_list_fixed_before() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + item: vec![ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + element: vec![(); 2], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `element`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn with_nested_list_fixed_after() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + item: vec![ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + element: vec![(); 2], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `$value`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + e + ), + } + } + } + } + + /// A field with a variable-name items defined after a field with a fixed-name + /// items + mod fixed_and_choice { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Pair { + element: Vec<()>, + #[serde(rename = "$value")] + item: Vec, + } + + /// A list with fixed-name elements located before a list with variable-name + /// elements in an XML + #[test] + fn fixed_before() { + let data: Pair = from_str( + r#" + + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Pair { + element: vec![(), ()], + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + } + ); + } + + /// A list with fixed-name elements located after a list with variable-name + /// elements in an XML + #[test] + fn fixed_after() { + let data: Pair = from_str( + r#" + + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Pair { + element: vec![(), ()], + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + } + ); + } + + mod overlapped { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + element: Vec<()>, + #[serde(rename = "$value")] + item: Vec, + } + + /// A list with fixed-name elements are mixed with a list with variable-name + /// elements in an XML, and the first element is a fixed-name one + #[test] + fn fixed_before() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + element: vec![(), ()], + item: vec![ + Choice::One, + Choice::Two, + Choice::Other("three".into()) + ], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `element`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, + e + ), + } + } + + /// A list with fixed-name elements are mixed with a list with variable-name + /// elements in an XML, and the first element is a variable-name one + #[test] + fn fixed_after() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + element: vec![(), ()], + item: vec![ + Choice::One, + Choice::Two, + Choice::Other("three".into()) + ], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `$value`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn with_nested_list_fixed_before() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + element: vec![(); 2], + item: vec![ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `element`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, + e + ), + } + } + + /// Test for https://github.com/tafia/quick-xml/issues/435 + #[test] + fn with_nested_list_fixed_after() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Root { + element: vec![(); 2], + item: vec![ + Choice4::One { inner: [()] }, + Choice4::Two { inner: [()] }, + Choice4::Other, + ], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "duplicate field `$value`") + } + e => panic!( + r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + e + ), + } + } + } + } + + /// Tests are ignored, but exists to show a problem. + /// May be it will be solved in the future + mod choice_and_choice { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize)] + struct Pair { + #[serde(rename = "$value")] + item: Vec, + // Actually, we cannot rename both fields to `$value`, which is now + // required to indicate, that field accepts elements with any name + #[serde(rename = "$value")] + element: Vec, + } + + #[test] + #[ignore = "There is no way to associate XML elements with `item` or `element` without extra knowledge from type"] + fn splitted() { + let data: Pair = from_str( + r#" + + + + + + + + "#, + ) + .unwrap(); + + assert_eq!( + data, + Pair { + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + element: vec![Choice2::First, Choice2::Second], + } + ); + } + + #[test] + #[ignore = "There is no way to associate XML elements with `item` or `element` without extra knowledge from type"] + fn overlapped() { + let data = from_str::( + r#" + + + + + + + + "#, + ); + + #[cfg(feature = "overlapped-lists")] + assert_eq!( + data.unwrap(), + Pair { + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], + element: vec![Choice2::First, Choice2::Second], + } + ); + + #[cfg(not(feature = "overlapped-lists"))] + match data { + Err(DeError::Custom(e)) => { + assert_eq!(e, "invalid length 1, expected an array of length 3") + } + e => panic!( + r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + e + ), + } + } + } + } + + /// Deserialization of primitives slightly differs from deserialization + /// of complex types, so need to check this separately + #[test] + fn primitives() { + #[derive(Debug, PartialEq, Deserialize)] + struct List { + #[serde(rename = "$value")] + item: Vec, + } + + let data: List = from_str( + r#" + + 41 + 42 + 43 + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + item: vec![ + Choice3::One(41), + Choice3::Two("42".to_string()), + Choice3::Other, + ], + } + ); + + from_str::( + r#" + + 41 + 42 + 43 + + "#, + ) + .unwrap_err(); + } + + /// Checks that sequences represented by elements can contain sequences, + /// represented by `xs:list`s + mod xs_list { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// Outer list mapped to elements, inner -- to `xs:list`. + /// + /// `#[serde(default)]` is required to correctly deserialize + /// empty sequence, because without elements the field + /// also is missing and derived `Deserialize` implementation + /// would complain about that unless field is marked as + /// `default`. + #[serde(default)] + #[serde(rename = "$value")] + element: Vec>, + } + + /// Special case: zero elements + #[test] + fn zero() { + let data: List = from_str( + r#" + + + "#, + ) + .unwrap(); + + assert_eq!(data, List { element: vec![] }); + } + + /// Special case: one element + #[test] + fn one() { + let data: List = from_str( + r#" + + first list + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + element: vec![vec!["first".to_string(), "list".to_string()]] + } + ); + } + + /// Special case: outer list is always mapped to an elements sequence, + /// not to an `xs:list` + #[test] + fn element() { + #[derive(Debug, Deserialize, PartialEq)] + struct List { + /// Outer list mapped to elements. + #[serde(rename = "$value")] + element: Vec, + } + + let data: List = from_str( + r#" + + first item + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + element: vec!["first item".to_string()] + } + ); + } + + /// This tests demonstrates, that for `$value` field (`list`) actual + /// name of XML element (`item`) does not matter. That allows list + /// item to be an enum, where tag name determines enum variant + #[test] + fn many() { + let data: List = from_str( + r#" + + first list + second list + + "#, + ) + .unwrap(); + + assert_eq!( + data, + List { + element: vec![ + vec!["first".to_string(), "list".to_string()], + vec!["second".to_string(), "list".to_string()], + ] + } + ); + } + } + } +} diff --git a/tests/serde-de.rs b/tests/serde-de.rs index 554abac4..f31ed5eb 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -8,27 +8,8 @@ use serde::de::IgnoredAny; use serde::serde_if_integer128; use serde::Deserialize; -/// Deserialize an instance of type T from a string of XML text. -/// If deserialization was succeeded checks that all XML events was consumed -fn from_str<'de, T>(s: &'de str) -> Result -where - T: Deserialize<'de>, -{ - // Log XML that we try to deserialize to see it in the failed tests output - dbg!(s); - let mut de = Deserializer::from_str(s); - let result = T::deserialize(&mut de); - - // If type was deserialized, the whole XML document should be consumed - if let Ok(_) = result { - match <()>::deserialize(&mut de) { - Err(DeError::UnexpectedEof) => (), - e => panic!("Expected end `UnexpectedEof`, but got {:?}", e), - } - } - - result -} +mod helpers; +use helpers::from_str; /// Tests for deserializing into specially named field `$text` which represent /// textual content of an XML element @@ -471,5624 +452,517 @@ mod tuple_struct { } } -mod seq { - use super::*; - - /// Check that top-level sequences can be deserialized from the multi-root XML documents - mod top_level { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn simple() { - from_str::<[(); 3]>("42answer").unwrap(); - - let data: Vec<()> = from_str("42answer").unwrap(); - assert_eq!(data, vec![(), (), ()]); - } - - /// Special case: empty sequence - #[test] - fn empty() { - from_str::<[(); 0]>("").unwrap(); - - let data: Vec<()> = from_str("").unwrap(); - assert_eq!(data, vec![]); - } - - /// Special case: one-element sequence - #[test] - fn one_element() { - from_str::<[(); 1]>("").unwrap(); - from_str::<[(); 1]>("42").unwrap(); - from_str::<[(); 1]>("text").unwrap(); - from_str::<[(); 1]>("").unwrap(); - - let data: Vec<()> = from_str("").unwrap(); - assert_eq!(data, vec![()]); - - let data: Vec<()> = from_str("42").unwrap(); - assert_eq!(data, vec![()]); - - let data: Vec<()> = from_str("text").unwrap(); - assert_eq!(data, vec![()]); - - let data: Vec<()> = from_str("").unwrap(); - assert_eq!(data, vec![()]); - } - - #[test] - fn excess_attribute() { - from_str::<[(); 3]>(r#"42answer"#) - .unwrap(); - - let data: Vec<()> = - from_str(r#"42answer"#) - .unwrap(); - assert_eq!(data, vec![(), (), ()]); - } +// seq tests are so big, so it in the separate file serde-de-seq.rs to speed-up compilation - #[test] - fn mixed_content() { - // Text and CDATA represents a one logical text item - from_str::<[(); 2]>( - r#" - - text - - "#, - ) - .unwrap(); +macro_rules! maplike_errors { + ($type:ty) => { + maplike_errors!($type, $type); + }; + ( + $attributes:ty, + $mixed:ty + ) => { + mod non_closed { + use super::*; - let data: Vec<()> = from_str( - r#" - - text - - "#, - ) - .unwrap(); - // Text and CDATA represents a one logical text item - assert_eq!(data, vec![(), ()]); - } + /// For struct we expect that error about not closed tag appears + /// earlier than error about missing fields + #[test] + fn missing_field() { + let data = from_str::<$mixed>(r#""#); - /// This test ensures that composition of deserializer building blocks plays well - #[test] - fn list_of_struct() { - #[derive(Debug, PartialEq, Default, Deserialize)] - #[serde(default)] - struct Struct { - #[serde(rename = "@attribute")] - attribute: Option, - element: Option, + match data { + Err(DeError::UnexpectedEof) => (), + _ => panic!("Expected `UnexpectedEof`, found {:?}", data), + } } - let data: Vec = from_str( - r#" - - - - value - - - value - - "#, - ) - .unwrap(); - assert_eq!( - data, - vec![ - Struct { - attribute: None, - element: None, - }, - Struct { - attribute: Some("value".to_string()), - element: None, - }, - Struct { - attribute: None, - element: Some("value".to_string()), - }, - Struct { - attribute: Some("value".to_string()), - element: Some("value".to_string()), - }, - ] - ); - } + #[test] + fn attributes() { + let data = from_str::<$attributes>(r#""#); - /// Test for https://github.com/tafia/quick-xml/issues/500 - #[test] - fn list_of_enum() { - #[derive(Debug, PartialEq, Deserialize)] - enum Enum { - One, - Two, + match data { + Err(DeError::UnexpectedEof) => (), + _ => panic!("Expected `UnexpectedEof`, found {:?}", data), + } } - let data: Vec = from_str( - r#" - - - - "#, - ) - .unwrap(); - assert_eq!(data, vec![Enum::One, Enum::Two, Enum::One]); - } - } - - /// Tests where each sequence item have an identical name in an XML. - /// That explicitly means that `enum`s as list elements are not supported - /// in that case, because enum requires different tags. - /// - /// (by `enums` we mean [externally tagged enums] is serde terminology) - /// - /// [externally tagged enums]: https://serde.rs/enum-representations.html#externally-tagged - mod fixed_name { - use super::*; - - /// This module contains tests where size of the list have a compile-time size - mod fixed_size { - use super::*; - use pretty_assertions::assert_eq; + #[test] + fn elements_root() { + let data = from_str::<$mixed>(r#"answer"#); - #[derive(Debug, PartialEq, Deserialize)] - struct List { - item: [(); 3], + match data { + Err(DeError::UnexpectedEof) => (), + _ => panic!("Expected `UnexpectedEof`, found {:?}", data), + } } - /// Simple case: count of elements matches expected size of sequence, - /// each element has the same name. Successful deserialization expected #[test] - fn simple() { - from_str::( - r#" - - - - - - "#, - ) - .unwrap(); - } + fn elements_child() { + let data = from_str::<$mixed>(r#"answer"#); - /// Special case: empty sequence - #[test] - #[ignore = "it is impossible to distinguish between missed field and empty list: use `Option<>` or #[serde(default)]"] - fn empty() { - #[derive(Debug, PartialEq, Deserialize)] - struct List { - item: [(); 0], + match data { + Err(DeError::UnexpectedEof) => (), + _ => panic!("Expected `UnexpectedEof`, found {:?}", data), } - - from_str::(r#""#).unwrap(); - from_str::(r#""#).unwrap(); } + } + + mod mismatched_end { + use super::*; + use quick_xml::Error::EndEventMismatch; - /// Special case: one-element sequence + /// For struct we expect that error about mismatched tag appears + /// earlier than error about missing fields #[test] - fn one_element() { - #[derive(Debug, PartialEq, Deserialize)] - struct List { - item: [(); 1], - } + fn missing_field() { + let data = from_str::<$mixed>(r#""#); - from_str::( - r#" - - - - "#, - ) - .unwrap(); + match data { + Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), + _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), + } } - /// Fever elements than expected size of sequence, each element has - /// the same name. Failure expected #[test] - fn fever_elements() { - let data = from_str::( - r#" - - - - - "#, + fn attributes() { + let data = from_str::<$attributes>( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, ); match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 2, expected an array of length 3") - } - e => panic!( - r#"Expected `Err(Custom("invalid length 2, expected an array of length 3"))`, but found {:?}"#, - e - ), + Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), + _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), } } - /// More elements than expected size of sequence, each element has - /// the same name. Failure expected. If you wish to ignore excess - /// elements, use the special type, that consume as much elements - /// as possible, but ignores excess elements #[test] - fn excess_elements() { - let data = from_str::( - r#" - - - - - - - "#, + fn elements_root() { + let data = from_str::<$mixed>( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"answer"#, ); match data { - Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `item`"), - e => panic!( - r#"Expected `Err(Custom("duplicate field `item`"))`, but found {:?}"#, - e - ), + Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), + _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), } } - /// Mixed content assumes, that some elements will have an internal - /// name `$text` or `$value`, so, unless field named the same, it is expected - /// to fail #[test] - fn mixed_content() { - let data = from_str::( - r#" - - - text - - - "#, + fn elements_child() { + let data = from_str::<$mixed>( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"answer"#, ); match data { - Err(DeError::Custom(e)) => assert_eq!(e, "missing field `item`"), - e => panic!( - r#"Expected `Err(Custom("missing field `item`"))`, but found {:?}"#, - e - ), + Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), + _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), } } + } + }; +} - /// In those tests sequence should be deserialized from an XML - /// with additional elements that is not defined in the struct. - /// That fields should be skipped during deserialization - mod unknown_items { - use super::*; - #[cfg(not(feature = "overlapped-lists"))] - use pretty_assertions::assert_eq; - - #[test] - fn before() { - from_str::( - r#" - - - - - - - "#, - ) - .unwrap(); - } +mod map { + use super::*; + use pretty_assertions::assert_eq; + use std::collections::HashMap; + use std::iter::FromIterator; - #[test] - fn after() { - from_str::( - r#" - - - - - - - "#, - ) - .unwrap(); - } + #[test] + fn elements() { + let data: HashMap<(), ()> = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"42answer"#, + ) + .unwrap(); + assert_eq!( + data, + HashMap::from_iter([((), ()), ((), ()),].iter().cloned()) + ); + } - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - data.unwrap(); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } + #[test] + fn attributes() { + let data: HashMap<(), ()> = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + HashMap::from_iter([((), ()), ((), ()),].iter().cloned()) + ); + } - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - outer: [List; 3], - } + #[test] + fn attribute_and_element() { + let data: HashMap<(), ()> = from_str( + r#" + + answer + + "#, + ) + .unwrap(); - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - data.unwrap(); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - } + assert_eq!( + data, + HashMap::from_iter([((), ()), ((), ()),].iter().cloned()) + ); + } - /// In those tests non-sequential field is defined in the struct - /// before sequential, so it will be deserialized before the list. - /// That struct should be deserialized from an XML where these - /// fields comes in an arbitrary order - mod field_before_list { - use super::*; - #[cfg(not(feature = "overlapped-lists"))] - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - node: (), - item: [(); 3], - } + maplike_errors!(HashMap<(), ()>); +} - #[test] - fn before() { - from_str::( - r#" - - - - - - - "#, - ) - .unwrap(); - } +mod struct_ { + use super::*; + use pretty_assertions::assert_eq; - #[test] - fn after() { - from_str::( - r#" - - - - - - - "#, - ) - .unwrap(); - } + /// Type where all struct fields represented by elements + #[derive(Debug, Deserialize, PartialEq)] + struct Elements { + float: f64, + string: String, + } - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - data.unwrap(); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } + /// Type where all struct fields represented by attributes + #[derive(Debug, Deserialize, PartialEq)] + struct Attributes { + #[serde(rename = "@float")] + float: f64, + #[serde(rename = "@string")] + string: String, + } - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - node: (), - outer: [List; 3], - } + /// Type where one field represented by an attribute and one by an element + #[derive(Debug, Deserialize, PartialEq)] + struct Mixed { + #[serde(rename = "@float")] + float: f64, + string: String, + } - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - data.unwrap(); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } + #[test] + fn elements() { + let data: Elements = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"42answer"#, + ) + .unwrap(); + assert_eq!( + data, + Elements { + float: 42.0, + string: "answer".into() } + ); + } - /// In those tests non-sequential field is defined in the struct - /// after sequential, so it will be deserialized after the list. - /// That struct should be deserialized from an XML where these - /// fields comes in an arbitrary order - mod field_after_list { - use super::*; - #[cfg(not(feature = "overlapped-lists"))] - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - item: [(); 3], - node: (), - } - - #[test] - fn before() { - from_str::( - r#" - - - - - - - "#, - ) - .unwrap(); - } - - #[test] - fn after() { - from_str::( - r#" - - - - - - - "#, - ) - .unwrap(); - } - - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - data.unwrap(); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - outer: [List; 3], - node: (), - } - - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - data.unwrap(); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - } - - /// In those tests two lists are deserialized simultaneously. - /// Lists should be deserialized even when them overlaps - mod two_lists { - use super::*; - #[cfg(not(feature = "overlapped-lists"))] - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Pair { - item: [(); 3], - element: [(); 2], - } - - #[test] - fn splitted() { - from_str::( - r#" - - - - - - - - "#, - ) - .unwrap(); - } - - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - data.unwrap(); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Pair { - outer: [List; 3], - element: [(); 2], - } - - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - data.unwrap(); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - } - - /// Deserialization of primitives slightly differs from deserialization - /// of complex types, so need to check this separately - #[test] - fn primitives() { - #[derive(Debug, PartialEq, Deserialize)] - struct List { - item: [usize; 3], - } - - let data: List = from_str( - r#" - - 41 - 42 - 43 - - "#, - ) - .unwrap(); - assert_eq!(data, List { item: [41, 42, 43] }); - - from_str::( - r#" - - 41 - 42 - 43 - - "#, - ) - .unwrap_err(); - } - - /// This test ensures that composition of deserializer building blocks - /// plays well - #[test] - fn list_of_struct() { - #[derive(Debug, PartialEq, Default, Deserialize)] - #[serde(default)] - struct Struct { - #[serde(rename = "@attribute")] - attribute: Option, - element: Option, - } - - #[derive(Debug, PartialEq, Deserialize)] - struct List { - item: [Struct; 4], - } - - let data: List = from_str( - r#" - - - - - value - - - value - - - "#, - ) - .unwrap(); - assert_eq!( - data, - List { - item: [ - Struct { - attribute: None, - element: None, - }, - Struct { - attribute: Some("value".to_string()), - element: None, - }, - Struct { - attribute: None, - element: Some("value".to_string()), - }, - Struct { - attribute: Some("value".to_string()), - element: Some("value".to_string()), - }, - ], - } - ); - } - - /// Checks that sequences represented by elements can contain sequences, - /// represented by [`xs:list`s](https://www.w3schools.com/xml/el_list.asp) - mod xs_list { - use super::*; - use pretty_assertions::assert_eq; - - /// Special case: zero elements - #[test] - fn zero() { - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// Outer list mapped to elements, inner -- to `xs:list`. - /// - /// `#[serde(default)]` is required to correctly deserialize - /// empty sequence, because without elements the field - /// also is missing and derived `Deserialize` implementation - /// would complain about that unless field is marked as - /// `default`. - #[serde(default)] - item: [Vec; 0], - } - - let data: List = from_str( - r#" - - - "#, - ) - .unwrap(); - - assert_eq!(data, List { item: [] }); - } - - /// Special case: one element - #[test] - fn one() { - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// Outer list mapped to elements, inner -- to `xs:list`. - /// - /// `#[serde(default)]` is not required, because correct - /// XML will always contains at least 1 element. - item: [Vec; 1], - } - - let data: List = from_str( - r#" - - first list - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: [vec!["first".to_string(), "list".to_string()]] - } - ); - } - - /// Special case: outer list is always mapped to an elements sequence, - /// not to an `xs:list` - #[test] - fn element() { - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// Outer list mapped to elements, inner -- to `xs:list`. - /// - /// `#[serde(default)]` is not required, because correct - /// XML will always contains at least 1 element. - item: [String; 1], - } - - let data: List = from_str( - r#" - - first item - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: ["first item".to_string()] - } - ); - } - - /// This tests demonstrates, that for `$value` field (`list`) actual - /// name of XML element (`item`) does not matter. That allows list - /// item to be an enum, where tag name determines enum variant - #[test] - fn many() { - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// Outer list mapped to elements, inner -- to `xs:list`. - /// - /// `#[serde(default)]` is not required, because correct - /// XML will always contains at least 1 element. - item: [Vec; 2], - } - - let data: List = from_str( - r#" - - first list - second list - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: [ - vec!["first".to_string(), "list".to_string()], - vec!["second".to_string(), "list".to_string()], - ] - } - ); - } - } - } - - /// This module contains tests where size of the list have an unspecified size - mod variable_size { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct List { - item: Vec<()>, - } - - /// Simple case: count of elements matches expected size of sequence, - /// each element has the same name. Successful deserialization expected - #[test] - fn simple() { - let data: List = from_str( - r#" - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: vec![(), (), ()], - } - ); - } - - /// Special case: empty sequence - #[test] - #[ignore = "it is impossible to distinguish between missed field and empty list: use `Option<>` or #[serde(default)]"] - fn empty() { - let data: List = from_str(r#""#).unwrap(); - assert_eq!(data, List { item: vec![] }); - - let data: List = from_str(r#""#).unwrap(); - assert_eq!(data, List { item: vec![] }); - } - - /// Special case: one-element sequence - #[test] - fn one_element() { - let data: List = from_str( - r#" - - - - "#, - ) - .unwrap(); - - assert_eq!(data, List { item: vec![()] }); - } - - /// Mixed content assumes, that some elements will have an internal - /// name `$text` or `$value`, so, unless field named the same, it is expected - /// to fail - #[test] - fn mixed_content() { - let data = from_str::( - r#" - - - text - - - "#, - ); - - match data { - Err(DeError::Custom(e)) => assert_eq!(e, "missing field `item`"), - e => panic!( - r#"Expected `Err(Custom("missing field `item`"))`, but found {:?}"#, - e - ), - } - } - - /// In those tests sequence should be deserialized from the XML - /// with additional elements that is not defined in the struct. - /// That fields should be skipped during deserialization - mod unknown_items { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn before() { - let data: List = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: vec![(), (), ()], - } - ); - } - - #[test] - fn after() { - let data: List = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: vec![(), (), ()], - } - ); - } - - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - List { - item: vec![(), (), ()], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `item`"), - e => panic!( - r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - outer: Vec, - } - - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - outer: vec![ - List { item: vec![()] }, - List { item: vec![()] }, - List { item: vec![()] }, - ], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), - e => panic!( - r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, - e - ), - } - } - } - - /// In those tests non-sequential field is defined in the struct - /// before sequential, so it will be deserialized before the list. - /// That struct should be deserialized from the XML where these - /// fields comes in an arbitrary order - mod field_before_list { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Default, Deserialize)] - struct Root { - node: (), - item: Vec<()>, - } - - #[test] - fn before() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - node: (), - item: vec![(), (), ()], - } - ); - } - - #[test] - fn after() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - node: (), - item: vec![(), (), ()], - } - ); - } - - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - node: (), - item: vec![(), (), ()], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `item`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - node: (), - outer: Vec, - } - - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - node: (), - outer: vec![ - List { item: vec![()] }, - List { item: vec![()] }, - List { item: vec![()] }, - ], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), - e => panic!( - r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, - e - ), - } - } - } - - /// In those tests non-sequential field is defined in the struct - /// after sequential, so it will be deserialized after the list. - /// That struct should be deserialized from the XML where these - /// fields comes in an arbitrary order - mod field_after_list { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Default, Deserialize)] - struct Root { - item: Vec<()>, - node: (), - } - - #[test] - fn before() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - item: vec![(), (), ()], - node: (), - } - ); - } - - #[test] - fn after() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - item: vec![(), (), ()], - node: (), - } - ); - } - - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - item: vec![(), (), ()], - node: (), - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `item`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - outer: Vec, - node: (), - } - - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - outer: vec![ - List { item: vec![()] }, - List { item: vec![()] }, - List { item: vec![()] }, - ], - node: (), - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), - e => panic!( - r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, - e - ), - } - } - } - - /// In those tests two lists are deserialized simultaneously. - /// Lists should be deserialized even when them overlaps - mod two_lists { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Pair { - item: Vec<()>, - element: Vec<()>, - } - - #[test] - fn splitted() { - let data: Pair = from_str( - r#" - - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Pair { - item: vec![(), (), ()], - element: vec![(), ()], - } - ); - } - - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - item: vec![(), (), ()], - element: vec![(), ()], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `item`"), - e => panic!( - r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, - e - ), - } - } - - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Pair { - outer: Vec, - element: Vec<()>, - } - - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - outer: vec![ - List { item: vec![()] }, - List { item: vec![()] }, - List { item: vec![()] }, - ], - element: vec![()], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), - e => panic!( - r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, - e - ), - } - } - } - - /// Deserialization of primitives slightly differs from deserialization - /// of complex types, so need to check this separately - #[test] - fn primitives() { - #[derive(Debug, PartialEq, Deserialize)] - struct List { - item: Vec, - } - - let data: List = from_str( - r#" - - 41 - 42 - 43 - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: vec![41, 42, 43], - } - ); - - from_str::( - r#" - - 41 - 42 - 43 - - "#, - ) - .unwrap_err(); - } - - /// This test ensures that composition of deserializer building blocks - /// plays well - #[test] - fn list_of_struct() { - #[derive(Debug, PartialEq, Default, Deserialize)] - #[serde(default)] - struct Struct { - #[serde(rename = "@attribute")] - attribute: Option, - element: Option, - } - - #[derive(Debug, PartialEq, Deserialize)] - struct List { - item: Vec, - } - - let data: List = from_str( - r#" - - - - - value - - - value - - - "#, - ) - .unwrap(); - assert_eq!( - data, - List { - item: vec![ - Struct { - attribute: None, - element: None, - }, - Struct { - attribute: Some("value".to_string()), - element: None, - }, - Struct { - attribute: None, - element: Some("value".to_string()), - }, - Struct { - attribute: Some("value".to_string()), - element: Some("value".to_string()), - }, - ], - } - ); - } - - /// Checks that sequences represented by elements can contain sequences, - /// represented by `xs:list`s - mod xs_list { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// Outer list mapped to elements, inner -- to `xs:list`. - /// - /// `#[serde(default)]` is required to correctly deserialize - /// empty sequence, because without elements the field - /// also is missing and derived `Deserialize` implementation - /// would complain about that unless field is marked as - /// `default`. - #[serde(default)] - item: Vec>, - } - - /// Special case: zero elements - #[test] - fn zero() { - let data: List = from_str( - r#" - - - "#, - ) - .unwrap(); - - assert_eq!(data, List { item: vec![] }); - } - - /// Special case: one element - #[test] - fn one() { - let data: List = from_str( - r#" - - first list - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: vec![vec!["first".to_string(), "list".to_string()]] - } - ); - } - - /// Special case: outer list is always mapped to an elements sequence, - /// not to an `xs:list` - #[test] - fn element() { - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// List mapped to elements, inner -- to `xs:list`. - /// - /// `#[serde(default)]` is not required, because correct - /// XML will always contains at least 1 element. - item: Vec, - } - - let data: List = from_str( - r#" - - first item - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: vec!["first item".to_string()] - } - ); - } - - /// This tests demonstrates, that for `$value` field (`list`) actual - /// name of XML element (`item`) does not matter. That allows list - /// item to be an enum, where tag name determines enum variant - #[test] - fn many() { - let data: List = from_str( - r#" - - first list - second list - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: vec![ - vec!["first".to_string(), "list".to_string()], - vec!["second".to_string(), "list".to_string()], - ] - } - ); - } - } - } - } - - /// Check that sequences inside element can be deserialized. - /// In terms of serde this is a sequence flatten into the struct: - /// - /// ```ignore - /// struct Root { - /// #[serde(flatten)] - /// items: Vec, - /// } - /// ``` - /// except that fact that this is not supported nowadays - /// (https://github.com/serde-rs/serde/issues/1905) - /// - /// Because this is very frequently used pattern in the XML, quick-xml - /// have a workaround for this. If a field will have a special name `$value` - /// then any `xs:element`s in the `xs:sequence` / `xs:all`, except that - /// which name matches the struct name, will be associated with this field: - /// - /// ```ignore - /// struct Root { - /// field: U, - /// #[serde(rename = "$value")] - /// items: Vec, - /// } - /// ``` - /// In this example `` tag will be associated with a `field` field, - /// but all other tags will be associated with an `items` field. Disadvantages - /// of this approach that you can have only one field, but usually you don't - /// want more - mod variable_name { - use super::*; - use serde::de::{Deserializer, EnumAccess, VariantAccess, Visitor}; - use std::fmt::{self, Formatter}; - - // NOTE: Derive could be possible once https://github.com/serde-rs/serde/issues/2126 is resolved - macro_rules! impl_deserialize_choice { - ($name:ident : $(($field:ident, $field_name:literal)),*) => { - impl<'de> Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(field_identifier)] - #[serde(rename_all = "kebab-case")] - enum Tag { - $($field,)* - Other(String), - } - - struct EnumVisitor; - impl<'de> Visitor<'de> for EnumVisitor { - type Value = $name; - - fn expecting(&self, f: &mut Formatter) -> fmt::Result { - f.write_str("enum ")?; - f.write_str(stringify!($name)) - } - - fn visit_enum(self, data: A) -> Result - where - A: EnumAccess<'de>, - { - match data.variant()? { - $( - (Tag::$field, variant) => variant.unit_variant().map(|_| $name::$field), - )* - (Tag::Other(t), v) => v.unit_variant().map(|_| $name::Other(t)), - } - } - } - - const VARIANTS: &'static [&'static str] = &[ - $($field_name,)* - "" - ]; - deserializer.deserialize_enum(stringify!($name), VARIANTS, EnumVisitor) - } - } - }; - } - - /// Type that can be deserialized from ``, ``, or any other element - #[derive(Debug, PartialEq)] - enum Choice { - One, - Two, - /// Any other tag name except `One` or `Two`, name of tag stored inside variant - Other(String), - } - impl_deserialize_choice!(Choice: (One, "one"), (Two, "two")); - - /// Type that can be deserialized from ``, ``, or any other element - #[derive(Debug, PartialEq)] - enum Choice2 { - First, - Second, - /// Any other tag name except `First` or `Second`, name of tag stored inside variant - Other(String), - } - impl_deserialize_choice!(Choice2: (First, "first"), (Second, "second")); - - /// Type that can be deserialized from ``, ``, or any other element. - /// Used for `primitives` tests - #[derive(Debug, PartialEq, Deserialize)] - #[serde(rename_all = "kebab-case")] - enum Choice3 { - One(usize), - Two(String), - #[serde(other)] - Other, - } - - #[derive(Debug, PartialEq, Deserialize)] - #[serde(rename_all = "kebab-case")] - enum Choice4 { - One { - inner: [(); 1], - }, - Two { - inner: [(); 1], - }, - #[serde(other)] - Other, - } - - /// This module contains tests where size of the list have a compile-time size - mod fixed_size { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct List { - #[serde(rename = "$value")] - item: [Choice; 3], - } - - /// Simple case: count of elements matches expected size of sequence, - /// each element has the same name. Successful deserialization expected - #[test] - fn simple() { - let data: List = from_str( - r#" - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - } - ); - } - - /// Special case: empty sequence - #[test] - #[ignore = "it is impossible to distinguish between missed field and empty list: use `Option<>` or #[serde(default)]"] - fn empty() { - #[derive(Debug, PartialEq, Deserialize)] - struct List { - #[serde(rename = "$value")] - item: [Choice; 0], - } - - from_str::(r#""#).unwrap(); - from_str::(r#""#).unwrap(); - } - - /// Special case: one-element sequence - #[test] - fn one_element() { - #[derive(Debug, PartialEq, Deserialize)] - struct List { - #[serde(rename = "$value")] - item: [Choice; 1], - } - - let data: List = from_str( - r#" - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: [Choice::One], - } - ); - } - - /// Fever elements than expected size of sequence, each element has - /// the same name. Failure expected - #[test] - fn fever_elements() { - from_str::( - r#" - - - - - "#, - ) - .unwrap_err(); - } - - /// More elements than expected size of sequence, each element has - /// the same name. Failure expected. If you wish to ignore excess - /// elements, use the special type, that consume as much elements - /// as possible, but ignores excess elements - #[test] - fn excess_elements() { - from_str::( - r#" - - - - - - - "#, - ) - .unwrap_err(); - } - - #[test] - fn mixed_content() { - #[derive(Debug, PartialEq, Deserialize)] - struct List { - /// Text and CDATA represents a one logical text item - #[serde(rename = "$value")] - item: [(); 2], - } - - from_str::( - r#" - - - text - - - "#, - ) - .unwrap(); - } - - // There cannot be unknown items, because any tag name is accepted - - /// In those tests non-sequential field is defined in the struct - /// before sequential, so it will be deserialized before the list. - /// That struct should be deserialized from the XML where these - /// fields comes in an arbitrary order - mod field_before_list { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - node: (), - #[serde(rename = "$value")] - item: [Choice; 3], - } - - #[test] - fn before() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - node: (), - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - } - ); - } - - #[test] - fn after() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - node: (), - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - } - ); - } - - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - node: (), - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - node: (), - #[serde(rename = "$value")] - item: [Choice4; 3], - } - - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - node: (), - item: [ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - } - - /// In those tests non-sequential field is defined in the struct - /// after sequential, so it will be deserialized after the list. - /// That struct should be deserialized from the XML where these - /// fields comes in an arbitrary order - mod field_after_list { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - #[serde(rename = "$value")] - item: [Choice; 3], - node: (), - } - - #[test] - fn before() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - node: (), - } - ); - } - - #[test] - fn after() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - node: (), - } - ); - } - - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - node: (), - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - #[serde(rename = "$value")] - item: [Choice4; 3], - node: (), - } - - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - item: [ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - node: (), - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - } - - /// In those tests two lists are deserialized simultaneously. - /// Lists should be deserialized even when them overlaps - mod two_lists { - use super::*; - - /// A field with a variable-name items defined before a field with a fixed-name - /// items - mod choice_and_fixed { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Pair { - #[serde(rename = "$value")] - item: [Choice; 3], - element: [(); 2], - } - - /// A list with fixed-name elements located before a list with variable-name - /// elements in an XML - #[test] - fn fixed_before() { - let data: Pair = from_str( - r#" - - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Pair { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - element: [(), ()], - } - ); - } - - /// A list with fixed-name elements located after a list with variable-name - /// elements in an XML - #[test] - fn fixed_after() { - let data: Pair = from_str( - r#" - - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Pair { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - element: [(), ()], - } - ); - } - - mod overlapped { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - #[serde(rename = "$value")] - item: [Choice4; 3], - element: [(); 2], - } - - /// A list with fixed-name elements are mixed with a list with variable-name - /// elements in an XML, and the first element is a fixed-name one - #[test] - fn fixed_before() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - element: [(), ()], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 2") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, - e - ), - } - } - - /// A list with fixed-name elements are mixed with a list with variable-name - /// elements in an XML, and the first element is a variable-name one - #[test] - fn fixed_after() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - element: [(), ()], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn with_nested_list_fixed_before() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - item: [ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - element: [(); 2], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 2") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn with_nested_list_fixed_after() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - item: [ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - element: [(); 2], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - } - } - - /// A field with a variable-name items defined after a field with a fixed-name - /// items - mod fixed_and_choice { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Pair { - element: [(); 2], - #[serde(rename = "$value")] - item: [Choice; 3], - } - - /// A list with fixed-name elements located before a list with variable-name - /// elements in an XML - #[test] - fn fixed_before() { - let data: Pair = from_str( - r#" - - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Pair { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - element: [(), ()], - } - ); - } - - /// A list with fixed-name elements located after a list with variable-name - /// elements in an XML - #[test] - fn fixed_after() { - let data: Pair = from_str( - r#" - - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Pair { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - element: [(), ()], - } - ); - } - - mod overlapped { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - element: [(); 2], - #[serde(rename = "$value")] - item: [Choice4; 3], - } - - /// A list with fixed-name elements are mixed with a list with variable-name - /// elements in an XML, and the first element is a fixed-name one - #[test] - fn fixed_before() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - element: [(), ()], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 2") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, - e - ), - } - } - - /// A list with fixed-name elements are mixed with a list with variable-name - /// elements in an XML, and the first element is a variable-name one - #[test] - fn fixed_after() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - element: [(), ()], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn with_nested_list_fixed_before() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - element: [(); 2], - item: [ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 2") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn with_nested_list_fixed_after() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - element: [(); 2], - item: [ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - } - } - - /// Tests are ignored, but exists to show a problem. - /// May be it will be solved in the future - mod choice_and_choice { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Pair { - #[serde(rename = "$value")] - item: [Choice; 3], - // Actually, we cannot rename both fields to `$value`, which is now - // required to indicate, that field accepts elements with any name - #[serde(rename = "$value")] - element: [Choice2; 2], - } - - #[test] - #[ignore = "There is no way to associate XML elements with `item` or `element` without extra knowledge from type"] - fn splitted() { - let data: Pair = from_str( - r#" - - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Pair { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - element: [Choice2::First, Choice2::Second], - } - ); - } - - #[test] - #[ignore = "There is no way to associate XML elements with `item` or `element` without extra knowledge from type"] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - item: [Choice::One, Choice::Two, Choice::Other("three".into())], - element: [Choice2::First, Choice2::Second], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - } - } - - /// Deserialization of primitives slightly differs from deserialization - /// of complex types, so need to check this separately - #[test] - fn primitives() { - #[derive(Debug, PartialEq, Deserialize)] - struct List { - #[serde(rename = "$value")] - item: [Choice3; 3], - } - - let data: List = from_str( - r#" - - 41 - 42 - 43 - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: [ - Choice3::One(41), - Choice3::Two("42".to_string()), - Choice3::Other, - ], - } - ); - - from_str::( - r#" - - 41 - 42 - 43 - - "#, - ) - .unwrap_err(); - } - - /// Checks that sequences represented by elements can contain sequences, - /// represented by `xs:list`s - mod xs_list { - use super::*; - use pretty_assertions::assert_eq; - - /// Special case: zero elements - #[test] - fn zero() { - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// Outer list mapped to elements, inner -- to `xs:list`. - /// - /// `#[serde(default)]` is required to correctly deserialize - /// empty sequence, because without elements the field - /// also is missing and derived `Deserialize` implementation - /// would complain about that unless field is marked as - /// `default`. - #[serde(default)] - #[serde(rename = "$value")] - element: [Vec; 0], - } - - let data: List = from_str( - r#" - - - "#, - ) - .unwrap(); - - assert_eq!(data, List { element: [] }); - } - - /// Special case: one element - #[test] - fn one() { - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// Outer list mapped to elements, inner -- to `xs:list`. - /// - /// `#[serde(default)]` is not required, because correct - /// XML will always contains at least 1 element. - #[serde(rename = "$value")] - element: [Vec; 1], - } - - let data: List = from_str( - r#" - - first list - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - element: [vec!["first".to_string(), "list".to_string()]] - } - ); - } - - /// Special case: outer list is always mapped to an elements sequence, - /// not to an `xs:list` - #[test] - fn element() { - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// List mapped to elements, String -- to `xs:list`. - /// - /// `#[serde(default)]` is not required, because correct - /// XML will always contains at least 1 element. - #[serde(rename = "$value")] - element: [String; 1], - } - - let data: List = from_str( - r#" - - first item - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - element: ["first item".to_string()] - } - ); - } - - #[test] - fn many() { - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// Outer list mapped to elements, inner -- to `xs:list` - #[serde(rename = "$value")] - element: [Vec; 2], - } - - let data: List = from_str( - r#" - - first list - second list - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - element: [ - vec!["first".to_string(), "list".to_string()], - vec!["second".to_string(), "list".to_string()], - ] - } - ); - } - } - } - - /// This module contains tests where size of the list have an unspecified size - mod variable_size { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct List { - #[serde(rename = "$value")] - item: Vec, - } - - /// Simple case: count of elements matches expected size of sequence, - /// each element has the same name. Successful deserialization expected - #[test] - fn simple() { - let data: List = from_str( - r#" - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - } - ); - } - - /// Special case: empty sequence - #[test] - #[ignore = "it is impossible to distinguish between missed field and empty list: use `Option<>` or #[serde(default)]"] - fn empty() { - let data = from_str::(r#""#).unwrap(); - assert_eq!(data, List { item: vec![] }); - - let data = from_str::(r#""#).unwrap(); - assert_eq!(data, List { item: vec![] }); - } - - /// Special case: one-element sequence - #[test] - fn one_element() { - let data: List = from_str( - r#" - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: vec![Choice::One], - } - ); - } - - #[test] - fn mixed_content() { - #[derive(Debug, PartialEq, Deserialize)] - struct List { - #[serde(rename = "$value")] - item: Vec<()>, - } - - let data: List = from_str( - r#" - - - text - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - // Text and CDATA represents a one logical text item - item: vec![(), ()], - } - ); - } - - // There cannot be unknown items, because any tag name is accepted - - /// In those tests non-sequential field is defined in the struct - /// before sequential, so it will be deserialized before the list. - /// That struct should be deserialized from the XML where these - /// fields comes in an arbitrary order - mod field_before_list { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - node: (), - #[serde(rename = "$value")] - item: Vec, - } - - #[test] - fn before() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - node: (), - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - } - ); - } - - #[test] - fn after() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - node: (), - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - } - ); - } - - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - node: (), - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `$value`"), - e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - node: (), - #[serde(rename = "$value")] - item: Vec, - } - - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - node: (), - item: vec![ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `$value`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, - e - ), - } - } - } - - /// In those tests non-sequential field is defined in the struct - /// after sequential, so it will be deserialized after the list. - /// That struct should be deserialized from the XML where these - /// fields comes in an arbitrary order - mod field_after_list { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - #[serde(rename = "$value")] - item: Vec, - node: (), - } - - #[test] - fn before() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - node: (), - } - ); - } - - #[test] - fn after() { - let data: Root = from_str( - r#" - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Root { - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - node: (), - } - ); - } - - #[test] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - node: (), - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `$value`"), - e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn overlapped_with_nested_list() { - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - #[serde(rename = "$value")] - item: Vec, - node: (), - } - - let data = from_str::( - r#" - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - item: vec![ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - node: (), - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `$value`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, - e - ), - } - } - } - - /// In those tests two lists are deserialized simultaneously. - /// Lists should be deserialized even when them overlaps - mod two_lists { - use super::*; - - /// A field with a variable-name items defined before a field with a fixed-name - /// items - mod choice_and_fixed { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Pair { - #[serde(rename = "$value")] - item: Vec, - element: Vec<()>, - } - - /// A list with fixed-name elements located before a list with variable-name - /// elements in an XML - #[test] - fn fixed_before() { - let data: Pair = from_str( - r#" - - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Pair { - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - element: vec![(), ()], - } - ); - } - - /// A list with fixed-name elements located after a list with variable-name - /// elements in an XML - #[test] - fn fixed_after() { - let data: Pair = from_str( - r#" - - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Pair { - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - element: vec![(), ()], - } - ); - } - - mod overlapped { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - #[serde(rename = "$value")] - item: Vec, - element: Vec<()>, - } - - /// A list with fixed-name elements are mixed with a list with variable-name - /// elements in an XML, and the first element is a fixed-name one - #[test] - fn fixed_before() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - item: vec![ - Choice::One, - Choice::Two, - Choice::Other("three".into()) - ], - element: vec![(), ()], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `element`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, - e - ), - } - } - - /// A list with fixed-name elements are mixed with a list with variable-name - /// elements in an XML, and the first element is a variable-name one - #[test] - fn fixed_after() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - item: vec![ - Choice::One, - Choice::Two, - Choice::Other("three".into()) - ], - element: vec![(), ()], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `$value`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn with_nested_list_fixed_before() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - item: vec![ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - element: vec![(); 2], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `element`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn with_nested_list_fixed_after() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - item: vec![ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - element: vec![(); 2], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `$value`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, - e - ), - } - } - } - } - - /// A field with a variable-name items defined after a field with a fixed-name - /// items - mod fixed_and_choice { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Pair { - element: Vec<()>, - #[serde(rename = "$value")] - item: Vec, - } - - /// A list with fixed-name elements located before a list with variable-name - /// elements in an XML - #[test] - fn fixed_before() { - let data: Pair = from_str( - r#" - - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Pair { - element: vec![(), ()], - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - } - ); - } - - /// A list with fixed-name elements located after a list with variable-name - /// elements in an XML - #[test] - fn fixed_after() { - let data: Pair = from_str( - r#" - - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Pair { - element: vec![(), ()], - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - } - ); - } - - mod overlapped { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Root { - element: Vec<()>, - #[serde(rename = "$value")] - item: Vec, - } - - /// A list with fixed-name elements are mixed with a list with variable-name - /// elements in an XML, and the first element is a fixed-name one - #[test] - fn fixed_before() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - element: vec![(), ()], - item: vec![ - Choice::One, - Choice::Two, - Choice::Other("three".into()) - ], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `element`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, - e - ), - } - } - - /// A list with fixed-name elements are mixed with a list with variable-name - /// elements in an XML, and the first element is a variable-name one - #[test] - fn fixed_after() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - element: vec![(), ()], - item: vec![ - Choice::One, - Choice::Two, - Choice::Other("three".into()) - ], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `$value`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn with_nested_list_fixed_before() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - element: vec![(); 2], - item: vec![ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `element`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, - e - ), - } - } - - /// Test for https://github.com/tafia/quick-xml/issues/435 - #[test] - fn with_nested_list_fixed_after() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Root { - element: vec![(); 2], - item: vec![ - Choice4::One { inner: [()] }, - Choice4::Two { inner: [()] }, - Choice4::Other, - ], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "duplicate field `$value`") - } - e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, - e - ), - } - } - } - } - - /// Tests are ignored, but exists to show a problem. - /// May be it will be solved in the future - mod choice_and_choice { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, PartialEq, Deserialize)] - struct Pair { - #[serde(rename = "$value")] - item: Vec, - // Actually, we cannot rename both fields to `$value`, which is now - // required to indicate, that field accepts elements with any name - #[serde(rename = "$value")] - element: Vec, - } - - #[test] - #[ignore = "There is no way to associate XML elements with `item` or `element` without extra knowledge from type"] - fn splitted() { - let data: Pair = from_str( - r#" - - - - - - - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Pair { - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - element: vec![Choice2::First, Choice2::Second], - } - ); - } - - #[test] - #[ignore = "There is no way to associate XML elements with `item` or `element` without extra knowledge from type"] - fn overlapped() { - let data = from_str::( - r#" - - - - - - - - "#, - ); - - #[cfg(feature = "overlapped-lists")] - assert_eq!( - data.unwrap(), - Pair { - item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], - element: vec![Choice2::First, Choice2::Second], - } - ); - - #[cfg(not(feature = "overlapped-lists"))] - match data { - Err(DeError::Custom(e)) => { - assert_eq!(e, "invalid length 1, expected an array of length 3") - } - e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, - e - ), - } - } - } - } - - /// Deserialization of primitives slightly differs from deserialization - /// of complex types, so need to check this separately - #[test] - fn primitives() { - #[derive(Debug, PartialEq, Deserialize)] - struct List { - #[serde(rename = "$value")] - item: Vec, - } - - let data: List = from_str( - r#" - - 41 - 42 - 43 - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - item: vec![ - Choice3::One(41), - Choice3::Two("42".to_string()), - Choice3::Other, - ], - } - ); - - from_str::( - r#" - - 41 - 42 - 43 - - "#, - ) - .unwrap_err(); - } - - /// Checks that sequences represented by elements can contain sequences, - /// represented by `xs:list`s - mod xs_list { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// Outer list mapped to elements, inner -- to `xs:list`. - /// - /// `#[serde(default)]` is required to correctly deserialize - /// empty sequence, because without elements the field - /// also is missing and derived `Deserialize` implementation - /// would complain about that unless field is marked as - /// `default`. - #[serde(default)] - #[serde(rename = "$value")] - element: Vec>, - } - - /// Special case: zero elements - #[test] - fn zero() { - let data: List = from_str( - r#" - - - "#, - ) - .unwrap(); - - assert_eq!(data, List { element: vec![] }); - } - - /// Special case: one element - #[test] - fn one() { - let data: List = from_str( - r#" - - first list - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - element: vec![vec!["first".to_string(), "list".to_string()]] - } - ); - } - - /// Special case: outer list is always mapped to an elements sequence, - /// not to an `xs:list` - #[test] - fn element() { - #[derive(Debug, Deserialize, PartialEq)] - struct List { - /// Outer list mapped to elements. - #[serde(rename = "$value")] - element: Vec, - } - - let data: List = from_str( - r#" - - first item - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - element: vec!["first item".to_string()] - } - ); - } - - /// This tests demonstrates, that for `$value` field (`list`) actual - /// name of XML element (`item`) does not matter. That allows list - /// item to be an enum, where tag name determines enum variant - #[test] - fn many() { - let data: List = from_str( - r#" - - first list - second list - - "#, - ) - .unwrap(); - - assert_eq!( - data, - List { - element: vec![ - vec!["first".to_string(), "list".to_string()], - vec!["second".to_string(), "list".to_string()], - ] - } - ); - } - } - } - } -} - -macro_rules! maplike_errors { - ($type:ty) => { - maplike_errors!($type, $type); - }; - ( - $attributes:ty, - $mixed:ty - ) => { - mod non_closed { - use super::*; - - /// For struct we expect that error about not closed tag appears - /// earlier than error about missing fields - #[test] - fn missing_field() { - let data = from_str::<$mixed>(r#""#); - - match data { - Err(DeError::UnexpectedEof) => (), - _ => panic!("Expected `UnexpectedEof`, found {:?}", data), - } - } - - #[test] - fn attributes() { - let data = from_str::<$attributes>(r#""#); - - match data { - Err(DeError::UnexpectedEof) => (), - _ => panic!("Expected `UnexpectedEof`, found {:?}", data), - } - } - - #[test] - fn elements_root() { - let data = from_str::<$mixed>(r#"answer"#); - - match data { - Err(DeError::UnexpectedEof) => (), - _ => panic!("Expected `UnexpectedEof`, found {:?}", data), - } - } - - #[test] - fn elements_child() { - let data = from_str::<$mixed>(r#"answer"#); - - match data { - Err(DeError::UnexpectedEof) => (), - _ => panic!("Expected `UnexpectedEof`, found {:?}", data), - } - } - } - - mod mismatched_end { - use super::*; - use quick_xml::Error::EndEventMismatch; - - /// For struct we expect that error about mismatched tag appears - /// earlier than error about missing fields - #[test] - fn missing_field() { - let data = from_str::<$mixed>(r#""#); - - match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), - _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), - } - } - - #[test] - fn attributes() { - let data = from_str::<$attributes>( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ); - - match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), - _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), - } - } - - #[test] - fn elements_root() { - let data = from_str::<$mixed>( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"answer"#, - ); - - match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), - _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), - } - } - - #[test] - fn elements_child() { - let data = from_str::<$mixed>( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"answer"#, - ); - - match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), - _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), - } - } - } - }; -} - -mod map { - use super::*; - use pretty_assertions::assert_eq; - use std::collections::HashMap; - use std::iter::FromIterator; - - #[test] - fn elements() { - let data: HashMap<(), ()> = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"42answer"#, - ) - .unwrap(); - assert_eq!( - data, - HashMap::from_iter([((), ()), ((), ()),].iter().cloned()) - ); - } - - #[test] - fn attributes() { - let data: HashMap<(), ()> = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - HashMap::from_iter([((), ()), ((), ()),].iter().cloned()) - ); - } - - #[test] - fn attribute_and_element() { - let data: HashMap<(), ()> = from_str( - r#" - - answer - - "#, - ) - .unwrap(); - - assert_eq!( - data, - HashMap::from_iter([((), ()), ((), ()),].iter().cloned()) - ); - } - - maplike_errors!(HashMap<(), ()>); -} - -mod struct_ { - use super::*; - use pretty_assertions::assert_eq; - - /// Type where all struct fields represented by elements - #[derive(Debug, Deserialize, PartialEq)] - struct Elements { - float: f64, - string: String, - } - - /// Type where all struct fields represented by attributes - #[derive(Debug, Deserialize, PartialEq)] - struct Attributes { - #[serde(rename = "@float")] - float: f64, - #[serde(rename = "@string")] - string: String, - } - - /// Type where one field represented by an attribute and one by an element - #[derive(Debug, Deserialize, PartialEq)] - struct Mixed { - #[serde(rename = "@float")] - float: f64, - string: String, - } - - #[test] - fn elements() { - let data: Elements = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"42answer"#, - ) - .unwrap(); - assert_eq!( - data, - Elements { - float: 42.0, - string: "answer".into() - } - ); - } - - #[test] - fn excess_elements() { - let data: Elements = from_str( - r#" - - - 42 - - answer - - "#, - ) - .unwrap(); - assert_eq!( - data, - Elements { - float: 42.0, - string: "answer".into() - } - ); - } - - #[test] - fn attributes() { - let data: Attributes = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - Attributes { - float: 42.0, - string: "answer".into() - } - ); - } - - #[test] - fn excess_attributes() { - let data: Attributes = from_str( - r#""#, - ) - .unwrap(); - assert_eq!( - data, - Attributes { - float: 42.0, - string: "answer".into() - } - ); - } - - #[test] - fn attribute_and_element() { - let data: Mixed = from_str( - r#" - - answer - - "#, - ) - .unwrap(); - - assert_eq!( - data, - Mixed { - float: 42.0, - string: "answer".into() - } - ); - } - - #[test] - fn namespaces() { - let data: Elements = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"42answer"#, - ) - .unwrap(); - assert_eq!( - data, - Elements { - float: 42.0, - string: "answer".into() - } - ); - } - - /// Checks that excess data before the struct correctly handled. - /// Any data not allowed before the struct - mod excess_data_before { - use super::*; - use pretty_assertions::assert_eq; - - /// Space-only text events does not treated as data - #[test] - fn text_spaces_only() { - let data: Elements = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - " \t\n\r42answer", - ) - .unwrap(); - assert_eq!( - data, - Elements { - float: 42.0, - string: "answer".into() - } - ); - } - - /// Text events with non-space characters are not allowed - #[test] - fn text_non_spaces() { - match from_str::( - "\nexcess text\t42answer", - ) { - Err(DeError::ExpectedStart) => (), - x => panic!("Expected Err(ExpectedStart), but got {:?}", x), - }; - } - - /// CDATA events are not allowed - #[test] - fn cdata() { - match from_str::( - "42answer", - ) { - Err(DeError::ExpectedStart) => (), - x => panic!("Expected Err(ExpectedStart), but got {:?}", x), - }; - } - - /// Comments are ignored, so they are allowed - #[test] - fn comment() { - let data: Elements = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - "42answer", - ) - .unwrap(); - assert_eq!( - data, - Elements { - float: 42.0, - string: "answer".into() - } - ); - } - - /// Processing instructions are ignored, so they are allowed - #[test] - fn pi() { - let data: Elements = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - "42answer", - ) - .unwrap(); - assert_eq!( - data, - Elements { - float: 42.0, - string: "answer".into() - } - ); - } - } - - maplike_errors!(Attributes, Mixed); -} - -mod nested_struct { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn elements() { - #[derive(Debug, Deserialize, PartialEq)] - struct Struct { - nested: Nested, - string: String, - } - - #[derive(Debug, Deserialize, PartialEq)] - struct Nested { - float: f32, - } - - let data: Struct = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"answer42"#, - ) - .unwrap(); - assert_eq!( - data, - Struct { - nested: Nested { float: 42.0 }, - string: "answer".into() - } - ); - } - - #[test] - fn attributes() { - #[derive(Debug, Deserialize, PartialEq)] - struct Struct { - nested: Nested, - #[serde(rename = "@string")] - string: String, - } - - #[derive(Debug, Deserialize, PartialEq)] - struct Nested { - #[serde(rename = "@float")] - float: f32, - } - - let data: Struct = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - Struct { - nested: Nested { float: 42.0 }, - string: "answer".into() - } - ); - } -} - -mod flatten_struct { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn elements() { - #[derive(Debug, Deserialize, PartialEq)] - struct Struct { - #[serde(flatten)] - nested: Nested, - string: String, - } - - #[derive(Debug, Deserialize, PartialEq)] - struct Nested { - //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 - float: String, - } - - let data: Struct = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"42answer"#, - ) - .unwrap(); - assert_eq!( - data, - Struct { - nested: Nested { float: "42".into() }, - string: "answer".into() - } - ); - } - - #[test] - fn attributes() { - #[derive(Debug, Deserialize, PartialEq)] - struct Struct { - #[serde(flatten)] - nested: Nested, - #[serde(rename = "@string")] - string: String, - } - - #[derive(Debug, Deserialize, PartialEq)] - struct Nested { - //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 - #[serde(rename = "@float")] - float: String, - } - - let data: Struct = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - Struct { - nested: Nested { float: "42".into() }, - string: "answer".into() - } - ); - } -} - -mod enum_ { - use super::*; - - #[derive(Debug, Deserialize, PartialEq)] - struct Nested { - //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 - float: String, - } - - /// Type where all struct fields represented by attributes - #[derive(Debug, Deserialize, PartialEq)] - struct NestedAttr { - //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 - #[serde(rename = "@float")] - float: String, - } - - /// Enum tag selector is a name of the `` or text / CDATA content - /// for a `$text` variant - mod externally_tagged { - use super::*; - use pretty_assertions::assert_eq; - - /// Type where all fields of struct variants represented by elements - #[derive(Debug, Deserialize, PartialEq)] - enum Node { - Unit, - Newtype(bool), - //TODO: serde bug https://github.com/serde-rs/serde/issues/1904 - // Tuple(f64, String), - Struct { - float: f64, - string: String, - }, - Holder { - nested: Nested, - string: String, - }, - Flatten { - #[serde(flatten)] - nested: Nested, - string: String, - }, - } - - /// Type where all fields of struct variants represented by attributes - #[derive(Debug, Deserialize, PartialEq)] - enum NodeAttr { - Struct { - #[serde(rename = "@float")] - float: f64, - #[serde(rename = "@string")] - string: String, - }, - Holder { - nested: NestedAttr, - #[serde(rename = "@string")] - string: String, - }, - Flatten { - #[serde(flatten)] - nested: NestedAttr, - #[serde(rename = "@string")] - string: String, - }, - } - - /// Workaround for serde bug https://github.com/serde-rs/serde/issues/1904 - #[derive(Debug, Deserialize, PartialEq)] - enum Workaround { - Tuple(f64, String), - } - - #[test] - fn unit() { - let data: Node = from_str("").unwrap(); - assert_eq!(data, Node::Unit); - } - - #[test] - fn newtype() { - let data: Node = from_str("true").unwrap(); - assert_eq!(data, Node::Newtype(true)); - } - - #[test] - fn tuple_struct() { - let data: Workaround = from_str("42answer").unwrap(); - assert_eq!(data, Workaround::Tuple(42.0, "answer".into())); - } - - mod struct_ { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn elements() { - let data: Node = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"42answer"#, - ) - .unwrap(); - assert_eq!( - data, - Node::Struct { - float: 42.0, - string: "answer".into() - } - ); - } - - #[test] - fn attributes() { - let data: NodeAttr = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - NodeAttr::Struct { - float: 42.0, - string: "answer".into() - } - ); - } - - #[test] - fn namespaces() { - let data: Node = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"42answer"#, - ) - .unwrap(); - assert_eq!( - data, - Node::Struct { - float: 42.0, - string: "answer".into() - } - ); - } - } - - mod nested_struct { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn elements() { - let data: Node = from_str( - r#"answer42"#, - ) - .unwrap(); - assert_eq!( - data, - Node::Holder { - nested: Nested { float: "42".into() }, - string: "answer".into() - } - ); - } - - #[test] - fn attributes() { - let data: NodeAttr = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - NodeAttr::Holder { - nested: NestedAttr { float: "42".into() }, - string: "answer".into() - } - ); - } - } - - mod flatten_struct { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn elements() { - let data: Node = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"42answer"#, - ) - .unwrap(); - assert_eq!( - data, - Node::Flatten { - nested: Nested { float: "42".into() }, - string: "answer".into() - } - ); - } - - #[test] - fn attributes() { - let data: NodeAttr = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - NodeAttr::Flatten { - nested: NestedAttr { float: "42".into() }, - string: "answer".into() - } - ); - } - } - - /// Test deserialization of the specially named variant `$text` - mod text { - use super::*; - - mod unit { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, Deserialize, PartialEq)] - enum Text { - #[serde(rename = "$text")] - Unit, - } - - #[test] - fn text() { - let data: Text = from_str(" text ").unwrap(); - assert_eq!(data, Text::Unit); - } - - #[test] - fn cdata() { - let data: Text = from_str("").unwrap(); - assert_eq!(data, Text::Unit); - } - - #[test] - #[ignore = "awaiting fix of https://github.com/tafia/quick-xml/issues/474"] - fn mixed() { - let data: Text = from_str(" te xt ").unwrap(); - assert_eq!(data, Text::Unit); - } - } - - mod newtype { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, Deserialize, PartialEq)] - enum Text { - #[serde(rename = "$text")] - Newtype(String), - } - - #[test] - fn text() { - let data: Text = from_str(" text ").unwrap(); - assert_eq!(data, Text::Newtype("text".into())); - } - - #[test] - fn cdata() { - let data: Text = from_str("").unwrap(); - assert_eq!(data, Text::Newtype(" cdata ".into())); - } - - #[test] - #[ignore = "awaiting fix of https://github.com/tafia/quick-xml/issues/474"] - fn mixed() { - let data: Text = from_str(" te xt ").unwrap(); - assert_eq!(data, Text::Newtype("te cdata xt".into())); - } - } - - /// Tuple variant deserialized as an `xs:list`, that is why spaces - /// are trimmed even in CDATA sections - mod tuple { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Debug, Deserialize, PartialEq)] - enum Text { - #[serde(rename = "$text")] - Tuple(f64, String), - } - - #[test] - fn text() { - let data: Text = from_str(" 4.2 text ").unwrap(); - assert_eq!(data, Text::Tuple(4.2, "text".into())); - } - - #[test] - fn cdata() { - let data: Text = from_str("").unwrap(); - assert_eq!(data, Text::Tuple(4.2, "cdata".into())); - } - - #[test] - #[ignore = "awaiting fix of https://github.com/tafia/quick-xml/issues/474"] - fn mixed() { - let data: Text = from_str(" 4.2 ").unwrap(); - assert_eq!(data, Text::Tuple(4.2, "cdata".into())); - } - } - - /// Struct variant cannot be directly deserialized from `Text` / `CData` events - mod struct_ { - use super::*; - - #[derive(Debug, Deserialize, PartialEq)] - enum Text { - #[serde(rename = "$text")] - Struct { float: f64, string: String }, - } - - #[test] - fn text() { - match from_str::(" text ") { - Err(DeError::Unsupported(_)) => {} - x => panic!("Expected `Err(Unsupported(_))`, but found {:?}", x), - } - } - - #[test] - fn cdata() { - match from_str::("") { - Err(DeError::Unsupported(_)) => {} - x => panic!("Expected `Err(Unsupported(_))`, but found {:?}", x), - } - } - - #[test] - fn mixed() { - match from_str::(" te xt ") { - Err(DeError::Unsupported(_)) => {} - x => panic!("Expected `Err(Unsupported(_))`, but found {:?}", x), - } - } + #[test] + fn excess_elements() { + let data: Elements = from_str( + r#" + + + 42 + + answer + + "#, + ) + .unwrap(); + assert_eq!( + data, + Elements { + float: 42.0, + string: "answer".into() } - } + ); } - /// Enum tag selector either an attribute "tag", or a tag "tag". - /// `$text` variant could be defined, but that name has no special meaning - mod internally_tagged { - use super::*; - - /// Type where all fields of struct variants and a tag represented by elements - #[derive(Debug, Deserialize, PartialEq)] - #[serde(tag = "tag")] - enum Node { - Unit, - /// Primitives (such as `bool`) are not supported by serde in the internally tagged mode - Newtype(NewtypeContent), - // Tuple(f64, String),// Tuples are not supported in the internally tagged mode - Struct { - //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 - float: String, - string: String, - }, - Holder { - nested: Nested, - string: String, - }, - Flatten { - #[serde(flatten)] - nested: Nested, - string: String, - }, - } - - /// Type where all fields of struct variants and a tag represented by attributes - #[derive(Debug, Deserialize, PartialEq)] - #[serde(tag = "@tag")] - enum NodeAttr { - Unit, - /// Primitives (such as `bool`) are not supported by serde in the internally tagged mode - Newtype(NewtypeContent), - // Tuple(f64, String),// Tuples are not supported in the internally tagged mode - Struct { - //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 - #[serde(rename = "@float")] - float: String, - #[serde(rename = "@string")] - string: String, - }, - Holder { - nested: NestedAttr, - #[serde(rename = "@string")] - string: String, - }, - Flatten { - #[serde(flatten)] - nested: NestedAttr, - #[serde(rename = "@string")] - string: String, - }, - } - - #[derive(Debug, Deserialize, PartialEq)] - struct NewtypeContent { - value: bool, - } - - mod unit { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn elements() { - let data: Node = from_str(r#"Unit"#).unwrap(); - assert_eq!(data, Node::Unit); - } - - #[test] - fn attributes() { - let data: NodeAttr = from_str(r#""#).unwrap(); - assert_eq!(data, NodeAttr::Unit); - } - } - - mod newtype { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn elements() { - let data: Node = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"Newtypetrue"#, - ) - .unwrap(); - assert_eq!(data, Node::Newtype(NewtypeContent { value: true })); - } - - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn attributes() { - let data: NodeAttr = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"true"#, - ) - .unwrap(); - assert_eq!(data, NodeAttr::Newtype(NewtypeContent { value: true })); - } - } - - mod struct_ { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn elements() { - let data: Node = from_str( - r#"Struct42answer"#, - ) - .unwrap(); - assert_eq!( - data, - Node::Struct { - float: "42".into(), - string: "answer".into() - } - ); - } - - #[test] - fn attributes() { - let data: NodeAttr = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - NodeAttr::Struct { - float: "42".into(), - string: "answer".into() - } - ); - } - } - - mod nested_struct { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn elements() { - let data: Node = from_str( - r#"Holderanswer42"#, - ).unwrap(); - assert_eq!( - data, - Node::Holder { - nested: Nested { float: "42".into() }, - string: "answer".into() - } - ); - } - - #[test] - fn attributes() { - let data: NodeAttr = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - NodeAttr::Holder { - nested: NestedAttr { float: "42".into() }, - string: "answer".into() - } - ); - } - } - - mod flatten_struct { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn elements() { - let data: Node = from_str( - r#"Flatten42answer"#, - ) - .unwrap(); - assert_eq!( - data, - Node::Flatten { - nested: Nested { float: "42".into() }, - string: "answer".into() - } - ); - } - - #[test] - fn attributes() { - let data: NodeAttr = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - NodeAttr::Flatten { - nested: NestedAttr { float: "42".into() }, - string: "answer".into() - } - ); + #[test] + fn attributes() { + let data: Attributes = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + Attributes { + float: 42.0, + string: "answer".into() } - } + ); } - /// Enum tag selector either an attribute "tag", or a tag "tag". - /// `$text` variant could be defined, but that name has no special meaning - mod adjacently_tagged { - use super::*; - - /// Type where all fields of struct variants, tag and content fields - /// represented by elements - #[derive(Debug, Deserialize, PartialEq)] - #[serde(tag = "tag", content = "content")] - enum Node { - Unit, - Newtype(bool), - //TODO: serde bug https://github.com/serde-rs/serde/issues/1904 - // Tuple(f64, String), - Struct { - float: f64, - string: String, - }, - Holder { - nested: Nested, - string: String, - }, - Flatten { - #[serde(flatten)] - nested: Nested, - string: String, - }, - } - - /// Type where all fields of struct variants, tag and content fields - /// represented by attributes - #[derive(Debug, Deserialize, PartialEq)] - #[serde(tag = "@tag", content = "@content")] - enum NodeAttrSimple { - Unit, - Newtype(bool), - } - - /// Type where all fields of struct variants and a tag represented by attributes - /// content cannot be represented by attribute because this is a complex struct - #[derive(Debug, Deserialize, PartialEq)] - #[serde(tag = "@tag", content = "content")] - enum NodeAttrComplex { - //TODO: serde bug https://github.com/serde-rs/serde/issues/1904 - // Tuple(f64, String), - Struct { - #[serde(rename = "@float")] - float: f64, - #[serde(rename = "@string")] - string: String, - }, - Holder { - nested: NestedAttr, - #[serde(rename = "@string")] - string: String, - }, - Flatten { - #[serde(flatten)] - nested: NestedAttr, - #[serde(rename = "@string")] - string: String, - }, - } - - /// Workaround for serde bug https://github.com/serde-rs/serde/issues/1904 - #[derive(Debug, Deserialize, PartialEq)] - #[serde(tag = "tag", content = "content")] - enum Workaround { - Tuple(f64, String), - } - - /// Workaround for serde bug https://github.com/serde-rs/serde/issues/1904 - #[derive(Debug, Deserialize, PartialEq)] - #[serde(tag = "@tag", content = "@content")] - enum WorkaroundAttr { - Tuple(f64, String), - } - - mod unit { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn elements() { - let data: Node = from_str(r#"Unit"#).unwrap(); - assert_eq!(data, Node::Unit); - } - - #[test] - fn attributes() { - let data: NodeAttrSimple = from_str(r#""#).unwrap(); - assert_eq!(data, NodeAttrSimple::Unit); - } - } - - mod newtype { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn elements() { - let data: Node = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"Newtypetrue"#, - ) - .unwrap(); - assert_eq!(data, Node::Newtype(true)); - } - - #[test] - fn attributes() { - let data: NodeAttrSimple = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!(data, NodeAttrSimple::Newtype(true)); - } - } - - mod tuple_struct { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn elements() { - let data: Workaround = from_str( - r#"Tuple42answer"#, - ).unwrap(); - assert_eq!(data, Workaround::Tuple(42.0, "answer".into())); - } - - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn attributes() { - let data: WorkaroundAttr = from_str( - // We cannot have two attributes with the same name, so both values stored in one attribute - r#""#, - ) - .unwrap(); - assert_eq!(data, WorkaroundAttr::Tuple(42.0, "answer".into())); - } - } - - mod struct_ { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn elements() { - let data: Node = from_str( - r#"Struct42answer"#, - ) - .unwrap(); - assert_eq!( - data, - Node::Struct { - float: 42.0, - string: "answer".into() - } - ); - } - - #[test] - fn attributes() { - let data: NodeAttrComplex = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - NodeAttrComplex::Struct { - float: 42.0, - string: "answer".into() - } - ); - } - } - - mod nested_struct { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn elements() { - let data: Node = from_str( - r#" - Holder - - answer - - 42 - - - "#, - ) - .unwrap(); - assert_eq!( - data, - Node::Holder { - nested: Nested { float: "42".into() }, - string: "answer".into() - } - ); - } - - #[test] - fn attributes() { - let data: NodeAttrComplex = from_str( - r#""#, - ).unwrap(); - assert_eq!( - data, - NodeAttrComplex::Holder { - nested: NestedAttr { float: "42".into() }, - string: "answer".into() - } - ); + #[test] + fn excess_attributes() { + let data: Attributes = from_str( + r#""#, + ) + .unwrap(); + assert_eq!( + data, + Attributes { + float: 42.0, + string: "answer".into() } - } + ); + } - mod flatten_struct { - use super::*; - use pretty_assertions::assert_eq; + #[test] + fn attribute_and_element() { + let data: Mixed = from_str( + r#" + + answer + + "#, + ) + .unwrap(); - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn elements() { - let data: Node = from_str( - r#"Flatten42answer"#, - ).unwrap(); - assert_eq!( - data, - Node::Flatten { - nested: Nested { float: "42".into() }, - string: "answer".into() - } - ); + assert_eq!( + data, + Mixed { + float: 42.0, + string: "answer".into() } + ); + } - #[test] - fn attributes() { - let data: NodeAttrComplex = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - NodeAttrComplex::Flatten { - nested: NestedAttr { float: "42".into() }, - string: "answer".into() - } - ); + #[test] + fn namespaces() { + let data: Elements = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"42answer"#, + ) + .unwrap(); + assert_eq!( + data, + Elements { + float: 42.0, + string: "answer".into() } - } + ); } - /// Enum tags does not exist. - /// `$text` variant could be defined, but that name has no special meaning - mod untagged { + /// Checks that excess data before the struct correctly handled. + /// Any data not allowed before the struct + mod excess_data_before { use super::*; use pretty_assertions::assert_eq; - /// Type where all fields of struct variants represented by elements - #[derive(Debug, Deserialize, PartialEq)] - #[serde(untagged)] - enum Node { - Unit, - Newtype(bool), - // serde bug https://github.com/serde-rs/serde/issues/1904 - // Tuple(f64, String), - Struct { - float: f64, - string: String, - }, - Holder { - nested: Nested, - string: String, - }, - Flatten { - #[serde(flatten)] - nested: Nested, - // Can't use "string" as name because in that case this variant - // will have no difference from `Struct` variant - string2: String, - }, + /// Space-only text events does not treated as data + #[test] + fn text_spaces_only() { + let data: Elements = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + " \t\n\r42answer", + ) + .unwrap(); + assert_eq!( + data, + Elements { + float: 42.0, + string: "answer".into() + } + ); } - /// Type where all fields of struct variants represented by attributes - #[derive(Debug, Deserialize, PartialEq)] - #[serde(untagged)] - enum NodeAttr { - // serde bug https://github.com/serde-rs/serde/issues/1904 - // Tuple(f64, String), - Struct { - #[serde(rename = "@float")] - float: f64, - #[serde(rename = "@string")] - string: String, - }, - Holder { - nested: NestedAttr, - #[serde(rename = "@string")] - string: String, - }, - Flatten { - #[serde(flatten)] - nested: NestedAttr, - // Can't use "string" as name because in that case this variant - // will have no difference from `Struct` variant - #[serde(rename = "@string2")] - string2: String, - }, + /// Text events with non-space characters are not allowed + #[test] + fn text_non_spaces() { + match from_str::( + "\nexcess text\t42answer", + ) { + Err(DeError::ExpectedStart) => (), + x => panic!("Expected Err(ExpectedStart), but got {:?}", x), + }; } - /// Workaround for serde bug https://github.com/serde-rs/serde/issues/1904 - #[derive(Debug, Deserialize, PartialEq)] - #[serde(untagged)] - enum Workaround { - Tuple(f64, String), + /// CDATA events are not allowed + #[test] + fn cdata() { + match from_str::( + "42answer", + ) { + Err(DeError::ExpectedStart) => (), + x => panic!("Expected Err(ExpectedStart), but got {:?}", x), + }; } + /// Comments are ignored, so they are allowed #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn unit() { - // Unit variant consists just from the tag, and because tags - // are not written, nothing is written - let data: Node = from_str("").unwrap(); - assert_eq!(data, Node::Unit); + fn comment() { + let data: Elements = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + "42answer", + ) + .unwrap(); + assert_eq!( + data, + Elements { + float: 42.0, + string: "answer".into() + } + ); } + /// Processing instructions are ignored, so they are allowed #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn newtype() { - let data: Node = from_str("true").unwrap(); - assert_eq!(data, Node::Newtype(true)); + fn pi() { + let data: Elements = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + "42answer", + ) + .unwrap(); + assert_eq!( + data, + Elements { + float: 42.0, + string: "answer".into() + } + ); } + } - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn tuple_struct() { - let data: Workaround = from_str("42answer").unwrap(); - assert_eq!(data, Workaround::Tuple(42.0, "answer".into())); + maplike_errors!(Attributes, Mixed); +} + +mod nested_struct { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn elements() { + #[derive(Debug, Deserialize, PartialEq)] + struct Struct { + nested: Nested, + string: String, } - mod struct_ { - use super::*; - use pretty_assertions::assert_eq; + #[derive(Debug, Deserialize, PartialEq)] + struct Nested { + float: f32, + } - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn elements() { - let data: Node = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"42answer"#, - ) - .unwrap(); - assert_eq!( - data, - Node::Struct { - float: 42.0, - string: "answer".into() - } - ); + let data: Struct = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"answer42"#, + ) + .unwrap(); + assert_eq!( + data, + Struct { + nested: Nested { float: 42.0 }, + string: "answer".into() } + ); + } - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn attributes() { - let data: NodeAttr = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - NodeAttr::Struct { - float: 42.0, - string: "answer".into() - } - ); - } + #[test] + fn attributes() { + #[derive(Debug, Deserialize, PartialEq)] + struct Struct { + nested: Nested, + #[serde(rename = "@string")] + string: String, } - mod nested_struct { - use super::*; - use pretty_assertions::assert_eq; + #[derive(Debug, Deserialize, PartialEq)] + struct Nested { + #[serde(rename = "@float")] + float: f32, + } - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn elements() { - let data: Node = from_str( - r#"answer42"#, - ) - .unwrap(); - assert_eq!( - data, - Node::Holder { - nested: Nested { float: "42".into() }, - string: "answer".into() - } - ); + let data: Struct = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + Struct { + nested: Nested { float: 42.0 }, + string: "answer".into() } + ); + } +} - #[test] - fn attributes() { - let data: NodeAttr = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - NodeAttr::Holder { - nested: NestedAttr { float: "42".into() }, - string: "answer".into() - } - ); - } +mod flatten_struct { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] + fn elements() { + #[derive(Debug, Deserialize, PartialEq)] + struct Struct { + #[serde(flatten)] + nested: Nested, + string: String, } - mod flatten_struct { - use super::*; - use pretty_assertions::assert_eq; + #[derive(Debug, Deserialize, PartialEq)] + struct Nested { + //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 + float: String, + } - #[test] - #[ignore = "Prime cause: deserialize_any under the hood + https://github.com/serde-rs/serde/issues/1183"] - fn elements() { - let data: Node = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#"42answer"#, - ) - .unwrap(); - assert_eq!( - data, - Node::Flatten { - nested: Nested { float: "42".into() }, - string2: "answer".into() - } - ); + let data: Struct = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#"42answer"#, + ) + .unwrap(); + assert_eq!( + data, + Struct { + nested: Nested { float: "42".into() }, + string: "answer".into() } + ); + } - #[test] - fn attributes() { - let data: NodeAttr = from_str( - // Comment for prevent unnecessary formatting - we use the same style in all tests - r#""#, - ) - .unwrap(); - assert_eq!( - data, - NodeAttr::Flatten { - nested: NestedAttr { float: "42".into() }, - string2: "answer".into() - } - ); - } + #[test] + fn attributes() { + #[derive(Debug, Deserialize, PartialEq)] + struct Struct { + #[serde(flatten)] + nested: Nested, + #[serde(rename = "@string")] + string: String, + } + + #[derive(Debug, Deserialize, PartialEq)] + struct Nested { + //TODO: change to f64 after fixing https://github.com/serde-rs/serde/issues/1183 + #[serde(rename = "@float")] + float: String, } + + let data: Struct = from_str( + // Comment for prevent unnecessary formatting - we use the same style in all tests + r#""#, + ) + .unwrap(); + assert_eq!( + data, + Struct { + nested: Nested { float: "42".into() }, + string: "answer".into() + } + ); } } +// enum tests are so big, so it in the separate file serde-de-seq.rs to speed-up compilation + /// https://www.w3schools.com/xml/el_list.asp mod xml_schema_lists { use super::*; From c5f7ec5e3ac7ba43528acf73d8a04fda219d9341 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 17 Sep 2023 18:13:18 +0500 Subject: [PATCH 2/5] Run `cargo fmt` After moving tests to separate files indentation level was lowered, so some reformatting is needed --- tests/serde-de-enum.rs | 3 ++- tests/serde-de-seq.rs | 27 +++++---------------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/tests/serde-de-enum.rs b/tests/serde-de-enum.rs index cc721e5a..351f7cd3 100644 --- a/tests/serde-de-enum.rs +++ b/tests/serde-de-enum.rs @@ -699,7 +699,8 @@ mod adjacently_tagged { fn elements() { let data: Workaround = from_str( r#"Tuple42answer"#, - ).unwrap(); + ) + .unwrap(); assert_eq!(data, Workaround::Tuple(42.0, "answer".into())); } diff --git a/tests/serde-de-seq.rs b/tests/serde-de-seq.rs index 1a3ffaf3..bddb8069 100644 --- a/tests/serde-de-seq.rs +++ b/tests/serde-de-seq.rs @@ -55,8 +55,7 @@ mod top_level { .unwrap(); let data: Vec<()> = - from_str(r#"42answer"#) - .unwrap(); + from_str(r#"42answer"#).unwrap(); assert_eq!(data, vec![(), (), ()]); } @@ -3464,11 +3463,7 @@ mod variable_name { assert_eq!( data.unwrap(), Pair { - item: vec![ - Choice::One, - Choice::Two, - Choice::Other("three".into()) - ], + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], element: vec![(), ()], } ); @@ -3505,11 +3500,7 @@ mod variable_name { assert_eq!( data.unwrap(), Pair { - item: vec![ - Choice::One, - Choice::Two, - Choice::Other("three".into()) - ], + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], element: vec![(), ()], } ); @@ -3705,11 +3696,7 @@ mod variable_name { data.unwrap(), Pair { element: vec![(), ()], - item: vec![ - Choice::One, - Choice::Two, - Choice::Other("three".into()) - ], + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], } ); @@ -3746,11 +3733,7 @@ mod variable_name { data.unwrap(), Pair { element: vec![(), ()], - item: vec![ - Choice::One, - Choice::Two, - Choice::Other("three".into()) - ], + item: vec![Choice::One, Choice::Two, Choice::Other("three".into())], } ); From a40cfb84285799c1b049ad56af39a87330114939 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 17 Sep 2023 20:21:11 +0500 Subject: [PATCH 3/5] Unify all "cannot serialize" errors' texts --- src/se/element.rs | 64 +++++++++++++++++++++---------------------- src/se/key.rs | 40 +++++++++++++-------------- src/se/simple_type.rs | 60 ++++++++++++++++++++-------------------- tests/serde-se.rs | 12 ++++---- 4 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/se/element.rs b/src/se/element.rs index 73ce416f..a4e3eb4d 100644 --- a/src/se/element.rs +++ b/src/se/element.rs @@ -112,7 +112,7 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> { if variant == TEXT_KEY { // We should write some text but we don't known what text to write Err(DeError::Unsupported( - format!("`{}::$text` unit variant cannot be serialized", name).into(), + format!("cannot serialize enum unit variant `{}::$text` as text content value", name).into(), )) } else { let name = XmlName::try_from(variant)?; @@ -216,7 +216,7 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> { ) -> Result { if variant == TEXT_KEY { Err(DeError::Unsupported( - format!("`{}::$text` struct variant cannot be serialized", name).into(), + format!("cannot serialize enum struct variant `{}::$text` as text content value", name).into(), )) } else { self.key = XmlName::try_from(variant)?; @@ -789,7 +789,7 @@ mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); // Sequences are serialized separated by spaces, all spaces inside are escaped text!(seq: vec![1, 2, 3] => "1 2 3"); @@ -806,7 +806,7 @@ mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -815,21 +815,21 @@ mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("map cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize map as an attribute or text content value")); err!(struct_: Text { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize struct `Struct` as an attribute or text content value")); err!(enum_struct: Text { before: "answer", content: Enum::Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value")); } /// `$text` field inside a struct @@ -916,7 +916,7 @@ mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); // Sequences are serialized separated by spaces, all spaces inside are escaped text!(seq: vec![1, 2, 3] => "1 2 3"); @@ -933,7 +933,7 @@ mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -942,21 +942,21 @@ mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("map cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize map as an attribute or text content value")); err!(struct_: Text { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize struct `Struct` as an attribute or text content value")); err!(enum_struct: Text { before: "answer", content: Enum::Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value")); } /// `$text` field inside a struct variant of an enum @@ -1043,7 +1043,7 @@ mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); // Sequences are serialized separated by spaces, all spaces inside are escaped text!(seq: vec![1, 2, 3] => "1 2 3"); @@ -1060,7 +1060,7 @@ mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -1069,21 +1069,21 @@ mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("map cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize map as an attribute or text content value")); err!(struct_: SpecialEnum::Text { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize struct `Struct` as an attribute or text content value")); err!(enum_struct: SpecialEnum::Text { before: "answer", content: Enum::Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value")); } } @@ -1753,7 +1753,7 @@ mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); // Sequences are serialized separated by spaces, all spaces inside are escaped text!(seq: vec![1, 2, 3] => "1 2 3"); @@ -1770,7 +1770,7 @@ mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -1779,21 +1779,21 @@ mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("map cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize map as an attribute or text content value")); err!(struct_: Text { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize struct `Struct` as an attribute or text content value")); err!(enum_struct: Text { before: "answer", content: Enum::Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value")); } /// `$text` field inside a struct @@ -1892,7 +1892,7 @@ mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); // Sequences are serialized separated by spaces, all spaces inside are escaped text!(seq: vec![1, 2, 3] => "1 2 3"); @@ -1909,7 +1909,7 @@ mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -1918,21 +1918,21 @@ mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("map cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize map as an attribute or text content value")); err!(struct_: Text { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize struct `Struct` as an attribute or text content value")); err!(enum_struct: Text { before: "answer", content: Enum::Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value")); } /// `$text` field inside a struct variant of an enum @@ -2031,7 +2031,7 @@ mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); // Sequences are serialized separated by spaces, all spaces inside are escaped text!(seq: vec![1, 2, 3] => "1 2 3"); @@ -2048,7 +2048,7 @@ mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -2057,21 +2057,21 @@ mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("map cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize map as an attribute or text content value")); err!(struct_: SpecialEnum::Text { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize struct `Struct` as an attribute or text content value")); err!(enum_struct: SpecialEnum::Text { before: "answer", content: Enum::Struct { key: "answer", val: (42, 42) }, after: "answer", } - => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value")); } } diff --git a/src/se/key.rs b/src/se/key.rs index 0f59ba20..bd8ebc25 100644 --- a/src/se/key.rs +++ b/src/se/key.rs @@ -46,7 +46,7 @@ impl Serializer for QNameSerializer { /// a valid XML name, serialization of unit returns `Err(Unsupported)` fn serialize_unit(self) -> Result { Err(DeError::Unsupported( - "unit type `()` cannot be serialized as an XML tag name".into(), + "cannot serialize unit type `()` as an XML tag name".into(), )) } @@ -55,7 +55,7 @@ impl Serializer for QNameSerializer { fn serialize_unit_struct(self, name: &'static str) -> Result { Err(DeError::Unsupported( format!( - "unit struct `{}` cannot be serialized as an XML tag name", + "cannot serialize unit struct `{}` as an XML tag name", name ) .into(), @@ -73,7 +73,7 @@ impl Serializer for QNameSerializer { ) -> Result { Err(DeError::Unsupported( format!( - "enum newtype variant `{}::{}` cannot be serialized as an XML tag name", + "cannot serialize enum newtype variant `{}::{}` as an XML tag name", name, variant ) .into(), @@ -82,13 +82,13 @@ impl Serializer for QNameSerializer { fn serialize_seq(self, _len: Option) -> Result { Err(DeError::Unsupported( - "sequence cannot be serialized as an XML tag name".into(), + "cannot serialize sequence as an XML tag name".into(), )) } fn serialize_tuple(self, _len: usize) -> Result { Err(DeError::Unsupported( - "tuple cannot be serialized as an XML tag name".into(), + "cannot serialize tuple as an XML tag name".into(), )) } @@ -99,7 +99,7 @@ impl Serializer for QNameSerializer { ) -> Result { Err(DeError::Unsupported( format!( - "tuple struct `{}` cannot be serialized as an XML tag name", + "cannot serialize tuple struct `{}` as an XML tag name", name ) .into(), @@ -115,7 +115,7 @@ impl Serializer for QNameSerializer { ) -> Result { Err(DeError::Unsupported( format!( - "enum tuple variant `{}::{}` cannot be serialized as an XML tag name", + "cannot serialize enum tuple variant `{}::{}` as an XML tag name", name, variant ) .into(), @@ -124,7 +124,7 @@ impl Serializer for QNameSerializer { fn serialize_map(self, _len: Option) -> Result { Err(DeError::Unsupported( - "map cannot be serialized as an XML tag name".into(), + "cannot serialize map as an XML tag name".into(), )) } @@ -134,7 +134,7 @@ impl Serializer for QNameSerializer { _len: usize, ) -> Result { Err(DeError::Unsupported( - format!("struct `{}` cannot be serialized as an XML tag name", name).into(), + format!("cannot serialize struct `{}` as an XML tag name", name).into(), )) } @@ -147,7 +147,7 @@ impl Serializer for QNameSerializer { ) -> Result { Err(DeError::Unsupported( format!( - "enum struct variant `{}::{}` cannot be serialized as an XML tag name", + "cannot serialize enum struct variant `{}::{}` as an XML tag name", name, variant ) .into(), @@ -276,30 +276,30 @@ mod tests { serialize_as!(option_some: Some("non-escaped-string") => "non-escaped-string"); err!(unit: () - => Unsupported("unit type `()` cannot be serialized as an XML tag name")); + => Unsupported("cannot serialize unit type `()` as an XML tag name")); err!(unit_struct: Unit - => Unsupported("unit struct `Unit` cannot be serialized as an XML tag name")); + => Unsupported("cannot serialize unit struct `Unit` as an XML tag name")); serialize_as!(enum_unit: Enum::Unit => "Unit"); serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<\"&'>"); serialize_as!(newtype: Newtype(true) => "true"); err!(enum_newtype: Enum::Newtype(false) - => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an XML tag name")); + => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an XML tag name")); err!(seq: vec![1, 2, 3] - => Unsupported("sequence cannot be serialized as an XML tag name")); + => Unsupported("cannot serialize sequence as an XML tag name")); err!(tuple: ("<\"&'>", "with\t\r\n spaces", 3usize) - => Unsupported("tuple cannot be serialized as an XML tag name")); + => Unsupported("cannot serialize tuple as an XML tag name")); err!(tuple_struct: Tuple("first", 42) - => Unsupported("tuple struct `Tuple` cannot be serialized as an XML tag name")); + => Unsupported("cannot serialize tuple struct `Tuple` as an XML tag name")); err!(enum_tuple: Enum::Tuple("first", 42) - => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an XML tag name")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an XML tag name")); err!(map: BTreeMap::from([("_1", 2), ("_3", 4)]) - => Unsupported("map cannot be serialized as an XML tag name")); + => Unsupported("cannot serialize map as an XML tag name")); err!(struct_: Struct { key: "answer", val: 42 } - => Unsupported("struct `Struct` cannot be serialized as an XML tag name")); + => Unsupported("cannot serialize struct `Struct` as an XML tag name")); err!(enum_struct: Enum::Struct { key: "answer", val: 42 } - => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an XML tag name")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an XML tag name")); } diff --git a/src/se/simple_type.rs b/src/se/simple_type.rs index 55832ed6..75d5d5f9 100644 --- a/src/se/simple_type.rs +++ b/src/se/simple_type.rs @@ -206,7 +206,7 @@ impl Serializer for AtomicSerializer { /// does not differ, so serialization of unit returns `Err(Unsupported)` fn serialize_unit(self) -> Result { Err(DeError::Unsupported( - "unit type `()` cannot be serialized as an `xs:list` item".into(), + "cannot serialize unit type `()` as an `xs:list` item".into(), )) } @@ -215,7 +215,7 @@ impl Serializer for AtomicSerializer { fn serialize_unit_struct(self, name: &'static str) -> Result { Err(DeError::Unsupported( format!( - "unit struct `{}` cannot be serialized as an `xs:list` item", + "cannot serialize unit struct `{}` as an `xs:list` item", name ) .into(), @@ -233,7 +233,7 @@ impl Serializer for AtomicSerializer { ) -> Result { Err(DeError::Unsupported( format!( - "enum newtype variant `{}::{}` cannot be serialized as an `xs:list` item", + "cannot serialize enum newtype variant `{}::{}` as an `xs:list` item", name, variant ) .into(), @@ -242,13 +242,13 @@ impl Serializer for AtomicSerializer { fn serialize_seq(self, _len: Option) -> Result { Err(DeError::Unsupported( - "sequence cannot be serialized as an `xs:list` item".into(), + "cannot serialize sequence as an `xs:list` item".into(), )) } fn serialize_tuple(self, _len: usize) -> Result { Err(DeError::Unsupported( - "tuple cannot be serialized as an `xs:list` item".into(), + "cannot serialize tuple as an `xs:list` item".into(), )) } @@ -259,7 +259,7 @@ impl Serializer for AtomicSerializer { ) -> Result { Err(DeError::Unsupported( format!( - "tuple struct `{}` cannot be serialized as an `xs:list` item", + "cannot serialize tuple struct `{}` as an `xs:list` item", name ) .into(), @@ -275,7 +275,7 @@ impl Serializer for AtomicSerializer { ) -> Result { Err(DeError::Unsupported( format!( - "enum tuple variant `{}::{}` cannot be serialized as an `xs:list` item", + "cannot serialize enum tuple variant `{}::{}` as an `xs:list` item", name, variant ) .into(), @@ -284,7 +284,7 @@ impl Serializer for AtomicSerializer { fn serialize_map(self, _len: Option) -> Result { Err(DeError::Unsupported( - "map cannot be serialized as an `xs:list` item".into(), + "cannot serialize map as an `xs:list` item".into(), )) } @@ -295,7 +295,7 @@ impl Serializer for AtomicSerializer { ) -> Result { Err(DeError::Unsupported( format!( - "struct `{}` cannot be serialized as an `xs:list` item", + "cannot serialize struct `{}` as an `xs:list` item", name ) .into(), @@ -311,7 +311,7 @@ impl Serializer for AtomicSerializer { ) -> Result { Err(DeError::Unsupported( format!( - "enum struct variant `{}::{}` cannot be serialized as an `xs:list` item", + "cannot serialize enum struct variant `{}::{}` as an `xs:list` item", name, variant ) .into(), @@ -389,7 +389,7 @@ impl<'i, W: Write> Serializer for SimpleTypeSerializer<'i, W> { _value: &T, ) -> Result { Err(DeError::Unsupported( - format!("enum newtype variant `{}::{}` cannot be serialized as an attribute or text content value", name, variant).into(), + format!("cannot serialize enum newtype variant `{}::{}` as an attribute or text content value", name, variant).into(), )) } @@ -426,13 +426,13 @@ impl<'i, W: Write> Serializer for SimpleTypeSerializer<'i, W> { _len: usize, ) -> Result { Err(DeError::Unsupported( - format!("enum tuple variant `{}::{}` cannot be serialized as an attribute or text content value", name, variant).into(), + format!("cannot serialize enum tuple variant `{}::{}` as an attribute or text content value", name, variant).into(), )) } fn serialize_map(self, _len: Option) -> Result { Err(DeError::Unsupported( - "map cannot be serialized as an attribute or text content value".into(), + "cannot serialize map as an attribute or text content value".into(), )) } @@ -443,7 +443,7 @@ impl<'i, W: Write> Serializer for SimpleTypeSerializer<'i, W> { ) -> Result { Err(DeError::Unsupported( format!( - "struct `{}` cannot be serialized as an attribute or text content value", + "cannot serialize struct `{}` as an attribute or text content value", name ) .into(), @@ -458,7 +458,7 @@ impl<'i, W: Write> Serializer for SimpleTypeSerializer<'i, W> { _len: usize, ) -> Result { Err(DeError::Unsupported( - format!("enum struct variant `{}::{}` cannot be serialized as an attribute or text content value", name, variant).into(), + format!("cannot serialize enum struct variant `{}::{}` as an attribute or text content value", name, variant).into(), )) } } @@ -912,32 +912,32 @@ mod tests { serialize_as!(option_some: Some("non-escaped-string") => "non-escaped-string"); err!(unit: () - => Unsupported("unit type `()` cannot be serialized as an `xs:list` item")); + => Unsupported("cannot serialize unit type `()` as an `xs:list` item")); err!(unit_struct: Unit - => Unsupported("unit struct `Unit` cannot be serialized as an `xs:list` item")); + => Unsupported("cannot serialize unit struct `Unit` as an `xs:list` item")); serialize_as!(enum_unit: Enum::Unit => "Unit"); serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>"); serialize_as!(newtype: Newtype(42) => "42"); err!(enum_newtype: Enum::Newtype(42) - => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an `xs:list` item")); + => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an `xs:list` item")); err!(seq: vec![1, 2, 3] - => Unsupported("sequence cannot be serialized as an `xs:list` item")); + => Unsupported("cannot serialize sequence as an `xs:list` item")); err!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) - => Unsupported("tuple cannot be serialized as an `xs:list` item")); + => Unsupported("cannot serialize tuple as an `xs:list` item")); err!(tuple_struct: Tuple("first", 42) - => Unsupported("tuple struct `Tuple` cannot be serialized as an `xs:list` item")); + => Unsupported("cannot serialize tuple struct `Tuple` as an `xs:list` item")); err!(enum_tuple: Enum::Tuple("first", 42) - => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an `xs:list` item")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an `xs:list` item")); err!(map: BTreeMap::from([(1, 2), (3, 4)]) - => Unsupported("map cannot be serialized as an `xs:list` item")); + => Unsupported("cannot serialize map as an `xs:list` item")); err!(struct_: Struct { key: "answer", val: 42 } - => Unsupported("struct `Struct` cannot be serialized as an `xs:list` item")); + => Unsupported("cannot serialize struct `Struct` as an `xs:list` item")); err!(enum_struct: Enum::Struct { key: "answer", val: 42 } - => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an `xs:list` item")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an `xs:list` item")); } mod simple_type { @@ -1037,7 +1037,7 @@ mod tests { serialize_as!(newtype: Newtype(42) => "42"); err!(enum_newtype: Enum::Newtype(42) - => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); serialize_as!(seq: vec![1, 2, 3] => "1 2 3"); serialize_as!(seq_empty: Vec::::new() => ""); @@ -1047,13 +1047,13 @@ mod tests { => "<"&'> with spaces 3"); serialize_as!(tuple_struct: Tuple("first", 42) => "first 42"); err!(enum_tuple: Enum::Tuple("first", 42) - => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); err!(map: BTreeMap::from([(1, 2), (3, 4)]) - => Unsupported("map cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize map as an attribute or text content value")); err!(struct_: Struct { key: "answer", val: 42 } - => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize struct `Struct` as an attribute or text content value")); err!(enum_struct: Enum::Struct { key: "answer", val: 42 } - => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value")); } } diff --git a/tests/serde-se.rs b/tests/serde-se.rs index b3fed32a..f6abf5a8 100644 --- a/tests/serde-se.rs +++ b/tests/serde-se.rs @@ -451,7 +451,7 @@ mod without_root { } // It is unknown how to exactly serialize unit to a text - err!(unit: Unit::Text => Unsupported("`Unit::$text` unit variant cannot be serialized")); + err!(unit: Unit::Text => Unsupported("cannot serialize enum unit variant `Unit::$text` as text content value")); serialize_as!(newtype: Newtype::Text("newtype text") => "newtype text"); // Tuple variant serialized as an `xs:list` serialize_as!(tuple: Tuple::Text(4.2, "newtype-text".into()) => "4.2 newtype-text"); @@ -466,7 +466,7 @@ mod without_root { float: 4.2, string: "newtype text", } - => Unsupported("`Struct::$text` struct variant cannot be serialized")); + => Unsupported("cannot serialize enum struct variant `Struct::$text` as text content value")); } } @@ -912,7 +912,7 @@ mod without_root { } // It is unknown how to exactly serialize unit to a text - err!(unit: Unit::Text => Unsupported("`Unit::$text` unit variant cannot be serialized")); + err!(unit: Unit::Text => Unsupported("cannot serialize enum unit variant `Unit::$text` as text content value")); serialize_as!(newtype: Newtype::Text("newtype text") => "newtype text"); // Tuple variant serialized as an `xs:list` serialize_as!(tuple: Tuple::Text(4.2, "newtype text") => "4.2 newtype text"); @@ -922,7 +922,7 @@ mod without_root { float: 4.2, string: "newtype text", } - => Unsupported("`Struct::$text` struct variant cannot be serialized")); + => Unsupported("cannot serialize enum struct variant `Struct::$text` as text content value")); } } @@ -1403,7 +1403,7 @@ mod with_root { } // It is unknown how to exactly serialize unit to a text - err!(unit: Unit::Text => Unsupported("`Unit::$text` unit variant cannot be serialized")); + err!(unit: Unit::Text => Unsupported("cannot serialize enum unit variant `Unit::$text` as text content value")); serialize_as!(newtype: Newtype::Text("newtype text") => "newtype text"); // Tuple variant serialized as an `xs:list` serialize_as!(tuple: Tuple::Text(4.2, "newtype-text".into()) => "4.2 newtype-text"); @@ -1418,7 +1418,7 @@ mod with_root { float: 4.2, string: "newtype text", } - => Unsupported("`Struct::$text` struct variant cannot be serialized")); + => Unsupported("cannot serialize enum struct variant `Struct::$text` as text content value")); } } From f71c6a71aa4a7ea65ee5a683bf5e7603bf96c0bf Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 17 Sep 2023 20:36:49 +0500 Subject: [PATCH 4/5] Run `cargo fmt` --- src/se/element.rs | 12 ++++++++++-- src/se/key.rs | 6 +----- src/se/simple_type.rs | 6 +----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/se/element.rs b/src/se/element.rs index a4e3eb4d..75be2da9 100644 --- a/src/se/element.rs +++ b/src/se/element.rs @@ -112,7 +112,11 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> { if variant == TEXT_KEY { // We should write some text but we don't known what text to write Err(DeError::Unsupported( - format!("cannot serialize enum unit variant `{}::$text` as text content value", name).into(), + format!( + "cannot serialize enum unit variant `{}::$text` as text content value", + name + ) + .into(), )) } else { let name = XmlName::try_from(variant)?; @@ -216,7 +220,11 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> { ) -> Result { if variant == TEXT_KEY { Err(DeError::Unsupported( - format!("cannot serialize enum struct variant `{}::$text` as text content value", name).into(), + format!( + "cannot serialize enum struct variant `{}::$text` as text content value", + name + ) + .into(), )) } else { self.key = XmlName::try_from(variant)?; diff --git a/src/se/key.rs b/src/se/key.rs index bd8ebc25..f5f6e047 100644 --- a/src/se/key.rs +++ b/src/se/key.rs @@ -54,11 +54,7 @@ impl Serializer for QNameSerializer { /// a valid XML name, serialization of unit struct returns `Err(Unsupported)` fn serialize_unit_struct(self, name: &'static str) -> Result { Err(DeError::Unsupported( - format!( - "cannot serialize unit struct `{}` as an XML tag name", - name - ) - .into(), + format!("cannot serialize unit struct `{}` as an XML tag name", name).into(), )) } diff --git a/src/se/simple_type.rs b/src/se/simple_type.rs index 75d5d5f9..58a4ca3b 100644 --- a/src/se/simple_type.rs +++ b/src/se/simple_type.rs @@ -294,11 +294,7 @@ impl Serializer for AtomicSerializer { _len: usize, ) -> Result { Err(DeError::Unsupported( - format!( - "cannot serialize struct `{}` as an `xs:list` item", - name - ) - .into(), + format!("cannot serialize struct `{}` as an `xs:list` item", name).into(), )) } From a2060e96b7d569334a9a8da2cc19f59589cb0e4b Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 17 Sep 2023 04:12:03 +0500 Subject: [PATCH 5/5] Additional tests with enum in the struct --- tests/serde-se.rs | 499 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 494 insertions(+), 5 deletions(-) diff --git a/tests/serde-se.rs b/tests/serde-se.rs index f6abf5a8..c91c241e 100644 --- a/tests/serde-se.rs +++ b/tests/serde-se.rs @@ -78,7 +78,7 @@ enum ExternallyTagged { /// /// Anyway, deserialization of that type in roundtrip suffers from /// -#[derive(Serialize)] +#[derive(Debug, PartialEq, Deserialize, Serialize)] enum ExternallyTaggedWorkaround { Flatten { #[serde(flatten)] @@ -213,7 +213,7 @@ mod without_root { use pretty_assertions::assert_eq; macro_rules! serialize_as { - ($name:ident: $data:expr => $expected:literal) => { + ($name:ident: $data:expr => $expected:expr) => { #[test] fn $name() { serialize_as!(@ $data => $expected); @@ -226,7 +226,7 @@ mod without_root { ); } }; - (@ $data:expr => $expected:literal) => { + (@ $data:expr => $expected:expr) => { let mut buffer = String::new(); let ser = Serializer::new(&mut buffer); @@ -246,7 +246,7 @@ mod without_root { /// Checks that attempt to serialize given `$data` results to a /// serialization error `$kind` with `$reason` macro_rules! err { - ($name:ident: $data:expr => $kind:ident($reason:literal)) => { + ($name:ident: $data:expr => $kind:ident($reason:literal), $buffer:literal) => { #[test] fn $name() { let mut buffer = String::new(); @@ -261,9 +261,12 @@ mod without_root { e ), } - assert_eq!(buffer, ""); + assert_eq!(buffer, $buffer); } }; + ($name:ident: $data:expr => $kind:ident($reason:literal)) => { + err!($name: $data => $kind($reason), ""); + }; } err!(false_: false => Unsupported("cannot serialize `bool` without defined root tag")); @@ -370,6 +373,11 @@ mod without_root { use super::*; use pretty_assertions::assert_eq; + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct Root { + field: T, + } + serialize_as!(unit: ExternallyTagged::Unit => ""); @@ -468,6 +476,487 @@ mod without_root { } => Unsupported("cannot serialize enum struct variant `Struct::$text` as text content value")); } + + /// Tests the enum type that is type of field of a struct. + /// The tests above does not cover those variants, because we use + /// different serializers for enums on top level and which represents + /// a field. + /// + /// Deserialization is not possible because we cannot choose with what + /// field we should associate the XML node that we see. To do that we + /// mark field by a special name `$value` ([`VALUE_KEY`]) and that is + /// tested in the `in_struct_value` module. + mod in_struct { + use super::*; + use pretty_assertions::assert_eq; + + serialize_as_only!(unit: + Root { field: ExternallyTagged::Unit } + => "\ + \ + "); + serialize_as_only!(newtype: + Root { field: ExternallyTagged::Newtype(true) } + => "\ + true\ + "); + serialize_as_only!(tuple_struct: + Root { field: ExternallyTagged::Tuple(42.0, "answer") } + => "\ + 42\ + answer\ + "); + serialize_as_only!(struct_: + Root { field: ExternallyTagged::Struct { + float: 42.0, + string: "answer" + }} + => "\ + \ + 42\ + answer\ + \ + "); + serialize_as_only!(nested_struct: + Root { field: ExternallyTagged::Holder { + nested: Nested { float: 42.0 }, + string: "answer", + }} + => "\ + \ + \ + 42\ + \ + answer\ + \ + "); + serialize_as_only!(flatten_struct: + Root { field: ExternallyTaggedWorkaround::Flatten { + nested: Nested { float: 42.0 }, + string: "answer", + }} + => "\ + \ + 42\ + answer\ + \ + "); + serialize_as_only!(empty_struct: + Root { field: ExternallyTagged::Empty {} } + => "\ + \ + "); + serialize_as_only!(text: + Root { field: ExternallyTagged::Text { + float: 42.0, + string: "answer" + }} + => "\ + \ + 42\ + answer\ + \ + "); + } + + /// The same tests as in `in_struct`, but enum at the second nesting + /// level. + mod in_struct2 { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct Root { + field: T, + } + + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct Inner { + inner: T, + } + + serialize_as_only!(unit: + Root { field: Inner { inner: ExternallyTagged::Unit } } + => "\ + \ + \ + \ + "); + serialize_as_only!(newtype: + Root { field: Inner { inner: ExternallyTagged::Newtype(true) } } + => "\ + \ + true\ + \ + "); + serialize_as_only!(tuple_struct: + Root { field: Inner { inner: ExternallyTagged::Tuple(42.0, "answer") } } + => "\ + \ + 42\ + answer\ + \ + "); + serialize_as_only!(struct_: + Root { field: Inner { inner: ExternallyTagged::Struct { + float: 42.0, + string: "answer" + }}} + => "\ + \ + \ + 42\ + answer\ + \ + \ + "); + serialize_as_only!(nested_struct: + Root { field: Inner { inner: ExternallyTagged::Holder { + nested: Nested { float: 42.0 }, + string: "answer", + }}} + => "\ + \ + \ + \ + 42\ + \ + answer\ + \ + \ + "); + serialize_as_only!(flatten_struct: + Root { field: Inner { inner: ExternallyTaggedWorkaround::Flatten { + nested: Nested { float: 42.0 }, + string: "answer", + }}} + => "\ + \ + \ + 42\ + answer\ + \ + \ + "); + serialize_as_only!(empty_struct: + Root { field: Inner { inner: ExternallyTagged::Empty {} } } + => "\ + \ + \ + \ + "); + serialize_as_only!(text: + Root { field: Inner { inner: ExternallyTagged::Text { + float: 42.0, + string: "answer" + }}} + => "\ + \ + \ + 42\ + answer\ + \ + \ + "); + } + + /// The same tests as in `in_struct`, but enum field renamed to `$value`. + mod in_struct_value { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct Root { + #[serde(rename = "$value")] + field: T, + } + + serialize_as!(unit: + Root { field: ExternallyTagged::Unit } + => "\ + \ + "); + serialize_as!(newtype: + Root { field: ExternallyTagged::Newtype(true) } + => "\ + true\ + "); + serialize_as!(tuple_struct: + Root { field: ExternallyTagged::Tuple(42.0, "answer") } + => "\ + 42\ + answer\ + "); + serialize_as!(struct_: + Root { field: ExternallyTagged::Struct { + float: 42.0, + string: "answer" + }} + => "\ + \ + 42\ + answer\ + \ + "); + serialize_as!(nested_struct: + Root { field: ExternallyTagged::Holder { + nested: Nested { float: 42.0 }, + string: "answer", + }} + => "\ + \ + \ + 42\ + \ + answer\ + \ + "); + // NOTE: Cannot be deserialized in roundtrip due to + // https://github.com/serde-rs/serde/issues/1183 + serialize_as_only!(flatten_struct: + Root { field: ExternallyTaggedWorkaround::Flatten { + nested: Nested { float: 42.0 }, + string: "answer", + }} + => "\ + \ + 42\ + answer\ + \ + "); + serialize_as!(empty_struct: + Root { field: ExternallyTagged::Empty {} } + => "\ + \ + "); + serialize_as!(text: + Root { field: ExternallyTagged::Text { + float: 42.0, + string: "answer" + }} + => "\ + \ + 42\ + answer\ + \ + "); + } + + /// The same tests as in `in_struct2`, but enum field renamed to `$value`. + mod in_struct_value2 { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct Inner { + #[serde(rename = "$value")] + inner: T, + } + + serialize_as!(unit: + Root { field: Inner { inner: ExternallyTagged::Unit } } + => "\ + \ + \ + \ + "); + serialize_as!(newtype: + Root { field: Inner { inner: ExternallyTagged::Newtype(true) } } + => "\ + \ + true\ + \ + "); + serialize_as!(tuple_struct: + Root { field: Inner { inner: ExternallyTagged::Tuple(42.0, "answer") } } + => "\ + \ + 42\ + answer\ + \ + "); + serialize_as!(struct_: + Root { field: Inner { inner: ExternallyTagged::Struct { + float: 42.0, + string: "answer" + }}} + => "\ + \ + \ + 42\ + answer\ + \ + \ + "); + serialize_as!(nested_struct: + Root { field: Inner { inner: ExternallyTagged::Holder { + nested: Nested { float: 42.0 }, + string: "answer", + }}} + => "\ + \ + \ + \ + 42\ + \ + answer\ + \ + \ + "); + // NOTE: Cannot be deserialized in roundtrip due to + // https://github.com/serde-rs/serde/issues/1183 + serialize_as_only!(flatten_struct: + Root { field: Inner { inner: ExternallyTaggedWorkaround::Flatten { + nested: Nested { float: 42.0 }, + string: "answer", + }}} + => "\ + \ + \ + 42\ + answer\ + \ + \ + "); + serialize_as!(empty_struct: + Root { field: Inner { inner: ExternallyTagged::Empty {} } } + => "\ + \ + \ + \ + "); + serialize_as!(text: + Root { field: Inner { inner: ExternallyTagged::Text { + float: 42.0, + string: "answer" + }}} + => "\ + \ + \ + 42\ + answer\ + \ + \ + "); + } + + /// The same tests as in `in_struct`, but enum field renamed to `$text`. + /// + /// Text representation of enum is possible only for unit variants. + mod in_struct_text { + use super::*; + use pretty_assertions::assert_eq; + + #[derive(Debug, PartialEq, Deserialize, Serialize)] + struct Root { + #[serde(rename = "$text")] + field: T, + } + + serialize_as!(unit: + Root { field: ExternallyTagged::Unit } + => "Unit"); + err!(newtype: + Root { field: ExternallyTagged::Newtype(true) } + => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype` as an attribute or text content value"), + " Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple` as an attribute or text content value"), + " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct` as an attribute or text content value"), + " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Holder` as an attribute or text content value"), + " Unsupported("cannot serialize enum newtype variant `ExternallyTaggedWorkaround::Flatten` as an attribute or text content value"), + " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty` as an attribute or text content value"), + " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Text` as an attribute or text content value"), + " { + #[serde(rename = "$text")] + inner: T, + } + + serialize_as!(unit: + Root { field: Inner { inner: ExternallyTagged::Unit } } + => "Unit"); + err!(newtype: + Root { field: Inner { inner: ExternallyTagged::Newtype(true) } } + => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype` as an attribute or text content value"), + " Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple` as an attribute or text content value"), + " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct` as an attribute or text content value"), + " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Holder` as an attribute or text content value"), + " Unsupported("cannot serialize enum newtype variant `ExternallyTaggedWorkaround::Flatten` as an attribute or text content value"), + " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty` as an attribute or text content value"), + " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Text` as an attribute or text content value"), + "