aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJooyung Han <jooyung@google.com>2021-07-15 19:01:25 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-07-15 19:01:25 +0000
commit054c65f3a29f08c2514b9277139430c0d6c39bb9 (patch)
tree33bde6c515a7bd7b50de72cedb31a280a14d37a1
parent242244901480917fe3d564dad6e48d664a78ebc4 (diff)
parentaa251e43eb2bb8f02e5106efd4fb5a9f8168bdd4 (diff)
downloadserde-xml-rs-054c65f3a29f08c2514b9277139430c0d6c39bb9.tar.gz
Import serde-xml-rs 0.4.1 am: 1e046a8595 am: ecb08a10cc am: fc2bfac46b am: aa251e43eb
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/serde-xml-rs/+/1767647 Change-Id: Id697721de05be6bf7d29ff4b1886574eade3ec9f
-rw-r--r--Android.bp34
-rw-r--r--Cargo.toml18
-rw-r--r--LICENSE21
-rw-r--r--METADATA17
-rw-r--r--MODULE_LICENSE_MIT0
-rw-r--r--OWNERS2
-rw-r--r--README.md62
-rw-r--r--cargo2android.json9
-rw-r--r--rustfmt.toml1
-rw-r--r--src/de/map.rs126
-rw-r--r--src/de/mod.rs367
-rw-r--r--src/de/seq.rs70
-rw-r--r--src/de/var.rs84
-rw-r--r--src/error.rs104
-rw-r--r--src/lib.rs55
-rw-r--r--src/ser/mod.rs419
-rw-r--r--src/ser/var.rs103
-rw-r--r--tests/failures.rs52
-rw-r--r--tests/migrated.rs1086
-rw-r--r--tests/readme.rs6
-rw-r--r--tests/round_trip.rs110
-rw-r--r--tests/test.rs161
22 files changed, 2907 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..ee9f639
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,34 @@
+// This file is generated by cargo2android.py --config cargo2android.json.
+// Do not modify this file as changes will be overridden on upgrade.
+
+
+
+rust_library {
+ name: "libserde_xml_rs",
+ host_supported: true,
+ crate_name: "serde_xml_rs",
+ srcs: ["src/lib.rs"],
+ edition: "2015",
+ rustlibs: [
+ "liblog_rust",
+ "libserde",
+ "libthiserror",
+ "libxml",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
+}
+
+// dependent_library ["feature_list"]
+// cfg-if-1.0.0
+// log-0.4.14 "std"
+// proc-macro2-1.0.27 "default,proc-macro"
+// quote-1.0.9 "default,proc-macro"
+// serde-1.0.126 "default,std"
+// syn-1.0.73 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+// thiserror-1.0.26
+// thiserror-impl-1.0.26
+// unicode-xid-0.2.2 "default"
+// xml-rs-0.8.3
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..fa4d9b9
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+authors = ["Ingvar Stepanyan <me@rreverser.com>"]
+description = "xml-rs based deserializer for Serde (compatible with 0.9+)"
+license = "MIT"
+name = "serde-xml-rs"
+repository = "https://github.com/RReverser/serde-xml-rs"
+version = "0.4.1"
+
+[dependencies]
+log = "0.4"
+serde = "1.0"
+xml-rs = "0.8.0"
+thiserror = "1.0"
+
+[dev-dependencies]
+serde_derive = "1.0"
+simple_logger = "1.0.1"
+docmatic = "0.1.2"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..774d94a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Ingvar Stepanyan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..a1b4d08
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,17 @@
+name: "serde-xml-rs"
+description:
+ "xml-rs based deserializer for Serde"
+
+third_party {
+ url {
+ type: HOMEPAGE
+ value: "https://crates.io/crates/serde_xml_rs"
+ }
+ url {
+ type: GIT
+ value: "https://github.com/RReverser/serde-xml-rs"
+ }
+ version: "0.4.1"
+ last_upgrade_date { year: 2021 month: 6 day: 21 }
+ license_type: NOTICE
+} \ No newline at end of file
diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_MIT
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..98a1473
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+include platform/prebuilts/rust:/OWNERS
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f2e530c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,62 @@
+# serde-xml-rs
+
+[![Build Status](https://travis-ci.org/RReverser/serde-xml-rs.svg?branch=master)](https://travis-ci.org/RReverser/serde-xml-rs)
+
+xml-rs based deserializer for Serde (compatible with 0.9+)
+
+## Usage
+
+Use `serde_xml_rs::from_reader(...)` on any type that implements [`std::io::Read`](https://doc.rust-lang.org/std/io/trait.Read.html) as following:
+
+```rust
+#[macro_use]
+extern crate serde_derive;
+extern crate serde;
+extern crate serde_xml_rs;
+
+use serde_xml_rs::from_reader;
+
+#[derive(Debug, Deserialize)]
+struct Item {
+ pub name: String,
+ pub source: String
+}
+
+#[derive(Debug, Deserialize)]
+struct Project {
+ pub name: String,
+
+ #[serde(rename = "Item", default)]
+ pub items: Vec<Item>
+}
+
+fn main() {
+ let s = r##"
+ <Project name="my_project">
+ <Item name="hello" source="world.rs" />
+ </Project>
+ "##;
+ let project: Project = from_reader(s.as_bytes()).unwrap();
+ println!("{:#?}", project);
+}
+```
+
+Alternatively, you can use `serde_xml_rs::Deserializer` to create a deserializer from a preconfigured [`xml_rs::EventReader`](https://netvl.github.io/xml-rs/xml/reader/struct.EventReader.html).
+
+## Parsing the "value" of a tag
+
+If you have an input of the form `<foo abc="xyz">bar</foo>`, and you want to get at the`bar`, you can use the special name `$value`:
+
+```rust,ignore
+struct Foo {
+ pub abc: String,
+ #[serde(rename = "$value")]
+ pub body: String,
+}
+```
+
+## Parsed representations
+
+Deserializer tries to be as intuitive as possible.
+
+However, there are some edge cases where you might get unexpected errors, so it's best to check out [`tests`](tests/test.rs) for expectations.
diff --git a/cargo2android.json b/cargo2android.json
new file mode 100644
index 0000000..42b7833
--- /dev/null
+++ b/cargo2android.json
@@ -0,0 +1,9 @@
+{
+ "apex-available": [
+ "//apex_available:platform",
+ "com.android.virt"
+ ],
+ "dependencies": true,
+ "device": true,
+ "run": true
+} \ No newline at end of file
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644
index 0000000..8c795ae
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+match_block_trailing_comma = true
diff --git a/src/de/map.rs b/src/de/map.rs
new file mode 100644
index 0000000..ed1d0bb
--- /dev/null
+++ b/src/de/map.rs
@@ -0,0 +1,126 @@
+use std::io::Read;
+
+use serde::de::{self, IntoDeserializer, Unexpected};
+use xml::attribute::OwnedAttribute;
+use xml::reader::XmlEvent;
+
+use Deserializer;
+use error::{Error, Result};
+
+pub struct MapAccess<'a, R: 'a + Read> {
+ attrs: ::std::vec::IntoIter<OwnedAttribute>,
+ next_value: Option<String>,
+ de: &'a mut Deserializer<R>,
+ inner_value: bool,
+}
+
+impl<'a, R: 'a + Read> MapAccess<'a, R> {
+ pub fn new(de: &'a mut Deserializer<R>, attrs: Vec<OwnedAttribute>, inner_value: bool) -> Self {
+ MapAccess {
+ attrs: attrs.into_iter(),
+ next_value: None,
+ de: de,
+ inner_value: inner_value,
+ }
+ }
+}
+
+impl<'de, 'a, R: 'a + Read> de::MapAccess<'de> for MapAccess<'a, R> {
+ type Error = Error;
+
+ fn next_key_seed<K: de::DeserializeSeed<'de>>(&mut self, seed: K) -> Result<Option<K::Value>> {
+ debug_assert_eq!(self.next_value, None);
+ match self.attrs.next() {
+ Some(OwnedAttribute { name, value }) => {
+ self.next_value = Some(value);
+ seed.deserialize(name.local_name.into_deserializer())
+ .map(Some)
+ },
+ None => match *self.de.peek()? {
+ XmlEvent::StartElement { ref name, .. } => seed.deserialize(
+ if !self.inner_value {
+ name.local_name.as_str()
+ } else {
+ "$value"
+ }.into_deserializer(),
+ ).map(Some),
+ XmlEvent::Characters(_) => seed.deserialize("$value".into_deserializer()).map(Some),
+ _ => Ok(None),
+ },
+ }
+ }
+
+ fn next_value_seed<V: de::DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value> {
+ match self.next_value.take() {
+ Some(value) => seed.deserialize(AttrValueDeserializer(value)),
+ None => {
+ if !self.inner_value {
+ if let XmlEvent::StartElement { .. } = *self.de.peek()? {
+ self.de.set_map_value();
+ }
+ }
+ let result = seed.deserialize(&mut *self.de)?;
+ Ok(result)
+ },
+ }
+ }
+
+ fn size_hint(&self) -> Option<usize> {
+ self.attrs.size_hint().1
+ }
+}
+
+struct AttrValueDeserializer(String);
+
+macro_rules! deserialize_type_attr {
+ ($deserialize:ident => $visit:ident) => {
+ fn $deserialize<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ visitor.$visit(self.0.parse()?)
+ }
+ }
+}
+
+impl<'de> de::Deserializer<'de> for AttrValueDeserializer {
+ type Error = Error;
+
+ fn deserialize_any<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ visitor.visit_string(self.0)
+ }
+
+ deserialize_type_attr!(deserialize_i8 => visit_i8);
+ deserialize_type_attr!(deserialize_i16 => visit_i16);
+ deserialize_type_attr!(deserialize_i32 => visit_i32);
+ deserialize_type_attr!(deserialize_i64 => visit_i64);
+ deserialize_type_attr!(deserialize_u8 => visit_u8);
+ deserialize_type_attr!(deserialize_u16 => visit_u16);
+ deserialize_type_attr!(deserialize_u32 => visit_u32);
+ deserialize_type_attr!(deserialize_u64 => visit_u64);
+ deserialize_type_attr!(deserialize_f32 => visit_f32);
+ deserialize_type_attr!(deserialize_f64 => visit_f64);
+
+ fn deserialize_enum<V: de::Visitor<'de>>(
+ self,
+ _name: &str,
+ _variants: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value> {
+ visitor.visit_enum(self.0.into_deserializer())
+ }
+
+ fn deserialize_option<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ visitor.visit_some(self)
+ }
+
+ fn deserialize_bool<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ match self.0.as_str() {
+ "true" | "1" => visitor.visit_bool(true),
+ "false" | "0" => visitor.visit_bool(false),
+ _ => Err(de::Error::invalid_value(Unexpected::Str(&self.0), &"a boolean")),
+ }
+ }
+
+ forward_to_deserialize_any! {
+ char str string unit seq bytes map unit_struct newtype_struct tuple_struct
+ struct identifier tuple ignored_any byte_buf
+ }
+}
diff --git a/src/de/mod.rs b/src/de/mod.rs
new file mode 100644
index 0000000..94077c5
--- /dev/null
+++ b/src/de/mod.rs
@@ -0,0 +1,367 @@
+use std::io::Read;
+
+use serde::de::{self, Unexpected};
+use xml::name::OwnedName;
+use xml::reader::{EventReader, ParserConfig, XmlEvent};
+
+use self::map::MapAccess;
+use self::seq::SeqAccess;
+use self::var::EnumAccess;
+use error::{Error, Result};
+
+mod map;
+mod seq;
+mod var;
+
+/// A convenience method for deserialize some object from a string.
+///
+/// ```rust
+/// # #[macro_use]
+/// # extern crate serde_derive;
+/// # extern crate serde;
+/// # extern crate serde_xml_rs;
+/// # use serde_xml_rs::from_str;
+/// #[derive(Debug, Deserialize, PartialEq)]
+/// struct Item {
+/// name: String,
+/// source: String,
+/// }
+/// # fn main() {
+/// let s = r##"<item name="hello" source="world.rs" />"##;
+/// let item: Item = from_str(s).unwrap();
+/// assert_eq!(item, Item { name: "hello".to_string(),source: "world.rs".to_string()});
+/// # }
+/// ```
+pub fn from_str<'de, T: de::Deserialize<'de>>(s: &str) -> Result<T> {
+ from_reader(s.as_bytes())
+}
+
+/// A convenience method for deserialize some object from a reader.
+///
+/// ```rust
+/// # #[macro_use]
+/// # extern crate serde_derive;
+/// # extern crate serde;
+/// # extern crate serde_xml_rs;
+/// # use serde_xml_rs::from_reader;
+/// #[derive(Debug, Deserialize, PartialEq)]
+/// struct Item {
+/// name: String,
+/// source: String,
+/// }
+/// # fn main() {
+/// let s = r##"<item name="hello" source="world.rs" />"##;
+/// let item: Item = from_reader(s.as_bytes()).unwrap();
+/// assert_eq!(item, Item { name: "hello".to_string(),source: "world.rs".to_string()});
+/// # }
+/// ```
+pub fn from_reader<'de, R: Read, T: de::Deserialize<'de>>(reader: R) -> Result<T> {
+ T::deserialize(&mut Deserializer::new_from_reader(reader))
+}
+
+pub struct Deserializer<R: Read> {
+ depth: usize,
+ reader: EventReader<R>,
+ peeked: Option<XmlEvent>,
+ is_map_value: bool,
+}
+
+impl<'de, R: Read> Deserializer<R> {
+ pub fn new(reader: EventReader<R>) -> Self {
+ Deserializer {
+ depth: 0,
+ reader: reader,
+ peeked: None,
+ is_map_value: false,
+ }
+ }
+
+ pub fn new_from_reader(reader: R) -> Self {
+ let config = ParserConfig::new()
+ .trim_whitespace(true)
+ .whitespace_to_characters(true)
+ .cdata_to_characters(true)
+ .ignore_comments(true)
+ .coalesce_characters(true);
+
+ Self::new(EventReader::new_with_config(reader, config))
+ }
+
+ fn peek(&mut self) -> Result<&XmlEvent> {
+ if self.peeked.is_none() {
+ self.peeked = Some(self.inner_next()?);
+ }
+ debug_expect!(self.peeked.as_ref(), Some(peeked) => {
+ debug!("Peeked {:?}", peeked);
+ Ok(peeked)
+ })
+ }
+
+ fn inner_next(&mut self) -> Result<XmlEvent> {
+ loop {
+ match self.reader.next()? {
+ XmlEvent::StartDocument { .. }
+ | XmlEvent::ProcessingInstruction { .. }
+ | XmlEvent::Whitespace { .. }
+ | XmlEvent::Comment(_) => { /* skip */ }
+ other => return Ok(other),
+ }
+ }
+ }
+
+ fn next(&mut self) -> Result<XmlEvent> {
+ let next = if let Some(peeked) = self.peeked.take() {
+ peeked
+ } else {
+ self.inner_next()?
+ };
+ match next {
+ XmlEvent::StartElement { .. } => {
+ self.depth += 1;
+ }
+ XmlEvent::EndElement { .. } => {
+ self.depth -= 1;
+ }
+ _ => {}
+ }
+ debug!("Fetched {:?}", next);
+ Ok(next)
+ }
+
+ fn set_map_value(&mut self) {
+ self.is_map_value = true;
+ }
+
+ pub fn unset_map_value(&mut self) -> bool {
+ ::std::mem::replace(&mut self.is_map_value, false)
+ }
+
+ fn read_inner_value<V: de::Visitor<'de>, T, F: FnOnce(&mut Self) -> Result<T>>(
+ &mut self,
+ f: F,
+ ) -> Result<T> {
+ if self.unset_map_value() {
+ debug_expect!(self.next(), Ok(XmlEvent::StartElement { name, .. }) => {
+ let result = f(self)?;
+ self.expect_end_element(name)?;
+ Ok(result)
+ })
+ } else {
+ f(self)
+ }
+ }
+
+ fn expect_end_element(&mut self, start_name: OwnedName) -> Result<()> {
+ expect!(self.next()?, XmlEvent::EndElement { name, .. } => {
+ if name == start_name {
+ Ok(())
+ } else {
+ Err(Error::Custom { field: format!(
+ "End tag </{}> didn't match the start tag <{}>",
+ name.local_name,
+ start_name.local_name
+ ) })
+ }
+ })
+ }
+
+ fn prepare_parse_type<V: de::Visitor<'de>>(&mut self) -> Result<String> {
+ if let XmlEvent::StartElement { .. } = *self.peek()? {
+ self.set_map_value()
+ }
+ self.read_inner_value::<V, String, _>(|this| {
+ if let XmlEvent::EndElement { .. } = *this.peek()? {
+ return Err(Error::UnexpectedToken {
+ token: "EndElement".into(),
+ found: "Characters".into(),
+ });
+ }
+
+ expect!(this.next()?, XmlEvent::Characters(s) => {
+ return Ok(s)
+ })
+ })
+ }
+}
+
+macro_rules! deserialize_type {
+ ($deserialize:ident => $visit:ident) => {
+ fn $deserialize<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ let value = self.prepare_parse_type::<V>()?.parse()?;
+ visitor.$visit(value)
+ }
+ }
+}
+
+impl<'de, 'a, R: Read> de::Deserializer<'de> for &'a mut Deserializer<R> {
+ type Error = Error;
+
+ forward_to_deserialize_any! {
+ identifier
+ }
+
+ fn deserialize_struct<V: de::Visitor<'de>>(
+ self,
+ _name: &'static str,
+ fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value> {
+ self.unset_map_value();
+ expect!(self.next()?, XmlEvent::StartElement { name, attributes, .. } => {
+ let map_value = visitor.visit_map(MapAccess::new(
+ self,
+ attributes,
+ fields.contains(&"$value")
+ ))?;
+ self.expect_end_element(name)?;
+ Ok(map_value)
+ })
+ }
+
+ deserialize_type!(deserialize_i8 => visit_i8);
+ deserialize_type!(deserialize_i16 => visit_i16);
+ deserialize_type!(deserialize_i32 => visit_i32);
+ deserialize_type!(deserialize_i64 => visit_i64);
+ deserialize_type!(deserialize_u8 => visit_u8);
+ deserialize_type!(deserialize_u16 => visit_u16);
+ deserialize_type!(deserialize_u32 => visit_u32);
+ deserialize_type!(deserialize_u64 => visit_u64);
+ deserialize_type!(deserialize_f32 => visit_f32);
+ deserialize_type!(deserialize_f64 => visit_f64);
+
+ fn deserialize_bool<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ if let XmlEvent::StartElement { .. } = *self.peek()? {
+ self.set_map_value()
+ }
+ self.read_inner_value::<V, V::Value, _>(|this| {
+ if let XmlEvent::EndElement { .. } = *this.peek()? {
+ return visitor.visit_bool(false);
+ }
+ expect!(this.next()?, XmlEvent::Characters(s) => {
+ match s.as_str() {
+ "true" | "1" => visitor.visit_bool(true),
+ "false" | "0" => visitor.visit_bool(false),
+ _ => Err(de::Error::invalid_value(Unexpected::Str(&s), &"a boolean")),
+ }
+
+ })
+ })
+ }
+
+ fn deserialize_char<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ self.deserialize_string(visitor)
+ }
+
+ fn deserialize_str<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ self.deserialize_string(visitor)
+ }
+
+ fn deserialize_bytes<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ self.deserialize_string(visitor)
+ }
+
+ fn deserialize_byte_buf<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ self.deserialize_string(visitor)
+ }
+
+ fn deserialize_unit<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ if let XmlEvent::StartElement { .. } = *self.peek()? {
+ self.set_map_value()
+ }
+ self.read_inner_value::<V, V::Value, _>(
+ |this| expect!(this.peek()?, &XmlEvent::EndElement { .. } => visitor.visit_unit()),
+ )
+ }
+
+ fn deserialize_unit_struct<V: de::Visitor<'de>>(
+ self,
+ _name: &'static str,
+ visitor: V,
+ ) -> Result<V::Value> {
+ self.deserialize_unit(visitor)
+ }
+
+ fn deserialize_newtype_struct<V: de::Visitor<'de>>(
+ self,
+ _name: &'static str,
+ visitor: V,
+ ) -> Result<V::Value> {
+ visitor.visit_newtype_struct(self)
+ }
+
+ fn deserialize_tuple_struct<V: de::Visitor<'de>>(
+ self,
+ _name: &'static str,
+ len: usize,
+ visitor: V,
+ ) -> Result<V::Value> {
+ self.deserialize_tuple(len, visitor)
+ }
+
+ fn deserialize_tuple<V: de::Visitor<'de>>(self, len: usize, visitor: V) -> Result<V::Value> {
+ visitor.visit_seq(SeqAccess::new(self, Some(len)))
+ }
+
+ fn deserialize_enum<V: de::Visitor<'de>>(
+ self,
+ _name: &'static str,
+ _variants: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value> {
+ self.read_inner_value::<V, V::Value, _>(|this| visitor.visit_enum(EnumAccess::new(this)))
+ }
+
+ fn deserialize_string<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ if let XmlEvent::StartElement { .. } = *self.peek()? {
+ self.set_map_value()
+ }
+ self.read_inner_value::<V, V::Value, _>(|this| {
+ if let XmlEvent::EndElement { .. } = *this.peek()? {
+ return visitor.visit_str("");
+ }
+ expect!(this.next()?, XmlEvent::Characters(s) => {
+ visitor.visit_string(s)
+ })
+ })
+ }
+
+ fn deserialize_seq<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ visitor.visit_seq(SeqAccess::new(self, None))
+ }
+
+ fn deserialize_map<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ self.unset_map_value();
+ expect!(self.next()?, XmlEvent::StartElement { name, attributes, .. } => {
+ let map_value = visitor.visit_map(MapAccess::new(self, attributes, false))?;
+ self.expect_end_element(name)?;
+ Ok(map_value)
+ })
+ }
+
+ fn deserialize_option<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ match *self.peek()? {
+ XmlEvent::EndElement { .. } => visitor.visit_none(),
+ _ => visitor.visit_some(self),
+ }
+ }
+
+ fn deserialize_ignored_any<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ self.unset_map_value();
+ let depth = self.depth;
+ loop {
+ self.next()?;
+ if self.depth == depth {
+ break;
+ }
+ }
+ visitor.visit_unit()
+ }
+
+ fn deserialize_any<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
+ match *self.peek()? {
+ XmlEvent::StartElement { .. } => self.deserialize_map(visitor),
+ XmlEvent::EndElement { .. } => self.deserialize_unit(visitor),
+ _ => self.deserialize_string(visitor),
+ }
+ }
+}
diff --git a/src/de/seq.rs b/src/de/seq.rs
new file mode 100644
index 0000000..7e88d0c
--- /dev/null
+++ b/src/de/seq.rs
@@ -0,0 +1,70 @@
+use std::io::Read;
+
+use serde::de;
+use xml::reader::XmlEvent;
+
+use de::Deserializer;
+use error::{Error, Result};
+
+pub struct SeqAccess<'a, R: 'a + Read> {
+ de: &'a mut Deserializer<R>,
+ max_size: Option<usize>,
+ expected_name: Option<String>,
+}
+
+impl<'a, R: 'a + Read> SeqAccess<'a, R> {
+ pub fn new(de: &'a mut Deserializer<R>, max_size: Option<usize>) -> Self {
+ let expected_name = if de.unset_map_value() {
+ debug_expect!(de.peek(), Ok(&XmlEvent::StartElement { ref name, .. }) => {
+ Some(name.local_name.clone())
+ })
+ } else {
+ None
+ };
+ SeqAccess {
+ de: de,
+ max_size: max_size,
+ expected_name: expected_name,
+ }
+ }
+}
+
+impl<'de, 'a, R: 'a + Read> de::SeqAccess<'de> for SeqAccess<'a, R> {
+ type Error = Error;
+
+ fn next_element_seed<T: de::DeserializeSeed<'de>>(
+ &mut self,
+ seed: T,
+ ) -> Result<Option<T::Value>> {
+ match self.max_size.as_mut() {
+ Some(&mut 0) => {
+ return Ok(None);
+ },
+ Some(max_size) => {
+ *max_size -= 1;
+ },
+ None => {},
+ }
+ let more = match (self.de.peek()?, self.expected_name.as_ref()) {
+ (&XmlEvent::StartElement { ref name, .. }, Some(expected_name)) => {
+ &name.local_name == expected_name
+ },
+ (&XmlEvent::EndElement { .. }, None) |
+ (_, Some(_)) |
+ (&XmlEvent::EndDocument { .. }, _) => false,
+ (_, None) => true,
+ };
+ if more {
+ if self.expected_name.is_some() {
+ self.de.set_map_value();
+ }
+ seed.deserialize(&mut *self.de).map(Some)
+ } else {
+ Ok(None)
+ }
+ }
+
+ fn size_hint(&self) -> Option<usize> {
+ self.max_size
+ }
+}
diff --git a/src/de/var.rs b/src/de/var.rs
new file mode 100644
index 0000000..dbb400f
--- /dev/null
+++ b/src/de/var.rs
@@ -0,0 +1,84 @@
+use std::io::Read;
+
+use serde::de::{self, Deserializer as SerdeDeserializer, IntoDeserializer};
+use xml::name::OwnedName;
+use xml::reader::XmlEvent;
+
+use de::Deserializer;
+use error::{Error, Result};
+
+pub struct EnumAccess<'a, R: 'a + Read> {
+ de: &'a mut Deserializer<R>,
+}
+
+impl<'a, R: 'a + Read> EnumAccess<'a, R> {
+ pub fn new(de: &'a mut Deserializer<R>) -> Self {
+ EnumAccess { de: de }
+ }
+}
+
+impl<'de, 'a, R: 'a + Read> de::EnumAccess<'de> for EnumAccess<'a, R> {
+ type Error = Error;
+ type Variant = VariantAccess<'a, R>;
+
+ fn variant_seed<V: de::DeserializeSeed<'de>>(
+ self,
+ seed: V,
+ ) -> Result<(V::Value, VariantAccess<'a, R>)> {
+ let name = expect!(
+ self.de.peek()?,
+
+ &XmlEvent::Characters(ref name) |
+ &XmlEvent::StartElement { name: OwnedName { local_name: ref name, .. }, .. } => {
+ seed.deserialize(name.as_str().into_deserializer())
+ }
+ )?;
+ self.de.set_map_value();
+ Ok((name, VariantAccess::new(self.de)))
+ }
+}
+
+pub struct VariantAccess<'a, R: 'a + Read> {
+ de: &'a mut Deserializer<R>,
+}
+
+impl<'a, R: 'a + Read> VariantAccess<'a, R> {
+ pub fn new(de: &'a mut Deserializer<R>) -> Self {
+ VariantAccess { de: de }
+ }
+}
+
+impl<'de, 'a, R: 'a + Read> de::VariantAccess<'de> for VariantAccess<'a, R> {
+ type Error = Error;
+
+ fn unit_variant(self) -> Result<()> {
+ self.de.unset_map_value();
+ match self.de.next()? {
+ XmlEvent::StartElement {
+ name, attributes, ..
+ } => if attributes.is_empty() {
+ self.de.expect_end_element(name)
+ } else {
+ Err(de::Error::invalid_length(attributes.len(), &"0"))
+ },
+ XmlEvent::Characters(_) => Ok(()),
+ _ => unreachable!(),
+ }
+ }
+
+ fn newtype_variant_seed<T: de::DeserializeSeed<'de>>(self, seed: T) -> Result<T::Value> {
+ seed.deserialize(&mut *self.de)
+ }
+
+ fn tuple_variant<V: de::Visitor<'de>>(self, len: usize, visitor: V) -> Result<V::Value> {
+ self.de.deserialize_tuple(len, visitor)
+ }
+
+ fn struct_variant<V: de::Visitor<'de>>(
+ self,
+ _fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value> {
+ self.de.deserialize_map(visitor)
+ }
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..8d6e4ad
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,104 @@
+use serde::de::Error as DeError;
+use serde::ser::Error as SerError;
+use std::fmt::Display;
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+pub enum Error {
+ #[error("Expected token {token}, found {found}")]
+ UnexpectedToken { token: String, found: String },
+ #[error("custom: {field}")]
+ Custom { field: String },
+ #[error("unsupported operation: '{operation}'")]
+ UnsupportedOperation { operation: String },
+
+ #[error("IO error: {source}")]
+ Io {
+ #[from]
+ source: ::std::io::Error,
+ },
+
+ #[error("FromUtf8Error: {source}")]
+ FromUtf8Error {
+ #[from]
+ source: ::std::string::FromUtf8Error,
+ },
+
+ #[error("ParseIntError: {source}")]
+ ParseIntError {
+ #[from]
+ source: ::std::num::ParseIntError,
+ },
+
+ #[error("ParseFloatError: {source}")]
+ ParseFloatError {
+ #[from]
+ source: ::std::num::ParseFloatError,
+ },
+
+ #[error("ParseBoolError: {source}")]
+ ParseBoolError {
+ #[from]
+ source: ::std::str::ParseBoolError,
+ },
+
+ #[error("Syntax: {source}")]
+ Syntax {
+ #[from]
+ source: ::xml::reader::Error,
+ },
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+macro_rules! expect {
+ ($actual: expr, $($expected: pat)|+ => $if_ok: expr) => {
+ match $actual {
+ $($expected)|+ => $if_ok,
+ actual => Err($crate::Error::UnexpectedToken {
+ token: stringify!($($expected)|+).to_string(),
+ found: format!("{:?}",actual)
+ }) as Result<_>
+ }
+ }
+}
+
+#[cfg(debug_assertions)]
+macro_rules! debug_expect {
+ ($actual: expr, $($expected: pat)|+ => $if_ok: expr) => {
+ match $actual {
+ $($expected)|+ => $if_ok,
+ actual => panic!(
+ "Internal error: Expected token {}, found {:?}",
+ stringify!($($expected)|+),
+ actual
+ )
+ }
+ }
+}
+
+#[cfg(not(debug_assertions))]
+macro_rules! debug_expect {
+ ($actual: expr, $($expected: pat)|+ => $if_ok: expr) => {
+ match $actual {
+ $($expected)|+ => $if_ok,
+ _ => unreachable!()
+ }
+ }
+}
+
+impl DeError for Error {
+ fn custom<T: Display>(msg: T) -> Self {
+ Error::Custom {
+ field: msg.to_string(),
+ }
+ }
+}
+
+impl SerError for Error {
+ fn custom<T: Display>(msg: T) -> Self {
+ Error::Custom {
+ field: msg.to_string(),
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..1f93ed8
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,55 @@
+//!
+//!
+//! # Examples
+//!
+//! ```rust
+//! extern crate serde;
+//! extern crate serde_xml_rs;
+//!
+//! #[macro_use]
+//! extern crate serde_derive;
+//!
+//! use serde_xml_rs::{from_str, to_string};
+//!
+//! #[derive(Debug, Serialize, Deserialize, PartialEq)]
+//! struct Item {
+//! name: String,
+//! source: String,
+//! }
+//!
+//! fn main() {
+//! let src = r#"<Item><name>Banana</name><source>Store</source></Item>"#;
+//! let should_be = Item {
+//! name: "Banana".to_string(),
+//! source: "Store".to_string(),
+//! };
+//!
+//! let item: Item = from_str(src).unwrap();
+//! assert_eq!(item, should_be);
+//!
+//! let reserialized_item = to_string(&item).unwrap();
+//! assert_eq!(src, reserialized_item);
+//! }
+//! ```
+
+#[macro_use]
+extern crate log;
+#[macro_use]
+extern crate serde;
+extern crate xml;
+
+extern crate thiserror;
+
+#[cfg(test)]
+#[macro_use]
+extern crate serde_derive;
+
+#[macro_use]
+mod error;
+pub mod de;
+pub mod ser;
+
+pub use de::{from_reader, from_str, Deserializer};
+pub use error::Error;
+pub use ser::{to_string, to_writer, Serializer};
+pub use xml::reader::{EventReader, ParserConfig};
diff --git a/src/ser/mod.rs b/src/ser/mod.rs
new file mode 100644
index 0000000..bf97b1f
--- /dev/null
+++ b/src/ser/mod.rs
@@ -0,0 +1,419 @@
+use std::fmt::Display;
+use std::io::Write;
+
+use serde::ser::{self, Impossible, Serialize};
+
+use self::var::{Map, Struct};
+use error::{Error, Result};
+
+mod var;
+
+/// A convenience method for serializing some object to a buffer.
+///
+/// # Examples
+///
+/// ```rust
+/// # #[macro_use]
+/// # extern crate serde_derive;
+/// # extern crate serde;
+/// # extern crate serde_xml_rs;
+/// # use serde_xml_rs::to_writer;
+/// #[derive(Serialize)]
+/// struct Person {
+/// name: String,
+/// age: u32,
+/// }
+///
+/// # fn main() {
+/// let mut buffer = Vec::new();
+/// let joe = Person {name: "Joe".to_string(), age: 42};
+///
+/// to_writer(&mut buffer, &joe).unwrap();
+///
+/// let serialized = String::from_utf8(buffer).unwrap();
+/// println!("{}", serialized);
+/// # }
+/// ```
+pub fn to_writer<W: Write, S: Serialize>(writer: W, value: &S) -> Result<()> {
+ let mut ser = Serializer::new(writer);
+ value.serialize(&mut ser)
+}
+
+/// A convenience method for serializing some object to a string.
+///
+/// # Examples
+///
+/// ```rust
+/// # #[macro_use]
+/// # extern crate serde_derive;
+/// # extern crate serde;
+/// # extern crate serde_xml_rs;
+/// # use serde_xml_rs::to_string;
+/// #[derive(Serialize)]
+/// struct Person {
+/// name: String,
+/// age: u32,
+/// }
+///
+/// # fn main() {
+///
+/// let joe = Person {name: "Joe".to_string(), age: 42};
+/// let serialized = to_string(&joe).unwrap();
+/// println!("{}", serialized);
+/// # }
+/// ```
+pub fn to_string<S: Serialize>(value: &S) -> Result<String> {
+ // Create a buffer and serialize our nodes into it
+ let mut writer = Vec::with_capacity(128);
+ to_writer(&mut writer, value)?;
+
+ // We then check that the serialized string is the same as what we expect
+ let string = String::from_utf8(writer)?;
+ Ok(string)
+}
+
+/// An XML `Serializer`.
+pub struct Serializer<W>
+where
+ W: Write,
+{
+ writer: W,
+}
+
+impl<W> Serializer<W>
+where
+ W: Write,
+{
+ pub fn new(writer: W) -> Self {
+ Self { writer: writer }
+ }
+
+ fn write_primitive<P: Display>(&mut self, primitive: P) -> Result<()> {
+ write!(self.writer, "{}", primitive)?;
+ Ok(())
+ }
+
+ fn write_wrapped<S: Serialize>(&mut self, tag: &str, value: S) -> Result<()> {
+ write!(self.writer, "<{}>", tag)?;
+ value.serialize(&mut *self)?;
+ write!(self.writer, "</{}>", tag)?;
+ Ok(())
+ }
+}
+
+#[allow(unused_variables)]
+impl<'w, W> ser::Serializer for &'w mut Serializer<W>
+where
+ W: Write,
+{
+ type Ok = ();
+ type Error = Error;
+
+ type SerializeSeq = Impossible<Self::Ok, Self::Error>;
+ type SerializeTuple = Impossible<Self::Ok, Self::Error>;
+ type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
+ type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
+ type SerializeMap = Map<'w, W>;
+ type SerializeStruct = Struct<'w, W>;
+ type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
+
+ fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
+ if v {
+ write!(self.writer, "true")?;
+ } else {
+ write!(self.writer, "false")?;
+ }
+
+ Ok(())
+ }
+
+ fn serialize_i8(self, v: i8) -> Result<Self::Ok> {
+ self.write_primitive(v)
+ }
+
+ fn serialize_i16(self, v: i16) -> Result<Self::Ok> {
+ self.write_primitive(v)
+ }
+
+ fn serialize_i32(self, v: i32) -> Result<Self::Ok> {
+ self.write_primitive(v)
+ }
+
+ fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
+ self.write_primitive(v)
+ }
+
+ fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
+ self.write_primitive(v)
+ }
+
+ fn serialize_u16(self, v: u16) -> Result<Self::Ok> {
+ self.write_primitive(v)
+ }
+
+ fn serialize_u32(self, v: u32) -> Result<Self::Ok> {
+ self.write_primitive(v)
+ }
+
+ fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
+ self.write_primitive(v)
+ }
+
+ fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
+ self.write_primitive(v)
+ }
+
+ fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
+ self.write_primitive(v)
+ }
+
+ fn serialize_char(self, v: char) -> Result<Self::Ok> {
+ self.write_primitive(v)
+ }
+
+ fn serialize_str(self, value: &str) -> Result<Self::Ok> {
+ self.write_primitive(value)
+ }
+
+ fn serialize_bytes(self, value: &[u8]) -> Result<Self::Ok> {
+ // TODO: I imagine you'd want to use base64 here.
+ // Not sure how to roundtrip effectively though...
+ Err(Error::UnsupportedOperation {
+ operation: "serialize_bytes".to_string(),
+ })
+ }
+
+ fn serialize_none(self) -> Result<Self::Ok> {
+ Ok(())
+ }
+
+ fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok> {
+ value.serialize(self)
+ }
+
+ fn serialize_unit(self) -> Result<Self::Ok> {
+ self.serialize_none()
+ }
+
+ fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok> {
+ self.write_wrapped(name, ())
+ }
+
+ fn serialize_unit_variant(
+ self,
+ name: &'static str,
+ variant_index: u32,
+ variant: &'static str,
+ ) -> Result<Self::Ok> {
+ Err(Error::UnsupportedOperation {
+ operation: "serialize_unit_variant".to_string(),
+ })
+ }
+
+ fn serialize_newtype_struct<T: ?Sized + Serialize>(
+ self,
+ name: &'static str,
+ value: &T,
+ ) -> Result<Self::Ok> {
+ Err(Error::UnsupportedOperation {
+ operation: "serialize_newtype_struct".to_string(),
+ })
+ }
+
+ fn serialize_newtype_variant<T: ?Sized + Serialize>(
+ self,
+ name: &'static str,
+ variant_index: u32,
+ variant: &'static str,
+ value: &T,
+ ) -> Result<Self::Ok> {
+ self.write_wrapped(variant, value)
+ }
+
+ fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+ // TODO: Figure out how to constrain the things written to only be composites
+ Err(Error::UnsupportedOperation {
+ operation: "serialize_seq".to_string(),
+ })
+ }
+
+ fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+ Err(Error::UnsupportedOperation {
+ operation: "serialize_tuple".to_string(),
+ })
+ }
+
+ fn serialize_tuple_struct(
+ self,
+ name: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleStruct> {
+ Err(Error::UnsupportedOperation {
+ operation: "serialize_tuple_struct".to_string(),
+ })
+ }
+
+ fn serialize_tuple_variant(
+ self,
+ name: &'static str,
+ variant_index: u32,
+ variant: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleVariant> {
+ Err(Error::UnsupportedOperation {
+ operation: "serialize_tuple_variant".to_string(),
+ })
+ }
+
+ fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
+ Ok(Map::new(self))
+ }
+
+ fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+ write!(self.writer, "<{}>", name)?;
+ Ok(Struct::new(self, name))
+ }
+
+ fn serialize_struct_variant(
+ self,
+ name: &'static str,
+ variant_index: u32,
+ variant: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeStructVariant> {
+ Err(Error::UnsupportedOperation {
+ operation: "Result".to_string(),
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use serde::ser::{SerializeMap, SerializeStruct};
+ use serde::Serializer as SerSerializer;
+
+ #[test]
+ fn test_serialize_bool() {
+ let inputs = vec![(true, "true"), (false, "false")];
+
+ for (src, should_be) in inputs {
+ let mut buffer = Vec::new();
+
+ {
+ let mut ser = Serializer::new(&mut buffer);
+ ser.serialize_bool(src).unwrap();
+ }
+
+ let got = String::from_utf8(buffer).unwrap();
+ assert_eq!(got, should_be);
+ }
+ }
+
+ #[test]
+ fn test_start_serialize_struct() {
+ let mut buffer = Vec::new();
+
+ {
+ let mut ser = Serializer::new(&mut buffer);
+ let _ = ser.serialize_struct("foo", 0).unwrap();
+ }
+
+ let got = String::from_utf8(buffer).unwrap();
+ assert_eq!(got, "<foo>");
+ }
+
+ #[test]
+ fn test_serialize_struct_field() {
+ let mut buffer = Vec::new();
+
+ {
+ let mut ser = Serializer::new(&mut buffer);
+ let mut struct_ser = Struct::new(&mut ser, "baz");
+ struct_ser.serialize_field("foo", "bar").unwrap();
+ }
+
+ let got = String::from_utf8(buffer).unwrap();
+ assert_eq!(got, "<foo>bar</foo>");
+ }
+
+ #[test]
+ fn test_serialize_struct() {
+ #[derive(Serialize)]
+ struct Person {
+ name: String,
+ age: u32,
+ }
+
+ let bob = Person {
+ name: "Bob".to_string(),
+ age: 42,
+ };
+ let should_be = "<Person><name>Bob</name><age>42</age></Person>";
+ let mut buffer = Vec::new();
+
+ {
+ let mut ser = Serializer::new(&mut buffer);
+ bob.serialize(&mut ser).unwrap();
+ }
+
+ let got = String::from_utf8(buffer).unwrap();
+ assert_eq!(got, should_be);
+ }
+
+ #[test]
+ fn test_serialize_map_entries() {
+ let should_be = "<name>Bob</name><age>5</age>";
+ let mut buffer = Vec::new();
+
+ {
+ let mut ser = Serializer::new(&mut buffer);
+ let mut map = Map::new(&mut ser);
+ map.serialize_entry("name", "Bob").unwrap();
+ map.serialize_entry("age", "5").unwrap();
+ }
+
+ let got = String::from_utf8(buffer).unwrap();
+ assert_eq!(got, should_be);
+ }
+
+ #[test]
+ fn test_serialize_enum() {
+ #[derive(Serialize)]
+ #[allow(dead_code)]
+ enum Node {
+ Boolean(bool),
+ Number(f64),
+ String(String),
+ }
+
+ let mut buffer = Vec::new();
+ let should_be = "<Boolean>true</Boolean>";
+
+ {
+ let mut ser = Serializer::new(&mut buffer);
+ let node = Node::Boolean(true);
+ node.serialize(&mut ser).unwrap();
+ }
+
+ let got = String::from_utf8(buffer).unwrap();
+ assert_eq!(got, should_be);
+ }
+
+ #[test]
+ #[ignore]
+ fn serialize_a_list() {
+ let inputs = vec![1, 2, 3, 4];
+
+ let mut buffer = Vec::new();
+
+ {
+ let mut ser = Serializer::new(&mut buffer);
+ inputs.serialize(&mut ser).unwrap();
+ }
+
+ let got = String::from_utf8(buffer).unwrap();
+ println!("{}", got);
+ panic!();
+ }
+}
diff --git a/src/ser/var.rs b/src/ser/var.rs
new file mode 100644
index 0000000..bb61472
--- /dev/null
+++ b/src/ser/var.rs
@@ -0,0 +1,103 @@
+use std::io::Write;
+
+use serde::ser::{self, Serialize};
+
+use ser::Serializer;
+use error::{Error, Result};
+
+/// An implementation of `SerializeMap` for serializing to XML.
+pub struct Map<'w, W>
+where
+ W: 'w + Write,
+{
+ parent: &'w mut Serializer<W>,
+}
+
+impl<'w, W> Map<'w, W>
+where
+ W: 'w + Write,
+{
+ pub fn new(parent: &'w mut Serializer<W>) -> Map<'w, W> {
+ Map { parent }
+ }
+}
+
+impl<'w, W> ser::SerializeMap for Map<'w, W>
+where
+ W: 'w + Write,
+{
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_key<T: ?Sized + Serialize>(&mut self, _: &T) -> Result<()> {
+ panic!("impossible to serialize the key on its own, please use serialize_entry()")
+ }
+
+ fn serialize_value<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<()> {
+ value.serialize(&mut *self.parent)
+ }
+
+ fn end(self) -> Result<Self::Ok> {
+ Ok(())
+ }
+
+ fn serialize_entry<K: ?Sized + Serialize, V: ?Sized + Serialize>(
+ &mut self,
+ key: &K,
+ value: &V,
+ ) -> Result<()> {
+ // TODO: Is it possible to ensure our key is never a composite type?
+ // Anything which isn't a "primitive" would lead to malformed XML here...
+ write!(self.parent.writer, "<")?;
+ key.serialize(&mut *self.parent)?;
+ write!(self.parent.writer, ">")?;
+
+ value.serialize(&mut *self.parent)?;
+
+ write!(self.parent.writer, "</")?;
+ key.serialize(&mut *self.parent)?;
+ write!(self.parent.writer, ">")?;
+ Ok(())
+ }
+}
+
+/// An implementation of `SerializeStruct` for serializing to XML.
+pub struct Struct<'w, W>
+where
+ W: 'w + Write,
+{
+ parent: &'w mut Serializer<W>,
+ name: &'w str,
+}
+
+impl<'w, W> Struct<'w, W>
+where
+ W: 'w + Write,
+{
+ pub fn new(parent: &'w mut Serializer<W>, name: &'w str) -> Struct<'w, W> {
+ Struct { parent, name }
+ }
+}
+
+impl<'w, W> ser::SerializeStruct for Struct<'w, W>
+where
+ W: 'w + Write,
+{
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T: ?Sized + Serialize>(
+ &mut self,
+ key: &'static str,
+ value: &T,
+ ) -> Result<()> {
+ write!(self.parent.writer, "<{}>", key)?;
+ value.serialize(&mut *self.parent)?;
+ write!(self.parent.writer, "</{}>", key)?;
+ Ok(())
+ }
+
+ fn end(self) -> Result<Self::Ok> {
+ write!(self.parent.writer, "</{}>", self.name).map_err(|e| e.into())
+ }
+}
diff --git a/tests/failures.rs b/tests/failures.rs
new file mode 100644
index 0000000..ca51f6b
--- /dev/null
+++ b/tests/failures.rs
@@ -0,0 +1,52 @@
+#[macro_use]
+extern crate serde_derive;
+extern crate serde_xml_rs;
+
+#[macro_use]
+extern crate log;
+extern crate simple_logger;
+
+use serde_xml_rs::from_str;
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Item {
+ name: String,
+ source: String,
+}
+
+#[test]
+fn simple_struct_from_attributes_should_fail() {
+ let _ = simple_logger::init();
+
+ let s = r##"
+ <item name="hello" source="world.rs />
+ "##;
+
+ let item: Result<Item, _> = from_str(s);
+ match item {
+ Ok(_) => assert!(false),
+ Err(e) => {
+ info!("simple_struct_from_attributes_should_fail(): {}", e);
+ assert!(true)
+ }
+ }
+}
+
+#[test]
+fn multiple_roots_attributes_should_fail() {
+ let _ = simple_logger::init();
+
+ let s = r##"
+ <item name="hello" source="world.rs" />
+ <item name="hello source="world.rs" />
+ "##;
+
+ let item: Result<Vec<Item>, _> = from_str(s);
+ match item {
+ Ok(_) => assert!(false),
+ Err(e) => {
+ info!("multiple_roots_attributes_should_fail(): {}", e);
+ assert!(true)
+ }
+ }
+}
diff --git a/tests/migrated.rs b/tests/migrated.rs
new file mode 100644
index 0000000..87d3e6a
--- /dev/null
+++ b/tests/migrated.rs
@@ -0,0 +1,1086 @@
+#[macro_use]
+extern crate serde_derive;
+
+extern crate log;
+extern crate simple_logger;
+
+extern crate serde;
+extern crate serde_xml_rs;
+
+use std::fmt::Debug;
+
+use serde::{de, ser};
+use serde_xml_rs::{from_str, Error};
+
+#[derive(PartialEq, Debug, Serialize, Deserialize)]
+enum Animal {
+ Dog,
+ Frog(String),
+ Ant(Simple),
+ Cat { age: usize, name: String },
+}
+
+#[derive(PartialEq, Debug, Serialize, Deserialize)]
+struct Simple {
+ a: (),
+ b: usize,
+ c: String,
+ d: Option<String>,
+}
+
+#[derive(PartialEq, Debug, Serialize, Deserialize)]
+struct Inner {
+ a: (),
+ b: (usize, String, i8),
+ c: Vec<String>,
+}
+
+#[derive(PartialEq, Debug, Serialize, Deserialize)]
+struct Outer {
+ inner: Option<Inner>,
+}
+
+fn test_parse_ok<'de, 'a, T>(errors: &[(&'a str, T)])
+where
+ T: PartialEq + Debug + ser::Serialize + de::Deserialize<'de>,
+{
+ for &(s, ref value) in errors {
+ let v: T = from_str(s).unwrap();
+ assert_eq!(v, *value);
+
+ // // Make sure we can deserialize into an `Element`.
+ // let xml_value: Element = from_str(s).unwrap();
+
+ // // Make sure we can deserialize from an `Element`.
+ // let v: T = from_value(xml_value.clone()).unwrap();
+ // assert_eq!(v, *value);
+ }
+}
+
+fn test_parse_err<'de, 'a, T>(errors: &[&'a str])
+where
+ T: PartialEq + Debug + ser::Serialize + de::Deserialize<'de>,
+{
+ for &s in errors {
+ assert!(match from_str::<T>(s) {
+ Err(Error::Syntax { source: _ }) => true,
+ _ => false,
+ });
+ }
+}
+
+fn test_parse_invalid<'de, 'a, T>(errors: &[&'a str])
+where
+ T: PartialEq + Debug + ser::Serialize + de::Deserialize<'de>,
+{
+ for &s in errors {
+ assert!(match from_str::<T>(s) {
+ Err(_) => true,
+ _ => false,
+ });
+ }
+}
+
+#[test]
+fn test_namespaces() {
+ let _ = simple_logger::init();
+ #[derive(PartialEq, Serialize, Deserialize, Debug)]
+ struct Envelope {
+ subject: String,
+ }
+ let s = r#"
+ <?xml version="1.0" encoding="UTF-8"?>
+ <gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
+ <gesmes:subject>Reference rates</gesmes:subject>
+ </gesmes:Envelope>"#;
+ test_parse_ok(&[(
+ s,
+ Envelope {
+ subject: "Reference rates".to_string(),
+ },
+ )]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_doctype() {
+ let _ = simple_logger::init();
+ #[derive(PartialEq, Serialize, Deserialize, Debug)]
+ struct Envelope {
+ subject: String,
+ }
+
+ test_parse_ok(&[
+ (
+ r#"
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE Envelope>
+ <Envelope>
+ <subject>Reference rates</subject>
+ </Envelope>"#,
+ Envelope {
+ subject: "Reference rates".to_string(),
+ },
+ ),
+ (
+ r#"
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE Envelope[]>
+ <Envelope>
+ <subject>Reference rates</subject>
+ </Envelope>"#,
+ Envelope {
+ subject: "Reference rates".to_string(),
+ },
+ ),
+ (
+ r#"
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE Envelope [
+ <!ELEMENT subject (#PCDATA)>
+ ] >
+ <Envelope>
+ <subject>Reference rates</subject>
+ </Envelope>"#,
+ Envelope {
+ subject: "Reference rates".to_string(),
+ },
+ ),
+ ]);
+}
+
+#[test]
+fn test_doctype_fail() {
+ let _ = simple_logger::init();
+ #[derive(PartialEq, Serialize, Deserialize, Debug)]
+ struct Envelope {
+ subject: String,
+ }
+
+ test_parse_err::<Envelope>(&[
+ r#"
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE Envelope [
+ <!ELEMENT subject (#PCDATA)>
+ >
+ <Envelope>
+ <subject>Reference rates</subject>
+ </Envelope>"#,
+ r#"
+ <?xml version="1.0" encoding="UTF-8"?>
+ <Envelope>
+ <subject>Reference rates</subject>
+
+ <!DOCTYPE Envelope [
+ <!ELEMENT subject (#PCDATA)>
+ ]>
+
+ </Envelope>"#,
+ ])
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_forwarded_namespace() {
+ #[derive(PartialEq, Serialize, Deserialize, Debug)]
+ struct Graphml {
+ #[serde(rename = "xsi:schemaLocation")]
+ schema_location: String,
+ }
+ let s = r#"
+ <?xml version="1.0" encoding="UTF-8"?>
+ <graphml xmlns="http://graphml.graphdrawing.org/xmlns"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
+ http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
+
+
+ </graphml>"#;
+ test_parse_ok(&[(
+ s,
+ Graphml {
+ schema_location: "http://graphml.graphdrawing.org/xmlns
+ http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"
+ .to_string(),
+ },
+ )]);
+}
+
+#[test]
+fn test_parse_string() {
+ let _ = simple_logger::init();
+
+ test_parse_ok(&[
+ (
+ "<bla>This is a String</bla>",
+ "This is a String".to_string(),
+ ),
+ ("<bla></bla>", "".to_string()),
+ ("<bla> </bla>", "".to_string()),
+ ("<bla>&lt;boom/&gt;</bla>", "<boom/>".to_string()),
+ ("<bla>&#9835;</bla>", "♫".to_string()),
+ ("<bla>&#x266B;</bla>", "♫".to_string()),
+ (
+ "<bla>♫<![CDATA[<cookies/>]]>♫</bla>",
+ "♫<cookies/>♫".to_string(),
+ ),
+ ]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_parse_string_not_trim() {
+ let _ = simple_logger::init();
+
+ test_parse_ok(&[("<bla> </bla>", " ".to_string())]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_parse_enum() {
+ use self::Animal::*;
+ let _ = simple_logger::init();
+
+ test_parse_ok(&[
+ ("<Animal xsi:type=\"Dog\"/>", Dog),
+ (
+ "<Animal xsi:type=\"Frog\">Quak</Animal>",
+ Frog("Quak".to_string()),
+ ),
+ (
+ "<Animal xsi:type=\"Ant\"><a/><c>bla</c><b>15</b><d>Foo</d></Animal>",
+ Ant(Simple {
+ a: (),
+ b: 15,
+ c: "bla".to_string(),
+ d: Some("Foo".to_string()),
+ }),
+ ),
+ (
+ "<Animal xsi:type=\"Ant\"><a/><c>bla</c><b>15</b></Animal>",
+ Ant(Simple {
+ a: (),
+ b: 15,
+ c: "bla".to_string(),
+ d: None,
+ }),
+ ),
+ (
+ "<Animal xsi:type=\"Cat\"><age>42</age><name>Shere Khan</name></Animal>",
+ Cat {
+ age: 42,
+ name: "Shere Khan".to_string(),
+ },
+ ),
+ ]);
+
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ struct Helper {
+ x: Animal,
+ }
+
+ test_parse_ok(&[
+ ("<Helper><x xsi:type=\"Dog\"/></Helper>", Helper { x: Dog }),
+ (
+ "<Helper><x xsi:type=\"Frog\">Quak</Animal></Helper>",
+ Helper {
+ x: Frog("Quak".to_string()),
+ },
+ ),
+ (
+ "<Helper><x xsi:type=\"Cat\">
+ <age>42</age>
+ <name>Shere Khan</name>
+ </x></Helper>",
+ Helper {
+ x: Cat {
+ age: 42,
+ name: "Shere Khan".to_string(),
+ },
+ },
+ ),
+ ]);
+}
+
+#[test]
+fn test_parse_i64() {
+ let _ = simple_logger::init();
+ test_parse_ok(&[
+ ("<bla>0</bla>", 0),
+ ("<bla>-2</bla>", -2),
+ ("<bla>-1234</bla>", -1234),
+ ("<bla> -1234 </bla>", -1234),
+ ]);
+}
+
+#[test]
+fn test_parse_u64() {
+ let _ = simple_logger::init();
+ test_parse_ok(&[
+ ("<bla>0</bla>", 0),
+ ("<bla>1234</bla>", 1234),
+ ("<bla> 1234 </bla>", 1234),
+ ]);
+}
+
+#[test]
+fn test_parse_bool_element() {
+ let _ = simple_logger::init();
+ test_parse_ok(&[
+ ("<bla>true</bla>", true),
+ ("<bla>false</bla>", false),
+ ("<bla> true </bla>", true),
+ ("<bla> false </bla>", false),
+ ("<bla>1</bla>", true),
+ ("<bla>0</bla>", false),
+ ]);
+
+ test_parse_invalid::<bool>(&["<bla>verum</bla>"]);
+}
+
+#[test]
+fn test_parse_bool_attribute() {
+ #[derive(PartialEq, Debug, Deserialize, Serialize)]
+ struct Dummy {
+ foo: bool,
+ }
+
+ let _ = simple_logger::init();
+ test_parse_ok(&[
+ ("<bla foo=\"true\"/>", Dummy { foo: true }),
+ ("<bla foo=\"false\"/>", Dummy { foo: false }),
+ ("<bla foo=\"1\"/>", Dummy { foo: true }),
+ ("<bla foo=\"0\"/>", Dummy { foo: false }),
+ ]);
+
+ test_parse_invalid::<Dummy>(&[
+ "<bla foo=\"bar\"/>",
+ "<bla foo=\" true \"/>",
+ "<bla foo=\"10\"/>",
+ "<bla foo=\"\"/>",
+ "<bla/>",
+ ]);
+}
+
+#[test]
+fn test_parse_unit() {
+ let _ = simple_logger::init();
+ test_parse_ok(&[("<bla/>", ())]);
+}
+
+#[test]
+fn test_parse_f64() {
+ let _ = simple_logger::init();
+ test_parse_ok(&[
+ ("<bla>3.0</bla>", 3.0f64),
+ ("<bla>3.1</bla>", 3.1),
+ ("<bla>-1.2</bla>", -1.2),
+ ("<bla>0.4</bla>", 0.4),
+ ("<bla>0.4e5</bla>", 0.4e5),
+ ("<bla>0.4e15</bla>", 0.4e15),
+ ("<bla>0.4e-01</bla>", 0.4e-01), // precision troubles
+ ("<bla> 0.4e-01 </bla>", 0.4e-01),
+ ]);
+}
+
+#[test]
+fn test_parse_struct() {
+ let _ = simple_logger::init();
+
+ test_parse_ok(&[
+ (
+ "<Simple>
+ <c>abc</c>
+ <a/>
+ <b>2</b>
+ </Simple>",
+ Simple {
+ a: (),
+ b: 2,
+ c: "abc".to_string(),
+ d: None,
+ },
+ ),
+ (
+ "<Simple><!-- this is a comment -->
+ <c>abc</c>
+ <a/>
+ <b>2</b>
+ </Simple>",
+ Simple {
+ a: (),
+ b: 2,
+ c: "abc".to_string(),
+ d: None,
+ },
+ ),
+ (
+ "<Simple d=\"Foo\"><!-- this is a comment -->
+ <c>abc</c>
+ <a/>
+ <b>2</b>
+ </Simple>",
+ Simple {
+ a: (),
+ b: 2,
+ c: "abc".to_string(),
+ d: Some("Foo".to_string()),
+ },
+ ),
+ ]);
+}
+
+#[test]
+fn test_option() {
+ let _ = simple_logger::init();
+ test_parse_ok(&[
+ ("<a/>", Some("".to_string())),
+ ("<a></a>", Some("".to_string())),
+ ("<a> </a>", Some("".to_string())),
+ ("<a>42</a>", Some("42".to_string())),
+ ]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_option_not_trim() {
+ let _ = simple_logger::init();
+ test_parse_ok(&[("<a> </a>", Some(" ".to_string()))]);
+}
+
+#[test]
+fn test_amoskvin() {
+ let _ = simple_logger::init();
+ #[derive(Debug, Deserialize, PartialEq, Serialize)]
+ struct Root {
+ foo: Vec<Foo>,
+ }
+
+ #[derive(Debug, Deserialize, PartialEq, Serialize)]
+ struct Foo {
+ a: String,
+ b: Option<String>,
+ }
+ test_parse_ok(&[(
+ "
+<root>
+<foo>
+ <a>Hello</a>
+ <b>World</b>
+</foo>
+<foo>
+ <a>Hi</a>
+</foo>
+</root>",
+ Root {
+ foo: vec![
+ Foo {
+ a: "Hello".to_string(),
+ b: Some("World".to_string()),
+ },
+ Foo {
+ a: "Hi".to_string(),
+ b: None,
+ },
+ ],
+ },
+ )]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_nicolai86() {
+ let _ = simple_logger::init();
+ #[derive(Serialize, Deserialize, PartialEq, Debug)]
+ struct TheSender {
+ name: String,
+ }
+
+ #[derive(Serialize, Deserialize, PartialEq, Debug)]
+ struct CurrencyCube {
+ currency: String,
+ rate: String,
+ }
+
+ #[derive(Serialize, Deserialize, PartialEq, Debug)]
+ #[allow(non_snake_case)]
+ struct InnerCube {
+ Cube: Vec<CurrencyCube>,
+ }
+
+ #[derive(Serialize, Deserialize, PartialEq, Debug)]
+ #[allow(non_snake_case)]
+ struct OuterCube {
+ Cube: Vec<InnerCube>,
+ }
+
+ #[derive(Serialize, Deserialize, PartialEq, Debug)]
+ #[allow(non_snake_case)]
+ struct Envelope {
+ subject: String,
+ Sender: TheSender,
+ Cube: OuterCube,
+ }
+ test_parse_ok(&[
+ (
+ r#"
+ <?xml version="1.0" encoding="UTF-8"?>
+ <gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
+ <gesmes:subject>Reference rates</gesmes:subject>
+ <gesmes:Sender>
+ <gesmes:name>European Central Bank</gesmes:name>
+ </gesmes:Sender>
+ <Cube> </Cube>
+ </gesmes:Envelope>"#,
+ Envelope {
+ subject: "Reference rates".to_string(),
+ Sender: TheSender {
+ name: "European Central Bank".to_string(),
+ },
+ Cube: OuterCube { Cube: vec![] },
+ },
+ ),
+ (
+ r#"
+ <?xml version="1.0" encoding="UTF-8"?>
+ <gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
+ <gesmes:subject>Reference rates</gesmes:subject>
+ <gesmes:Sender>
+ <gesmes:name>European Central Bank</gesmes:name>
+ </gesmes:Sender>
+ <Cube><Cube>
+ <Cube currency='GBP' rate='0.81725'/>
+ <Cube currency='Latinum' rate='999999'/>
+ </Cube></Cube>
+ </gesmes:Envelope>"#,
+ Envelope {
+ subject: "Reference rates".to_string(),
+ Sender: TheSender {
+ name: "European Central Bank".to_string(),
+ },
+ Cube: OuterCube {
+ Cube: vec![InnerCube {
+ Cube: vec![
+ CurrencyCube {
+ currency: "GBP".to_string(),
+ rate: "0.81725".to_string(),
+ },
+ CurrencyCube {
+ currency: "Latinum".to_string(),
+ rate: "999999".to_string(),
+ },
+ ],
+ }],
+ },
+ },
+ ),
+ ]);
+}
+
+#[test]
+fn test_hugo_duncan2() {
+ let _ = simple_logger::init();
+ let s = r#"
+ <?xml version="1.0" encoding="UTF-8"?>
+ <DescribeVpcsResponse xmlns="http://ec2.amazonaws.com/doc/2014-10-01/">
+ <requestId>8d521e9a-509e-4ef6-bbb7-9f1ac0d49cd1</requestId>
+ <vpcSet>
+ <item>
+ <vpcId>vpc-ba0d18d8</vpcId>
+ <state>available</state>
+ </item>
+ </vpcSet>
+ </DescribeVpcsResponse>"#;
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ #[allow(non_snake_case)]
+ struct VpcSet {
+ vpcId: String,
+ state: String,
+ }
+
+ #[derive(PartialEq, Debug, Serialize)]
+ struct ItemVec<T>(Vec<T>);
+
+ impl<'de, T: de::Deserialize<'de>> de::Deserialize<'de> for ItemVec<T> {
+ fn deserialize<D>(deserializer: D) -> Result<ItemVec<T>, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ struct Helper<U> {
+ item: Vec<U>,
+ }
+ let h: Helper<_> = de::Deserialize::deserialize(deserializer)?;
+ Ok(ItemVec(h.item))
+ }
+ }
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ #[allow(non_snake_case)]
+ struct DescribeVpcsResponse {
+ requestId: String,
+ vpcSet: ItemVec<VpcSet>,
+ }
+ test_parse_ok(&[(
+ s,
+ DescribeVpcsResponse {
+ requestId: "8d521e9a-509e-4ef6-bbb7-9f1ac0d49cd1".to_string(),
+ vpcSet: ItemVec(vec![VpcSet {
+ vpcId: "vpc-ba0d18d8".to_string(),
+ state: "available".to_string(),
+ }]),
+ },
+ )]);
+}
+
+#[test]
+fn test_hugo_duncan() {
+ let _ = simple_logger::init();
+ let s = "
+ <?xml version=\"1.0\" encoding=\"UTF-8\"?>
+ <DescribeInstancesResponse xmlns=\"http://ec2.amazonaws.com/doc/2014-10-01/\">
+ <requestId>9474f558-10a5-42e8-84d1-f9ee181fe943</requestId>
+ <reservationSet/>
+ </DescribeInstancesResponse>
+ ";
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ #[allow(non_snake_case)]
+ struct DescribeInstancesResponse {
+ requestId: String,
+ reservationSet: (),
+ }
+ test_parse_ok(&[(
+ s,
+ DescribeInstancesResponse {
+ requestId: "9474f558-10a5-42e8-84d1-f9ee181fe943".to_string(),
+ reservationSet: (),
+ },
+ )]);
+}
+
+#[test]
+fn test_parse_xml_value() {
+ let _ = simple_logger::init();
+ #[derive(Eq, Debug, PartialEq, Deserialize, Serialize)]
+ struct Test {
+ #[serde(rename = "$value")]
+ myval: String,
+ }
+ test_parse_ok(&[(
+ "<Test>abc</Test>",
+ Test {
+ myval: "abc".to_string(),
+ },
+ )]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_parse_complexstruct() {
+ let _ = simple_logger::init();
+
+ test_parse_ok(&[
+ (
+ "<Outer>
+ <inner>
+ <b>2</b>
+ <b>boom</b>
+ <b>88</b>
+ </inner>
+ </Outer>",
+ Outer {
+ inner: Some(Inner {
+ a: (),
+ b: (2, "boom".to_string(), 88),
+ c: vec![],
+ }),
+ },
+ ),
+ (
+ "<Outer>
+ <inner>
+ <c>abc</c>
+ <c>xyz</c>
+ <a/>
+ <b>2</b>
+ <b>boom</b>
+ <b>88</b>
+ </inner>
+ </Outer>",
+ Outer {
+ inner: Some(Inner {
+ a: (),
+ b: (2, "boom".to_string(), 88),
+ c: vec!["abc".to_string(), "xyz".to_string()],
+ }),
+ },
+ ),
+ ("<Outer/>", Outer { inner: None }),
+ ]);
+}
+
+#[test]
+fn test_parse_attributes() {
+ let _ = simple_logger::init();
+
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ struct A {
+ a1: String,
+ #[serde(rename = "$value")]
+ a2: i32,
+ }
+
+ test_parse_ok(&[(
+ r#"<A a1="What is the answer to the ultimate question?">42</A>"#,
+ A {
+ a1: "What is the answer to the ultimate question?".to_string(),
+ a2: 42,
+ },
+ )]);
+
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ struct B {
+ b1: String,
+ b2: i32,
+ }
+
+ test_parse_ok(&[(
+ r#"<B b1="What is the answer to the ultimate question?" b2="42"/>"#,
+ B {
+ b1: "What is the answer to the ultimate question?".to_string(),
+ b2: 42,
+ },
+ )]);
+
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ struct C {
+ c1: B,
+ }
+
+ test_parse_ok(&[
+ (
+ r#"<C><c1 b1="What is the answer to the ultimate question?" b2="42"/></C>"#,
+ C {
+ c1: B {
+ b1: "What is the answer to the ultimate question?".to_string(),
+ b2: 42,
+ },
+ },
+ ),
+ (
+ r#"<C><c1 b1="What is the answer to the ultimate question?" b2="42"/> </C>"#,
+ C {
+ c1: B {
+ b1: "What is the answer to the ultimate question?".to_string(),
+ b2: 42,
+ },
+ },
+ ),
+ (
+ r#"<C> <c1 b1="What is the answer to the ultimate question?" b2="42">
+ </c1> </C>"#,
+ C {
+ c1: B {
+ b1: "What is the answer to the ultimate question?".to_string(),
+ b2: 42,
+ },
+ },
+ ),
+ ]);
+
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ struct D {
+ d1: Option<A>,
+ }
+ test_parse_ok(&[(
+ r#"<D><d1 a1="What is the answer to the ultimate question?">42</d1></D>"#,
+ D {
+ d1: Some(A {
+ a1: "What is the answer to the ultimate question?".to_string(),
+ a2: 42,
+ }),
+ },
+ )]);
+}
+
+#[test]
+#[ignore] // FIXME
+fn test_parse_hierarchies() {
+ let _ = simple_logger::init();
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ struct A {
+ a1: String,
+ a2: (String, String),
+ }
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ struct B {
+ b1: A,
+ b2: (A, A),
+ }
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ struct C {
+ c1: B,
+ c2: Vec<B>,
+ }
+
+ test_parse_ok(&[
+ (
+ "<C><c1>
+ <b1>
+ <a1>No</a1>
+ <a2>Maybe</a2>
+ <a2>Yes</a2>
+ </b1>
+ <b2>
+ <a1>Red</a1>
+ <a2>Green</a2>
+ <a2>Blue</a2>
+ </b2>
+ <b2>
+ <a1>London</a1>
+ <a2>Berlin</a2>
+ <a2>Paris</a2>
+ </b2>
+ </c1></C>",
+ C {
+ c1: B {
+ b1: A {
+ a1: "No".to_string(),
+ a2: ("Maybe".to_string(), "Yes".to_string()),
+ },
+ b2: (
+ A {
+ a1: "Red".to_string(),
+ a2: ("Green".to_string(), "Blue".to_string()),
+ },
+ A {
+ a1: "London".to_string(),
+ a2: ("Berlin".to_string(), "Paris".to_string()),
+ },
+ ),
+ },
+ c2: vec![],
+ },
+ ),
+ (
+ "<C><c1>
+ <b2>
+ <a2>Green</a2>
+ <a2>Blue</a2>
+ <a1>Red</a1>
+ </b2>
+ <b2>
+ <a2>Berlin</a2>
+ <a2>Paris</a2>
+ <a1>London</a1>
+ </b2>
+ <b1>
+ <a2>Maybe</a2>
+ <a2>Yes</a2>
+ <a1>No</a1>
+ </b1>
+ </c1></C>",
+ C {
+ c1: B {
+ b1: A {
+ a1: "No".to_string(),
+ a2: ("Maybe".to_string(), "Yes".to_string()),
+ },
+ b2: (
+ A {
+ a1: "Red".to_string(),
+ a2: ("Green".to_string(), "Blue".to_string()),
+ },
+ A {
+ a1: "London".to_string(),
+ a2: ("Berlin".to_string(), "Paris".to_string()),
+ },
+ ),
+ },
+ c2: vec![],
+ },
+ ),
+ ]);
+}
+
+#[test]
+fn unknown_field() {
+ #[derive(Deserialize, Debug, PartialEq, Eq, Serialize)]
+ struct A {
+ other: Vec<Other>,
+ }
+
+ #[derive(Deserialize, Debug, PartialEq, Eq, Serialize)]
+ struct Other {
+ d: i32,
+ }
+ test_parse_ok(&[(
+ "<a>
+ <b>
+ <c>5</c>
+ </b>
+ <other>
+ <d>6</d>
+ </other>
+ </a>",
+ A {
+ other: vec![Other { d: 6 }],
+ },
+ )]);
+}
+
+// #[test]
+// fn eoz() {
+// use std::io::Read;
+// let mut file = std::fs::File::open("Report_test.2.xml").unwrap();
+// let mut s = String::new();
+// file.read_to_string(&mut s).unwrap();
+
+// let _xml_value: Element = from_str(&s).unwrap();
+// }
+
+#[test]
+fn test_parse_unfinished() {
+ test_parse_err::<Simple>(&["<Simple>
+ <c>abc</c>
+ <a/>
+ <b>2</b>
+ <d/>"]);
+}
+
+#[test]
+fn test_things_qc_found() {
+ test_parse_err::<u32>(&["<\u{0}:/"]);
+}
+
+#[test]
+fn futile() {
+ let _ = simple_logger::init();
+ #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
+ struct Object {
+ id: u8,
+ name: String,
+ x: u8,
+ y: u8,
+ width: u8,
+ height: u8,
+ ellipse: Option<()>,
+ }
+
+ test_parse_ok(&[
+ (
+ r###"
+ <object id="11" name="testEllipse" x="102" y="38" width="21" height="14">
+ <ellipse/>
+ </object>
+ "###,
+ Object {
+ id: 11,
+ name: "testEllipse".to_owned(),
+ x: 102,
+ y: 38,
+ width: 21,
+ height: 14,
+ ellipse: Some(()),
+ },
+ ),
+ (
+ r###"
+ <object id="11" name="testEllipse" x="102" y="38" width="21" height="14">
+ </object>
+ "###,
+ Object {
+ id: 11,
+ name: "testEllipse".to_owned(),
+ x: 102,
+ y: 38,
+ width: 21,
+ height: 14,
+ ellipse: None,
+ },
+ ),
+ ]);
+}
+
+#[test]
+fn futile2() {
+ let _ = simple_logger::init();
+ #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
+ struct Null;
+
+ #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
+ struct Object {
+ field: Option<Null>,
+ };
+
+ #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
+ struct Stuff {
+ stuff_field: Option<Object>,
+ };
+
+ test_parse_ok(&[
+ (
+ r###"
+ <object>
+ <field/>
+ </object>
+ "###,
+ Object { field: Some(Null) },
+ ),
+ (
+ r###"
+ <object>
+ </object>
+ "###,
+ Object { field: None },
+ ),
+ ]);
+
+ test_parse_ok(&[
+ (
+ r###"
+ <object>
+ <stuff_field/>
+ </object>
+ "###,
+ Stuff {
+ stuff_field: Some(Object { field: None }),
+ },
+ ),
+ (
+ r###"
+ <object>
+ <stuff_field>
+ <field/>
+ </stuff_field>
+ </object>
+ "###,
+ Stuff {
+ stuff_field: Some(Object { field: Some(Null) }),
+ },
+ ),
+ (
+ r###"
+ <object>
+ </object>
+ "###,
+ Stuff { stuff_field: None },
+ ),
+ (
+ r###"
+ <object/>
+ "###,
+ Stuff { stuff_field: None },
+ ),
+ ]);
+}
+
+#[test]
+fn newtype_struct() {
+ #[derive(PartialEq, Debug, Serialize, Deserialize)]
+ struct Wrapper(String);
+
+ test_parse_ok(&[(
+ r###"<wrapper>Content</wrapper>"###,
+ Wrapper("Content".into()),
+ )]);
+}
diff --git a/tests/readme.rs b/tests/readme.rs
new file mode 100644
index 0000000..881d572
--- /dev/null
+++ b/tests/readme.rs
@@ -0,0 +1,6 @@
+extern crate docmatic;
+
+#[test]
+fn test_readme() {
+ docmatic::assert_file("README.md");
+}
diff --git a/tests/round_trip.rs b/tests/round_trip.rs
new file mode 100644
index 0000000..54b3b53
--- /dev/null
+++ b/tests/round_trip.rs
@@ -0,0 +1,110 @@
+#[macro_use]
+extern crate serde_derive;
+extern crate serde;
+extern crate serde_xml_rs;
+
+use serde::Deserialize;
+use serde_xml_rs::{from_str, to_string, EventReader, ParserConfig};
+
+#[derive(Debug, Serialize, Deserialize, PartialEq)]
+struct Item {
+ name: String,
+ source: String,
+}
+
+
+#[derive(Debug, Serialize, Deserialize, PartialEq)]
+enum Node {
+ Boolean(bool),
+ Identifier { value: String, index: u32 },
+ EOF,
+}
+
+#[derive(Debug, Serialize, Deserialize, PartialEq)]
+struct Nodes {
+ #[serde(rename = "$value")]
+ items: Vec<Node>,
+}
+
+
+#[test]
+fn basic_struct() {
+ let src = r#"<Item><name>Banana</name><source>Store</source></Item>"#;
+ let should_be = Item {
+ name: "Banana".to_string(),
+ source: "Store".to_string(),
+ };
+
+ let item: Item = from_str(src).unwrap();
+ assert_eq!(item, should_be);
+
+ let reserialized_item = to_string(&item).unwrap();
+ assert_eq!(src, reserialized_item);
+}
+
+
+#[test]
+#[ignore]
+fn round_trip_list_of_enums() {
+ // Construct some inputs
+ let nodes = Nodes {
+ items: vec![
+ Node::Boolean(true),
+ Node::Identifier {
+ value: "foo".to_string(),
+ index: 5,
+ },
+ Node::EOF,
+ ],
+ };
+
+ let should_be = r#"
+ <Nodes>
+ <Boolean>
+ true
+ </Boolean>
+ <Identifier>
+ <value>foo</value>
+ <index>5</index>
+ </Identifier>
+ <EOF />
+ </Nodes>"#;
+
+ let serialized_nodes = to_string(&nodes).unwrap();
+ assert_eq!(serialized_nodes, should_be);
+
+ // Then turn it back into a `Nodes` struct and make sure it's the same
+ // as the original
+ let deserialized_nodes: Nodes = from_str(serialized_nodes.as_str()).unwrap();
+ assert_eq!(deserialized_nodes, nodes);
+}
+
+#[test]
+fn whitespace_preserving_config() {
+ // Test a configuration which does not clip whitespace from tags
+
+ let src = r#"
+ <Item>
+ <name> space banana </name>
+ <source> fantasy costco </source>
+ </Item>"#;
+
+ let item_should_be = Item {
+ name: " space banana ".to_string(),
+ source: " fantasy costco ".to_string(),
+ };
+ let config = ParserConfig::new()
+ .trim_whitespace(false)
+ .whitespace_to_characters(false);
+ let mut deserializer =
+ serde_xml_rs::Deserializer::new(EventReader::new_with_config(src.as_bytes(), config));
+
+ let item = Item::deserialize(&mut deserializer).unwrap();
+ assert_eq!(item, item_should_be);
+
+ // Space outside values is not preserved.
+ let serialized_should_be =
+ "<Item><name> space banana </name><source> fantasy costco </source></Item>";
+ let reserialized_item = to_string(&item).unwrap();
+ assert_eq!(reserialized_item, serialized_should_be);
+}
diff --git a/tests/test.rs b/tests/test.rs
new file mode 100644
index 0000000..4b8ec86
--- /dev/null
+++ b/tests/test.rs
@@ -0,0 +1,161 @@
+#[macro_use]
+extern crate serde_derive;
+extern crate serde_xml_rs;
+
+extern crate log;
+extern crate simple_logger;
+
+use serde_xml_rs::from_str;
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Item {
+ name: String,
+ source: String,
+}
+
+#[test]
+fn simple_struct_from_attributes() {
+ let _ = simple_logger::init();
+
+ let s = r##"
+ <item name="hello" source="world.rs" />
+ "##;
+
+ let item: Item = from_str(s).unwrap();
+
+ assert_eq!(
+ item,
+ Item {
+ name: "hello".to_string(),
+ source: "world.rs".to_string(),
+ }
+ );
+}
+
+#[test]
+fn multiple_roots_attributes() {
+ let _ = simple_logger::init();
+
+ let s = r##"
+ <item name="hello" source="world.rs" />
+ <item name="hello" source="world.rs" />
+ "##;
+
+ let item: Vec<Item> = from_str(s).unwrap();
+
+ assert_eq!(
+ item,
+ vec![
+ Item {
+ name: "hello".to_string(),
+ source: "world.rs".to_string(),
+ },
+ Item {
+ name: "hello".to_string(),
+ source: "world.rs".to_string(),
+ },
+ ]
+ );
+}
+
+#[test]
+fn simple_struct_from_attribute_and_child() {
+ let _ = simple_logger::init();
+
+ let s = r##"
+ <item name="hello">
+ <source>world.rs</source>
+ </item>
+ "##;
+
+ let item: Item = from_str(s).unwrap();
+
+ assert_eq!(
+ item,
+ Item {
+ name: "hello".to_string(),
+ source: "world.rs".to_string(),
+ }
+ );
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Project {
+ name: String,
+
+ #[serde(rename = "item", default)]
+ items: Vec<Item>,
+}
+
+#[test]
+fn nested_collection() {
+ let _ = simple_logger::init();
+
+ let s = r##"
+ <project name="my_project">
+ <item name="hello1" source="world1.rs" />
+ <item name="hello2" source="world2.rs" />
+ </project>
+ "##;
+
+ let project: Project = from_str(s).unwrap();
+
+ assert_eq!(
+ project,
+ Project {
+ name: "my_project".to_string(),
+ items: vec![
+ Item {
+ name: "hello1".to_string(),
+ source: "world1.rs".to_string(),
+ },
+ Item {
+ name: "hello2".to_string(),
+ source: "world2.rs".to_string(),
+ },
+ ],
+ }
+ );
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+enum MyEnum {
+ A(String),
+ B { name: String, flag: bool },
+ C,
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct MyEnums {
+ #[serde(rename = "$value")]
+ items: Vec<MyEnum>,
+}
+
+#[test]
+fn collection_of_enums() {
+ let _ = simple_logger::init();
+
+ let s = r##"
+ <enums>
+ <A>test</A>
+ <B name="hello" flag="true" />
+ <C />
+ </enums>
+ "##;
+
+ let project: MyEnums = from_str(s).unwrap();
+
+ assert_eq!(
+ project,
+ MyEnums {
+ items: vec![
+ MyEnum::A("test".to_string()),
+ MyEnum::B {
+ name: "hello".to_string(),
+ flag: true,
+ },
+ MyEnum::C,
+ ],
+ }
+ );
+}