diff options
author | Jeff Vander Stoep <jeffv@google.com> | 2023-01-03 20:44:45 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-01-03 20:44:45 +0000 |
commit | 254a8667172991a2f7860d7db3deb8c72fb44b15 (patch) | |
tree | 20813e9df9e74173b1fbfc707995b22ad437b4f8 | |
parent | 4f0fb1052481fe5bf9b9f0627bb3e35dfdce261f (diff) | |
parent | 85c49e4c7a532a38f183a4bc86f759b186a0417f (diff) | |
download | serde-xml-rs-254a8667172991a2f7860d7db3deb8c72fb44b15.tar.gz |
Upgrade serde-xml-rs to 0.6.0 am: 85c49e4c7a
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/serde-xml-rs/+/2362481
Change-Id: Ice7236efbb9fe27f8f338ce5cef113cdc3e15b40
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | .cargo_vcs_info.json | 7 | ||||
-rw-r--r-- | Android.bp | 7 | ||||
-rw-r--r-- | Cargo.toml | 13 | ||||
-rw-r--r-- | Cargo.toml.orig | 7 | ||||
-rw-r--r-- | METADATA | 14 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | cargo2android.json | 4 | ||||
-rw-r--r-- | src/de/mod.rs | 22 | ||||
-rw-r--r-- | src/error.rs | 6 | ||||
-rw-r--r-- | src/lib.rs | 14 | ||||
-rw-r--r-- | src/ser/map.rs | 124 | ||||
-rw-r--r-- | src/ser/mod.rs | 412 | ||||
-rw-r--r-- | src/ser/plain.rs | 196 | ||||
-rw-r--r-- | src/ser/seq.rs | 37 | ||||
-rw-r--r-- | src/ser/tuple.rs | 91 | ||||
-rw-r--r-- | src/ser/var.rs | 103 | ||||
-rw-r--r-- | tests/common/mod.rs | 5 | ||||
-rw-r--r-- | tests/datatypes.rs | 149 | ||||
-rw-r--r-- | tests/failures.rs | 10 | ||||
-rw-r--r-- | tests/migrated.rs | 141 | ||||
-rw-r--r-- | tests/round_trip.rs | 21 | ||||
-rw-r--r-- | tests/test.rs | 9 |
22 files changed, 891 insertions, 504 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 61ebbbd..1c94bec 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "2a13dbd3f6ae77e67d7d329e7fce0b66f2d16d05" - } -} + "sha1": "920e54d059efe1dce4cef7f9728ef70e9d47b7b9" + }, + "path_in_vcs": "" +}
\ No newline at end of file @@ -20,11 +20,10 @@ license { rust_library { name: "libserde_xml_rs", - // has rustc warnings host_supported: true, crate_name: "serde_xml_rs", cargo_env_compat: true, - cargo_pkg_version: "0.5.1", + cargo_pkg_version: "0.6.0", srcs: ["src/lib.rs"], edition: "2018", rustlibs: [ @@ -41,11 +40,10 @@ rust_library { rust_test { name: "serde-xml-rs_test_src_lib", - // has rustc warnings host_supported: true, crate_name: "serde_xml_rs", cargo_env_compat: true, - cargo_pkg_version: "0.5.1", + cargo_pkg_version: "0.6.0", srcs: ["src/lib.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -59,5 +57,4 @@ rust_test { "libthiserror", "libxml_rust", ], - proc_macros: ["libserde_derive"], } @@ -12,11 +12,13 @@ [package] edition = "2018" name = "serde-xml-rs" -version = "0.5.1" +version = "0.6.0" authors = ["Ingvar Stepanyan <me@rreverser.com>"] description = "xml-rs based deserializer for Serde (compatible with 0.9+)" +readme = "README.md" license = "MIT" repository = "https://github.com/RReverser/serde-xml-rs" + [dependencies.log] version = "0.4" @@ -28,11 +30,16 @@ version = "1.0" [dependencies.xml-rs] version = "0.8" + [dev-dependencies.docmatic] version = "0.1" -[dev-dependencies.serde_derive] +[dev-dependencies.rstest] +version = "0.12" + +[dev-dependencies.serde] version = "1.0" +features = ["derive"] [dev-dependencies.simple_logger] -version = "1.0" +version = "2.1" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 09ea859..1a4a29d 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -4,7 +4,7 @@ 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.5.1" +version = "0.6.0" edition = "2018" [dependencies] @@ -14,6 +14,7 @@ xml-rs = "0.8" thiserror = "1.0" [dev-dependencies] -serde_derive = "1.0" -simple_logger = "1.0" +serde = { version = "1.0", features = ["derive"] } +simple_logger = "2.1" docmatic = "0.1" +rstest = "0.12" @@ -1,3 +1,7 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update rust/crates/serde-xml-rs +# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md + name: "serde-xml-rs" description: "xml-rs based deserializer for Serde (compatible with 0.9+)" third_party { @@ -7,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/serde-xml-rs/serde-xml-rs-0.5.1.crate" + value: "https://static.crates.io/crates/serde-xml-rs/serde-xml-rs-0.6.0.crate" } - version: "0.5.1" + version: "0.6.0" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 9 - day: 28 + year: 2022 + month: 12 + day: 19 } } @@ -7,8 +7,7 @@ ## Example usage ```rust -use serde; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use serde_xml_rs::{from_str, to_string}; #[derive(Debug, Serialize, Deserialize, PartialEq)] diff --git a/cargo2android.json b/cargo2android.json index d735201..3223434 100644 --- a/cargo2android.json +++ b/cargo2android.json @@ -5,15 +5,17 @@ ], "dependency-blocklist": [ "docmatic", + "rstest", "simple_logger" ], "device": true, "run": true, "test-blocklist": [ + "tests/datatypes.rs", "tests/failures.rs", "tests/migrated.rs", "tests/round_trip.rs", "tests/test.rs" ], "tests": true -}
\ No newline at end of file +} diff --git a/src/de/mod.rs b/src/de/mod.rs index 21240c5..486b38c 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,6 +1,6 @@ use std::{io::Read, marker::PhantomData}; -use log::debug; +use log::trace; use serde::de::{self, Unexpected}; use serde::forward_to_deserialize_any; use xml::name::OwnedName; @@ -21,10 +21,7 @@ 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::{Deserialize, Serialize}; /// # use serde_xml_rs::from_str; /// #[derive(Debug, Deserialize, PartialEq)] /// struct Item { @@ -44,10 +41,7 @@ pub fn from_str<'de, T: de::Deserialize<'de>>(s: &str) -> Result<T> { /// 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::Deserialize; /// # use serde_xml_rs::from_reader; /// #[derive(Debug, Deserialize, PartialEq)] /// struct Item { @@ -108,12 +102,8 @@ impl<'de, R: Read> RootDeserializer<R> { /// default. Enabling this option may incur additional memory usage. /// /// ```rust - /// # #[macro_use] - /// # extern crate serde_derive; - /// # extern crate serde; - /// # extern crate serde_xml_rs; - /// # use serde_xml_rs::from_reader; /// # use serde::Deserialize; + /// # use serde_xml_rs::from_reader; /// #[derive(Debug, Deserialize, PartialEq)] /// struct Foo { /// bar: Vec<usize>, @@ -164,7 +154,7 @@ impl<'de, R: Read, B: BufferedXmlReader<R>> Deserializer<R, B> { fn peek(&mut self) -> Result<&XmlEvent> { let peeked = self.buffered_reader.peek()?; - debug!("Peeked {:?}", peeked); + trace!("Peeked {:?}", peeked); Ok(peeked) } @@ -181,7 +171,7 @@ impl<'de, R: Read, B: BufferedXmlReader<R>> Deserializer<R, B> { } _ => {} } - debug!("Fetched {:?}", next); + trace!("Fetched {:?}", next); Ok(next) } diff --git a/src/error.rs b/src/error.rs index 5061b52..50a15c0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -47,6 +47,12 @@ pub enum Error { #[from] source: ::xml::reader::Error, }, + + #[error("Writer: {source}")] + Writer { + #[from] + source: ::xml::writer::Error, + }, } pub type Result<T> = std::result::Result<T, Error>; @@ -49,8 +49,7 @@ //! ## Basic example //! //! ```rust -//! use serde; -//! use serde_derive::{Deserialize, Serialize}; +//! use serde::{Deserialize, Serialize}; //! use serde_xml_rs::{from_str, to_string}; //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] @@ -60,7 +59,7 @@ //! } //! //! fn main() { -//! let src = r#"<Item><name>Banana</name><source>Store</source></Item>"#; +//! let src = r#"<?xml version="1.0" encoding="UTF-8"?><Item><name>Banana</name><source>Store</source></Item>"#; //! let should_be = Item { //! name: "Banana".to_string(), //! source: "Store".to_string(), @@ -77,8 +76,7 @@ //! ## Tag contents //! //! ```rust -//! # use serde; -//! # use serde_derive::{Deserialize, Serialize}; +//! # use serde::{Deserialize, Serialize}; //! # use serde_xml_rs::{from_str, to_string}; //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] @@ -102,8 +100,7 @@ //! ## Repeated tags //! //! ```rust -//! # use serde; -//! # use serde_derive::{Deserialize, Serialize}; +//! # use serde::{Deserialize, Serialize}; //! # use serde_xml_rs::{from_str, to_string}; //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] @@ -160,8 +157,7 @@ //! ## Custom EventReader //! //! ```rust -//! use serde::Deserialize; -//! use serde_derive::{Deserialize, Serialize}; +//! use serde::{Deserialize, Serialize}; //! use serde_xml_rs::{from_str, to_string, de::Deserializer}; //! use xml::reader::{EventReader, ParserConfig}; //! diff --git a/src/ser/map.rs b/src/ser/map.rs new file mode 100644 index 0000000..b0793e6 --- /dev/null +++ b/src/ser/map.rs @@ -0,0 +1,124 @@ +use super::{plain::to_plain_string, Serializer}; +use crate::error::{Error, Result}; +use log::debug; +use serde::ser::Serialize; +use std::io::Write; + +pub struct MapSerializer<'ser, W: 'ser + Write> { + ser: &'ser mut Serializer<W>, + must_close_tag: bool, +} + +impl<'ser, W: 'ser + Write> MapSerializer<'ser, W> { + pub fn new(ser: &'ser mut Serializer<W>, must_close_tag: bool) -> Self { + MapSerializer { + ser, + must_close_tag, + } + } +} + +impl<'ser, W: Write> serde::ser::SerializeMap for MapSerializer<'ser, W> { + type Ok = (); + type Error = Error; + + fn serialize_key<T>(&mut self, key: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + self.ser.open_tag(&to_plain_string(key)?)?; + Ok(()) + } + + fn serialize_value<T>(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + value.serialize(&mut *self.ser)?; + Ok(()) + } + + fn end(self) -> Result<()> { + if self.must_close_tag { + self.ser.end_tag()?; + } + Ok(()) + } +} + +pub struct StructSerializer<'ser, W: 'ser + Write> { + ser: &'ser mut Serializer<W>, + must_close_tag: bool, +} + +impl<'ser, W: 'ser + Write> StructSerializer<'ser, W> { + pub fn new(ser: &'ser mut Serializer<W>, must_close_tag: bool) -> Self { + StructSerializer { + ser, + must_close_tag, + } + } + + fn serialize_struct_field<T>(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + if key.starts_with("@") { + debug!("attribute {}", key); + self.ser.add_attr(&key[1..], to_plain_string(value)?) + } else if key == "$value" { + self.ser.build_start_tag()?; + debug!("body"); + value.serialize(&mut *self.ser)?; + Ok(()) + } else { + self.ser.build_start_tag()?; + self.ser.open_tag(key)?; + debug!("field {}", key); + value.serialize(&mut *self.ser)?; + debug!("end field"); + Ok(()) + } + } + + fn after_fields(self) -> Result<()> { + self.ser.build_start_tag()?; + self.ser.end_tag()?; + if self.must_close_tag { + self.ser.end_tag()?; + } + Ok(()) + } +} + +impl<'ser, W: 'ser + Write> serde::ser::SerializeStruct for StructSerializer<'ser, W> { + type Ok = (); + type Error = Error; + + fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + self.serialize_struct_field(key, value) + } + + fn end(self) -> Result<()> { + self.after_fields() + } +} + +impl<'ser, W: 'ser + Write> serde::ser::SerializeStructVariant for StructSerializer<'ser, W> { + type Ok = (); + type Error = Error; + + fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + self.serialize_struct_field(key, value) + } + + fn end(self) -> Result<()> { + self.after_fields() + } +} diff --git a/src/ser/mod.rs b/src/ser/mod.rs index a29ca6f..8130a8d 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1,22 +1,25 @@ -use std::fmt::Display; -use std::io::Write; - -use serde::ser::{self, Impossible, Serialize}; - -use self::var::{Map, Struct}; +mod map; +mod plain; +mod seq; +mod tuple; + +use self::{ + map::{MapSerializer, StructSerializer}, + seq::SeqSeralizer, + tuple::TupleSerializer, +}; use crate::error::{Error, Result}; - -mod var; +use log::debug; +use serde::ser::Serialize; +use std::{collections::HashMap, io::Write}; +use xml::writer::{EmitterConfig, EventWriter, XmlEvent}; /// 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::Serialize; /// # use serde_xml_rs::to_writer; /// #[derive(Serialize)] /// struct Person { @@ -44,10 +47,7 @@ pub fn to_writer<W: Write, S: Serialize>(writer: W, value: &S) -> Result<()> { /// # Examples /// /// ```rust -/// # #[macro_use] -/// # extern crate serde_derive; -/// # extern crate serde; -/// # extern crate serde_xml_rs; +/// # use serde::Serialize; /// # use serde_xml_rs::to_string; /// #[derive(Serialize)] /// struct Person { @@ -77,266 +77,329 @@ pub struct Serializer<W> where W: Write, { - writer: W, + writer: EventWriter<W>, + root: bool, + current_tag: String, + current_tag_attrs: Option<HashMap<&'static str, String>>, } impl<W> Serializer<W> where W: Write, { + fn new_from_writer(writer: EventWriter<W>) -> Self { + Self { + writer, + root: true, + current_tag: "".into(), + current_tag_attrs: None, + } + } + pub fn new(writer: W) -> Self { - Self { writer: writer } + Self::new_from_writer(EmitterConfig::new().create_writer(writer)) } - fn write_primitive<P: Display>(&mut self, primitive: P) -> Result<()> { - write!(self.writer, "{}", primitive)?; + fn next(&mut self, event: XmlEvent) -> Result<()> { + self.writer.write(event)?; 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)?; + fn characters(&mut self, s: &str) -> Result<()> { + self.next(XmlEvent::characters(s)) + } + + fn start_document(&mut self) -> Result<()> { + self.next(XmlEvent::StartDocument { + encoding: Default::default(), + standalone: Default::default(), + version: xml::common::XmlVersion::Version10, + }) + } + + fn open_root_tag(&mut self, name: &'static str) -> Result<()> { + if self.root { + self.root = false; + self.start_document()?; + self.open_tag(name)?; + } + Ok(()) + } + + fn open_tag(&mut self, tag_name: &str) -> Result<()> { + self.current_tag = tag_name.into(); + self.current_tag_attrs = Some(HashMap::new()); + Ok(()) + } + + fn reopen_tag(&mut self) -> Result<()> { + self.current_tag_attrs = Some(HashMap::new()); Ok(()) } + + fn abandon_tag(&mut self) -> Result<()> { + self.current_tag = "".into(); + self.current_tag_attrs = None; + Ok(()) + } + + fn add_attr(&mut self, name: &'static str, value: String) -> Result<()> { + self.current_tag_attrs + .as_mut() + .ok_or(Error::Custom { + field: format!("Cannot add attribute {}", name), + }) + .map(|attrs| { + attrs.insert(name, value); + }) + } + + fn build_start_tag(&mut self) -> Result<bool> { + if let Some(attrs) = self.current_tag_attrs.take() { + self.start_tag(&self.current_tag(), attrs)?; + Ok(true) + } else { + Ok(false) + } + } + + fn start_tag(&mut self, tag_name: &str, attrs: HashMap<&str, String>) -> Result<()> { + let element = attrs + .iter() + .fold(XmlEvent::start_element(tag_name), |b, (&name, value)| { + b.attr(name, value) + }); + + self.next(element.into()) + } + + fn end_tag(&mut self) -> Result<()> { + self.next(XmlEvent::end_element().into()) + } + + fn current_tag(&self) -> String { + self.current_tag.clone() + } } -#[allow(unused_variables)] -impl<'w, W> ser::Serializer for &'w mut Serializer<W> -where - W: Write, -{ +impl<'ser, W: Write> serde::ser::Serializer for &'ser mut Serializer<W> { 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>; + type SerializeSeq = SeqSeralizer<'ser, W>; + type SerializeTuple = TupleSerializer<'ser, W>; + type SerializeTupleStruct = TupleSerializer<'ser, W>; + type SerializeTupleVariant = TupleSerializer<'ser, W>; + type SerializeMap = MapSerializer<'ser, W>; + type SerializeStruct = StructSerializer<'ser, W>; + type SerializeStructVariant = StructSerializer<'ser, W>; fn serialize_bool(self, v: bool) -> Result<Self::Ok> { - if v { - write!(self.writer, "true")?; - } else { - write!(self.writer, "false")?; - } - - Ok(()) + self.serialize_str(&v.to_string()) } fn serialize_i8(self, v: i8) -> Result<Self::Ok> { - self.write_primitive(v) + self.serialize_i64(i64::from(v)) } fn serialize_i16(self, v: i16) -> Result<Self::Ok> { - self.write_primitive(v) + self.serialize_i64(i64::from(v)) } fn serialize_i32(self, v: i32) -> Result<Self::Ok> { - self.write_primitive(v) + self.serialize_i64(i64::from(v)) } fn serialize_i64(self, v: i64) -> Result<Self::Ok> { - self.write_primitive(v) + self.serialize_str(&v.to_string()) } fn serialize_u8(self, v: u8) -> Result<Self::Ok> { - self.write_primitive(v) + self.serialize_u64(u64::from(v)) } fn serialize_u16(self, v: u16) -> Result<Self::Ok> { - self.write_primitive(v) + self.serialize_u64(u64::from(v)) } fn serialize_u32(self, v: u32) -> Result<Self::Ok> { - self.write_primitive(v) + self.serialize_u64(u64::from(v)) } fn serialize_u64(self, v: u64) -> Result<Self::Ok> { - self.write_primitive(v) + let must_close_tag = self.build_start_tag()?; + self.characters(&v.to_string())?; + if must_close_tag { + self.end_tag()?; + } + Ok(()) } fn serialize_f32(self, v: f32) -> Result<Self::Ok> { - self.write_primitive(v) + self.serialize_f64(f64::from(v)) } fn serialize_f64(self, v: f64) -> Result<Self::Ok> { - self.write_primitive(v) + self.serialize_str(&v.to_string()) } fn serialize_char(self, v: char) -> Result<Self::Ok> { - self.write_primitive(v) + self.serialize_str(&v.to_string()) } - fn serialize_str(self, value: &str) -> Result<Self::Ok> { - self.write_primitive(value) + fn serialize_str(self, v: &str) -> Result<Self::Ok> { + let must_close_tag = self.build_start_tag()?; + self.characters(v)?; + if must_close_tag { + self.end_tag()?; + } + Ok(()) } - 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_bytes(self, _v: &[u8]) -> Result<Self::Ok> { + unimplemented!() } fn serialize_none(self) -> Result<Self::Ok> { + debug!("None"); + let must_close_tag = self.build_start_tag()?; + if must_close_tag { + self.end_tag()?; + } Ok(()) } - fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok> { + fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok> + where + T: Serialize, + { + debug!("Some"); value.serialize(self) } fn serialize_unit(self) -> Result<Self::Ok> { - self.serialize_none() + debug!("Unit"); + let must_close_tag = self.build_start_tag()?; + if must_close_tag { + self.end_tag()?; + } + Ok(()) } fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok> { - self.write_wrapped(name, ()) + debug!("Unit struct {}", name); + self.serialize_unit() } fn serialize_unit_variant( self, name: &'static str, - variant_index: u32, + _variant_index: u32, variant: &'static str, ) -> Result<Self::Ok> { - Err(Error::UnsupportedOperation { - operation: "serialize_unit_variant".to_string(), - }) + debug!("Unit variant {}::{}", name, variant); + self.start_tag(variant, HashMap::new())?; + self.serialize_unit()?; + self.end_tag()?; + Ok(()) } - 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_struct<T: ?Sized>(self, name: &'static str, value: &T) -> Result<Self::Ok> + where + T: Serialize, + { + debug!("Newtype struct {}", name); + value.serialize(self) } - fn serialize_newtype_variant<T: ?Sized + Serialize>( + fn serialize_newtype_variant<T: ?Sized>( self, name: &'static str, - variant_index: u32, + _variant_index: u32, variant: &'static str, value: &T, - ) -> Result<Self::Ok> { - self.write_wrapped(variant, value) + ) -> Result<Self::Ok> + where + T: Serialize, + { + let must_close_tag = self.build_start_tag()?; + + debug!("Newtype variant {}::{}", name, variant); + self.open_tag(variant)?; + value.serialize(&mut *self)?; + + if must_close_tag { + self.end_tag()?; + } + Ok(()) } - 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_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> { + debug!("Sequence"); + Ok(SeqSeralizer::new(self)) } - fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> { - Err(Error::UnsupportedOperation { - operation: "serialize_tuple".to_string(), - }) + fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> { + debug!("Tuple"); + let must_close_tag = self.build_start_tag()?; + Ok(TupleSerializer::new(self, must_close_tag)) } fn serialize_tuple_struct( self, name: &'static str, - len: usize, + _len: usize, ) -> Result<Self::SerializeTupleStruct> { - Err(Error::UnsupportedOperation { - operation: "serialize_tuple_struct".to_string(), - }) + debug!("Tuple struct {}", name); + let must_close_tag = self.build_start_tag()?; + Ok(TupleSerializer::new(self, must_close_tag)) } fn serialize_tuple_variant( self, name: &'static str, - variant_index: u32, + _variant_index: u32, variant: &'static str, - len: usize, + _len: usize, ) -> Result<Self::SerializeTupleVariant> { - Err(Error::UnsupportedOperation { - operation: "serialize_tuple_variant".to_string(), - }) + debug!("Tuple variant {}::{}", name, variant); + let must_close_tag = self.build_start_tag()?; + self.start_tag(variant, HashMap::new())?; + Ok(TupleSerializer::new(self, must_close_tag)) } - fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> { - Ok(Map::new(self)) + fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> { + let must_close_tag = self.build_start_tag()?; + Ok(MapSerializer::new(self, must_close_tag)) } - fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> { - write!(self.writer, "<{}>", name)?; - Ok(Struct::new(self, name)) + fn serialize_struct(self, name: &'static str, _len: usize) -> Result<Self::SerializeStruct> { + self.open_root_tag(name)?; + + debug!("Struct {}", name); + Ok(StructSerializer::new(self, false)) } fn serialize_struct_variant( self, name: &'static str, - variant_index: u32, + _variant_index: u32, variant: &'static str, - len: usize, + _len: usize, ) -> Result<Self::SerializeStructVariant> { - Err(Error::UnsupportedOperation { - operation: "Result".to_string(), - }) + self.open_root_tag(name)?; + + debug!("Struct variant {}", variant); + let must_close_tag = self.build_start_tag()?; + self.open_tag(variant)?; + Ok(StructSerializer::new(self, must_close_tag)) } } #[cfg(test)] mod tests { use super::*; - use serde::ser::{SerializeMap, SerializeStruct}; - use serde::Serializer as SerSerializer; - use serde_derive::Serialize; - - #[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>"); - } + use serde::Serialize; #[test] fn test_serialize_struct() { @@ -350,7 +413,7 @@ mod tests { name: "Bob".to_string(), age: 42, }; - let should_be = "<Person><name>Bob</name><age>42</age></Person>"; + let should_be = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Person><name>Bob</name><age>42</age></Person>"; let mut buffer = Vec::new(); { @@ -363,22 +426,6 @@ mod tests { } #[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)] @@ -389,7 +436,7 @@ mod tests { } let mut buffer = Vec::new(); - let should_be = "<Boolean>true</Boolean>"; + let should_be = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Boolean>true</Boolean>"; { let mut ser = Serializer::new(&mut buffer); @@ -400,21 +447,4 @@ mod tests { 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/plain.rs b/src/ser/plain.rs new file mode 100644 index 0000000..49e2964 --- /dev/null +++ b/src/ser/plain.rs @@ -0,0 +1,196 @@ +use std::io::Write; + +use serde::ser::{Impossible, Serialize}; + +use crate::error::{Error, Result}; + +pub fn to_plain_string<T>(value: &T) -> Result<String> +where + T: ?Sized + Serialize, +{ + let mut writer = Vec::with_capacity(128); + value.serialize(&mut PlainStringSerializer::new(&mut writer))?; + + let string = String::from_utf8(writer)?; + Ok(string) +} + +struct PlainStringSerializer<W: Write> { + writer: W, +} + +impl<W: Write> PlainStringSerializer<W> { + fn new(writer: W) -> Self { + PlainStringSerializer { writer } + } + + fn characters(&mut self, s: &str) -> Result<()> { + write!(self.writer, "{}", s)?; + Ok(()) + } +} + +impl<'ser, W: 'ser + Write> serde::ser::Serializer for &'ser mut PlainStringSerializer<W> { + 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 = Impossible<Self::Ok, Self::Error>; + type SerializeStruct = Impossible<Self::Ok, Self::Error>; + type SerializeStructVariant = Impossible<Self::Ok, Self::Error>; + + fn serialize_bool(self, v: bool) -> Result<Self::Ok> { + self.characters(&v.to_string()) + } + + fn serialize_i8(self, v: i8) -> Result<Self::Ok> { + self.serialize_i64(i64::from(v)) + } + + fn serialize_i16(self, v: i16) -> Result<Self::Ok> { + self.serialize_i64(i64::from(v)) + } + + fn serialize_i32(self, v: i32) -> Result<Self::Ok> { + self.serialize_i64(i64::from(v)) + } + + fn serialize_i64(self, v: i64) -> Result<Self::Ok> { + self.characters(&v.to_string()) + } + + fn serialize_u8(self, v: u8) -> Result<Self::Ok> { + self.serialize_u64(u64::from(v)) + } + + fn serialize_u16(self, v: u16) -> Result<Self::Ok> { + self.serialize_u64(u64::from(v)) + } + + fn serialize_u32(self, v: u32) -> Result<Self::Ok> { + self.serialize_u64(u64::from(v)) + } + + fn serialize_u64(self, v: u64) -> Result<Self::Ok> { + self.characters(&v.to_string()) + } + + fn serialize_f32(self, v: f32) -> Result<Self::Ok> { + self.serialize_f64(f64::from(v)) + } + + fn serialize_f64(self, v: f64) -> Result<Self::Ok> { + self.characters(&v.to_string()) + } + + fn serialize_char(self, v: char) -> Result<Self::Ok> { + self.characters(&v.to_string()) + } + + fn serialize_str(self, v: &str) -> Result<Self::Ok> { + self.characters(v) + } + + fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok> { + unimplemented!() + } + + fn serialize_none(self) -> Result<Self::Ok> { + unimplemented!() + } + + fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<Self::Ok> + where + T: Serialize, + { + unimplemented!() + } + + fn serialize_unit(self) -> Result<Self::Ok> { + unimplemented!() + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> { + unimplemented!() + } + + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + ) -> Result<Self::Ok> { + unimplemented!() + } + + fn serialize_newtype_struct<T: ?Sized>( + self, + _name: &'static str, + _value: &T, + ) -> Result<Self::Ok> + where + T: Serialize, + { + unimplemented!() + } + + fn serialize_newtype_variant<T: ?Sized>( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result<Self::Ok> + where + T: Serialize, + { + unimplemented!() + } + + fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> { + unimplemented!() + } + + fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> { + unimplemented!() + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result<Self::SerializeTupleStruct> { + unimplemented!() + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result<Self::SerializeTupleVariant> { + unimplemented!() + } + + fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> { + unimplemented!() + } + + fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> { + unimplemented!() + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result<Self::SerializeStructVariant> { + unimplemented!() + } +} diff --git a/src/ser/seq.rs b/src/ser/seq.rs new file mode 100644 index 0000000..1681534 --- /dev/null +++ b/src/ser/seq.rs @@ -0,0 +1,37 @@ +use super::Serializer; +use crate::error::{Error, Result}; +use serde::ser::Serialize; +use std::io::Write; + +pub struct SeqSeralizer<'ser, W: 'ser + Write> { + ser: &'ser mut Serializer<W>, +} + +impl<'ser, W: 'ser + Write> SeqSeralizer<'ser, W> { + pub fn new(ser: &'ser mut Serializer<W>) -> Self { + SeqSeralizer { ser } + } +} + +impl<'ser, W: 'ser + Write> serde::ser::SerializeSeq for SeqSeralizer<'ser, W> { + type Ok = (); + type Error = Error; + + fn serialize_element<T>(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + let must_close_tag = self.ser.build_start_tag()?; + value.serialize(&mut *self.ser)?; + if must_close_tag { + self.ser.end_tag()?; + self.ser.reopen_tag()?; + } + Ok(()) + } + + fn end(self) -> Result<()> { + self.ser.abandon_tag()?; + Ok(()) + } +} diff --git a/src/ser/tuple.rs b/src/ser/tuple.rs new file mode 100644 index 0000000..3d893f8 --- /dev/null +++ b/src/ser/tuple.rs @@ -0,0 +1,91 @@ +use std::io::Write; + +use serde::ser::Serialize; + +use super::Serializer; +use crate::error::{Error, Result}; + +pub struct TupleSerializer<'ser, W: 'ser + Write> { + ser: &'ser mut Serializer<W>, + must_close_tag: bool, + first: bool, +} + +impl<'ser, W: 'ser + Write> TupleSerializer<'ser, W> { + pub fn new(ser: &'ser mut Serializer<W>, must_close_tag: bool) -> Self { + Self { + ser, + must_close_tag, + first: true, + } + } + + fn serialize_item<T>(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + if self.first { + self.first = false; + } else { + self.ser.characters(" ")?; + } + value.serialize(&mut *self.ser)?; + Ok(()) + } + + fn after_items(self) -> Result<()> { + if self.must_close_tag { + self.ser.end_tag()?; + } + Ok(()) + } +} + +impl<'ser, W: 'ser + Write> serde::ser::SerializeTupleVariant for TupleSerializer<'ser, W> { + type Ok = (); + type Error = Error; + + fn serialize_field<T>(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + self.serialize_item(value) + } + + fn end(self) -> Result<()> { + self.ser.end_tag()?; + self.after_items() + } +} + +impl<'ser, W: 'ser + Write> serde::ser::SerializeTupleStruct for TupleSerializer<'ser, W> { + type Ok = (); + type Error = Error; + + fn serialize_field<T>(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + self.serialize_item(value) + } + + fn end(self) -> Result<()> { + self.after_items() + } +} + +impl<'ser, W: 'ser + Write> serde::ser::SerializeTuple for TupleSerializer<'ser, W> { + type Ok = (); + type Error = Error; + + fn serialize_element<T>(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + self.serialize_item(value) + } + + fn end(self) -> Result<()> { + self.after_items() + } +} diff --git a/src/ser/var.rs b/src/ser/var.rs deleted file mode 100644 index e4ee0f0..0000000 --- a/src/ser/var.rs +++ /dev/null @@ -1,103 +0,0 @@ -use std::io::Write; - -use serde::ser::{self, Serialize}; - -use crate::error::{Error, Result}; -use crate::ser::Serializer; - -/// An implementation of `SerializeMap` for serializing to XML. -pub struct Map<'w, W> -where - 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: 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/common/mod.rs b/tests/common/mod.rs new file mode 100644 index 0000000..26a82e4 --- /dev/null +++ b/tests/common/mod.rs @@ -0,0 +1,5 @@ +use simple_logger::SimpleLogger; + +pub fn init_logger() { + let _ = SimpleLogger::new().with_utc_timestamps().init(); +} diff --git a/tests/datatypes.rs b/tests/datatypes.rs new file mode 100644 index 0000000..60410c4 --- /dev/null +++ b/tests/datatypes.rs @@ -0,0 +1,149 @@ +pub use rstest::{fixture, rstest}; +use simple_logger::SimpleLogger; +use std::fmt::Debug; + +#[fixture] +fn logger() { + let _ = SimpleLogger::new().init(); +} + +mod de { + use super::*; + use serde::Deserialize; + use serde_xml_rs::from_str; + + #[rstest] + #[case::string("<bla>This is a String</bla>", "This is a String".to_string())] + #[case::string("<bla></bla>", "".to_string())] + #[case::string("<bla> </bla>", "".to_string())] + #[case::string("<bla><boom/></bla>", "<boom/>".to_string())] + #[case::string("<bla>♫</bla>", "♫".to_string())] + #[case::string("<bla>♫</bla>", "♫".to_string())] + #[case::string("<bla>♫<![CDATA[<cookies/>]]>♫</bla>", "♫<cookies/>♫".to_string())] + #[case::i64("<bla>0</bla>", 0i64)] + #[case::i64("<bla>-2</bla>", -2i64)] + #[case::i64("<bla>-1234</bla>", -1234i64)] + #[case::i64("<bla> -1234 </bla>", -1234i64)] + #[case::u64("<bla>0</bla>", 0u64)] + #[case::u64("<bla>1234</bla>", 1234u64)] + #[case::u64("<bla> 1234 </bla>", 1234u64)] + #[case::bool("<bla>true</bla>", true)] + #[case::bool("<bla>false</bla>", false)] + #[case::unit("<bla/>", ())] + #[case::f64("<bla>3.0</bla>", 3.0f64)] + #[case::f64("<bla>3.1</bla>", 3.1f64)] + #[case::f64("<bla>-1.2</bla>", -1.2f64)] + #[case::f64("<bla>0.4</bla>", 0.4f64)] + #[case::f64("<bla>0.4e5</bla>", 0.4e5f64)] + #[case::f64("<bla>0.4e15</bla>", 0.4e15f64)] + #[case::f64_precision_troubles("<bla>0.4e-01</bla>", 0.4e-01f64)] + #[case::f64("<bla> 0.4e-01 </bla>", 0.4e-01f64)] + #[case::option("<bla/>", Some("".to_string()))] + #[case::option("<bla></bla>", Some("".to_string()))] + #[case::option("<bla> </bla>", Some("".to_string()))] + #[case::option("<bla>42</bla>", Some("42".to_string()))] + fn element_ok<T, 'de>(_logger: (), #[case] document: &str, #[case] expected: T) + where + T: Deserialize<'de> + Debug + PartialEq, + { + let actual: T = from_str(document).unwrap(); + assert_eq!(actual, expected); + } + + #[rstest] + #[case("<bla>verum</bla>", Some(true))] + fn element_ko<T, 'de>(_logger: (), #[case] document: &str, #[case] _type: Option<T>) + where + T: Deserialize<'de> + Debug + PartialEq, + { + let actual: Result<T, _> = from_str(document); + assert!(actual.is_err()); + } + + #[derive(PartialEq, Debug, Deserialize)] + struct DummyAttribute<T> { + foo: T, + } + + #[rstest] + #[case(r#"<bla foo="true"/>"#, DummyAttribute { foo: true })] + #[case(r#"<bla foo="false"/>"#, DummyAttribute { foo: false })] + #[case(r#"<bla foo="1"/>"#, DummyAttribute { foo: true })] + #[case(r#"<bla foo="0"/>"#, DummyAttribute { foo: false })] + fn attribute_ok<T, 'de>(_logger: (), #[case] document: &str, #[case] expected: T) + where + T: Deserialize<'de> + Debug + PartialEq, + { + let actual: T = from_str(document).unwrap(); + assert_eq!(actual, expected); + } +} + +mod ser { + use super::*; + use serde::{self, Serialize}; + use serde_xml_rs::to_string; + + #[derive(Serialize, Debug)] + #[serde(rename = "bla")] + struct Dummy<T> { + #[serde(rename = "$value")] + value: T, + } + + #[rstest] + #[case::string("<bla>This is a String</bla>", "This is a String".to_string())] + #[case::string("<bla></bla>", "".to_string())] + #[case::string("<bla><boom/></bla>", "<boom/>".to_string())] + #[case::string("<bla>♫</bla>", "♫".to_string())] + #[case::string("<bla>♫<cookies/>♫</bla>", "♫<cookies/>♫".to_string())] + #[case::i64("<bla>0</bla>", 0i64)] + #[case::i64("<bla>-2</bla>", -2i64)] + #[case::i64("<bla>-1234</bla>", -1234i64)] + #[case::u64("<bla>0</bla>", 0u64)] + #[case::u64("<bla>1234</bla>", 1234u64)] + #[case::bool("<bla>true</bla>", true)] + #[case::bool("<bla>false</bla>", false)] + #[case::unit("<bla />", ())] + #[case::f64("<bla>3</bla>", 3.0f64)] + #[case::f64("<bla>3.1</bla>", 3.1f64)] + #[case::f64("<bla>-1.2</bla>", -1.2f64)] + #[case::f64("<bla>0.4</bla>", 0.4f64)] + #[case::f64("<bla>40000</bla>", 0.4e5f64)] + #[case::f64("<bla>400000000000000</bla>", 0.4e15f64)] + #[case::f64_precision_troubles("<bla>0.04</bla>", 0.4e-01f64)] + #[case::option("<bla></bla>", Some("".to_string()))] + #[case::option("<bla>42</bla>", Some("42".to_string()))] + fn element_ok<T>(_logger: (), #[case] expected: &str, #[case] value: T) + where + T: Serialize + Debug, + { + let actual = to_string(&Dummy { value }).unwrap(); + assert_eq!( + actual, + format!(r#"<?xml version="1.0" encoding="UTF-8"?>{}"#, expected) + ); + } + + #[derive(Serialize, Debug)] + #[serde(rename = "bla")] + struct DummyAttribute<T> { + #[serde(rename = "@value")] + value: T, + } + + #[rstest] + #[case::string(r#"<bla value="" />"#, "".to_string())] + #[case::bool(r#"<bla value="true" />"#, true)] + #[case::bool(r#"<bla value="false" />"#, false)] + fn attribute_ok<T>(_logger: (), #[case] expected: &str, #[case] value: T) + where + T: Serialize + Debug, + { + let actual = to_string(&DummyAttribute { value }).unwrap(); + assert_eq!( + actual, + format!(r#"<?xml version="1.0" encoding="UTF-8"?>{}"#, expected) + ); + } +} diff --git a/tests/failures.rs b/tests/failures.rs index 7a55811..89ac200 100644 --- a/tests/failures.rs +++ b/tests/failures.rs @@ -1,11 +1,9 @@ +mod common; + +use common::init_logger; use log::info; -use serde_derive::Deserialize; +use serde::Deserialize; use serde_xml_rs::from_str; -use simple_logger::SimpleLogger; - -fn init_logger() { - let _ = SimpleLogger::new().init(); -} #[derive(Debug, Deserialize, PartialEq)] struct Item { diff --git a/tests/migrated.rs b/tests/migrated.rs index e01d750..c67712e 100644 --- a/tests/migrated.rs +++ b/tests/migrated.rs @@ -1,13 +1,9 @@ -use simple_logger::SimpleLogger; -use std::fmt::Debug; +mod common; -use serde::{de, ser}; -use serde_derive::{Deserialize, Serialize}; +use common::init_logger; +use serde::{de, ser, Deserialize, Serialize}; use serde_xml_rs::{from_str, Error}; - -fn init_logger() { - let _ = SimpleLogger::new().init(); -} +use std::fmt::Debug; #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Animal { @@ -66,18 +62,6 @@ where } } -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() { init_logger(); @@ -173,27 +157,6 @@ fn test_forwarded_namespace() { } #[test] -fn test_parse_string() { - init_logger(); - - test_parse_ok(&[ - ( - "<bla>This is a String</bla>", - "This is a String".to_string(), - ), - ("<bla></bla>", "".to_string()), - ("<bla> </bla>", "".to_string()), - ("<bla><boom/></bla>", "<boom/>".to_string()), - ("<bla>♫</bla>", "♫".to_string()), - ("<bla>♫</bla>", "♫".to_string()), - ( - "<bla>♫<![CDATA[<cookies/>]]>♫</bla>", - "♫<cookies/>♫".to_string(), - ), - ]); -} - -#[test] #[ignore] // FIXME fn test_parse_string_not_trim() { init_logger(); @@ -269,87 +232,6 @@ fn test_parse_enum() { } #[test] -fn test_parse_i64() { - init_logger(); - test_parse_ok(&[ - ("<bla>0</bla>", 0), - ("<bla>-2</bla>", -2), - ("<bla>-1234</bla>", -1234), - ("<bla> -1234 </bla>", -1234), - ]); -} - -#[test] -fn test_parse_u64() { - init_logger(); - test_parse_ok(&[ - ("<bla>0</bla>", 0), - ("<bla>1234</bla>", 1234), - ("<bla> 1234 </bla>", 1234), - ]); -} - -#[test] -fn test_parse_bool_element() { - init_logger(); - 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, - } - - init_logger(); - 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() { - init_logger(); - test_parse_ok(&[("<bla/>", ())]); -} - -#[test] -fn test_parse_f64() { - init_logger(); - 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() { init_logger(); @@ -397,17 +279,6 @@ fn test_parse_struct() { } #[test] -fn test_option() { - init_logger(); - 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() { init_logger(); @@ -976,12 +847,12 @@ fn futile2() { #[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(&[ ( diff --git a/tests/round_trip.rs b/tests/round_trip.rs index 450a4b5..65c84ae 100644 --- a/tests/round_trip.rs +++ b/tests/round_trip.rs @@ -1,5 +1,4 @@ -use serde::Deserialize; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use serde_xml_rs::{self, from_str, to_string, EventReader, ParserConfig}; #[derive(Debug, Serialize, Deserialize, PartialEq)] @@ -23,7 +22,7 @@ struct Nodes { #[test] fn basic_struct() { - let src = r#"<Item><name>Banana</name><source>Store</source></Item>"#; + let src = r#"<?xml version="1.0" encoding="UTF-8"?><Item><name>Banana</name><source>Store</source></Item>"#; let should_be = Item { name: "Banana".to_string(), source: "Store".to_string(), @@ -37,7 +36,6 @@ fn basic_struct() { } #[test] -#[ignore] fn round_trip_list_of_enums() { // Construct some inputs let nodes = Nodes { @@ -51,17 +49,7 @@ fn round_trip_list_of_enums() { ], }; - let should_be = r#" - <Nodes> - <Boolean> - true - </Boolean> - <Identifier> - <value>foo</value> - <index>5</index> - </Identifier> - <EOF /> - </Nodes>"#; + let should_be = r#"<?xml version="1.0" encoding="UTF-8"?><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); @@ -77,6 +65,7 @@ fn whitespace_preserving_config() { // Test a configuration which does not clip whitespace from tags let src = r#" + <?xml version="1.0" encoding="UTF-8"?> <Item> <name> space banana </name> <source> fantasy costco </source> @@ -97,7 +86,7 @@ fn whitespace_preserving_config() { // Space outside values is not preserved. let serialized_should_be = - "<Item><name> space banana </name><source> fantasy costco </source></Item>"; + "<?xml version=\"1.0\" encoding=\"UTF-8\"?><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 index 52075b1..aa93df6 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,11 +1,8 @@ +mod common; + +use common::init_logger; use serde::Deserialize; -use serde_derive::Deserialize; use serde_xml_rs::{from_str, Deserializer}; -use simple_logger::SimpleLogger; - -fn init_logger() { - let _ = SimpleLogger::new().init(); -} #[derive(Debug, Deserialize, PartialEq)] struct Item { |