diff --git a/Cargo.toml b/Cargo.toml index 48d71687..204eca20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -186,6 +186,10 @@ required-features = ["serialize"] name = "serde-de" required-features = ["serialize"] +[[test]] +name = "serde-se" +required-features = ["serialize"] + [[test]] name = "serde-migrated" required-features = ["serialize"] diff --git a/Changelog.md b/Changelog.md index 36db39d9..22d67156 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,11 @@ ### Misc Changes +- [#468]: Content of `DeError::Unsupported` changed from `&'static str` to `Cow<'static, str>` +- [#468]: Ensure that map keys are restricted to only types that can be serialized as primitives + +[#468]: https://github.com/tafia/quick-xml/pull/468 + ## 0.24.0 -- 2022-08-28 ### New Features diff --git a/src/de/escape.rs b/src/de/escape.rs index 976ab109..2a05a250 100644 --- a/src/de/escape.rs +++ b/src/de/escape.rs @@ -81,7 +81,7 @@ impl<'de, 'a> serde::Deserializer<'de> for EscapedDeserializer<'a> { V: Visitor<'de>, { Err(DeError::Unsupported( - "binary data content is not supported by XML format", + "binary data content is not supported by XML format".into(), )) } diff --git a/src/de/map.rs b/src/de/map.rs index 08538ec9..76524e68 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -752,7 +752,9 @@ where self.map.de.reader.decoder(), ) .deserialize_seq(visitor), - e => Err(DeError::Custom(format!("Unsupported event {:?}", e))), + e => Err(DeError::Unsupported( + format!("unsupported event {:?}", e).into(), + )), }; // TODO: May be assert that here we expect only matching closing tag? self.map.de.read_to_end(e.name())?; diff --git a/src/de/mod.rs b/src/de/mod.rs index a0235526..e2593363 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -81,7 +81,7 @@ macro_rules! deserialize_primitives { where V: Visitor<'de>, { - Err(DeError::Unsupported("binary data content is not supported by XML format")) + Err(DeError::Unsupported("binary data content is not supported by XML format".into())) } /// Forwards deserialization to the [`deserialize_bytes`](#method.deserialize_bytes). diff --git a/src/de/simple_type.rs b/src/de/simple_type.rs index 3c2b2ac3..19211099 100644 --- a/src/de/simple_type.rs +++ b/src/de/simple_type.rs @@ -47,7 +47,7 @@ macro_rules! unsupported { $($(_: $type,)*)? _visitor: V ) -> Result { - Err(DeError::Unsupported($message)) + Err(DeError::Unsupported($message.into())) } }; } @@ -345,7 +345,7 @@ impl<'de> VariantAccess<'de> for AtomicUnitOnly { T: DeserializeSeed<'de>, { Err(DeError::Unsupported( - "enum newtype variants are not supported as `xs:list` items", + "enum newtype variants are not supported as `xs:list` items".into(), )) } @@ -354,7 +354,7 @@ impl<'de> VariantAccess<'de> for AtomicUnitOnly { V: Visitor<'de>, { Err(DeError::Unsupported( - "enum tuple variants are not supported as `xs:list` items", + "enum tuple variants are not supported as `xs:list` items".into(), )) } @@ -367,7 +367,7 @@ impl<'de> VariantAccess<'de> for AtomicUnitOnly { V: Visitor<'de>, { Err(DeError::Unsupported( - "enum struct variants are not supported as `xs:list` items", + "enum struct variants are not supported as `xs:list` items".into(), )) } } @@ -648,7 +648,7 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { V: Visitor<'de>, { Err(DeError::Unsupported( - "binary data content is not supported by XML format", + "binary data content is not supported by XML format".into(), )) } @@ -796,7 +796,7 @@ impl<'de> VariantAccess<'de> for SimpleTypeUnitOnly { T: DeserializeSeed<'de>, { Err(DeError::Unsupported( - "enum newtype variants are not supported for XSD `simpleType`s", + "enum newtype variants are not supported for XSD `simpleType`s".into(), )) } @@ -805,7 +805,7 @@ impl<'de> VariantAccess<'de> for SimpleTypeUnitOnly { V: Visitor<'de>, { Err(DeError::Unsupported( - "enum tuple variants are not supported for XSD `simpleType`s", + "enum tuple variants are not supported for XSD `simpleType`s".into(), )) } @@ -818,7 +818,7 @@ impl<'de> VariantAccess<'de> for SimpleTypeUnitOnly { V: Visitor<'de>, { Err(DeError::Unsupported( - "enum struct variants are not supported for XSD `simpleType`s", + "enum struct variants are not supported for XSD `simpleType`s".into(), )) } } diff --git a/src/de/var.rs b/src/de/var.rs index 513e896d..2034cedc 100644 --- a/src/de/var.rs +++ b/src/de/var.rs @@ -43,7 +43,7 @@ where } _ => { return Err(DeError::Unsupported( - "Invalid event for Enum, expecting `Text` or `Start`", + "Invalid event for Enum, expecting `Text` or `Start`".into(), )) } }; diff --git a/src/errors.rs b/src/errors.rs index 0f23590f..57f8f3b1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -3,6 +3,8 @@ use crate::escape::EscapeError; use crate::events::attributes::AttrError; use crate::utils::write_byte_string; +use std::fmt; +use std::io::Error as IoError; use std::str::Utf8Error; use std::string::FromUtf8Error; @@ -10,7 +12,7 @@ use std::string::FromUtf8Error; #[derive(Debug)] pub enum Error { /// IO error - Io(::std::io::Error), + Io(IoError), /// Input decoding error. If `encoding` feature is disabled, contains `None`, /// otherwise contains the UTF-8 decoding error NonDecodable(Option), @@ -39,10 +41,10 @@ pub enum Error { UnknownPrefix(Vec), } -impl From<::std::io::Error> for Error { +impl From for Error { /// Creates a new `Error::Io` from the given error #[inline] - fn from(error: ::std::io::Error) -> Error { + fn from(error: IoError) -> Error { Error::Io(error) } } @@ -81,8 +83,8 @@ impl From for Error { /// A specialized `Result` type where the error is hard-wired to [`Error`]. pub type Result = std::result::Result; -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::Io(e) => write!(f, "I/O error: {}", e), Error::NonDecodable(None) => write!(f, "Malformed input, decoding impossible"), @@ -132,7 +134,7 @@ pub mod serialize { use super::*; use crate::utils::write_byte_string; - use std::fmt; + use std::borrow::Cow; #[cfg(feature = "overlapped-lists")] use std::num::NonZeroUsize; use std::num::{ParseFloatError, ParseIntError}; @@ -186,7 +188,7 @@ pub mod serialize { /// An attempt to deserialize to a type, that is not supported by the XML /// store at current position, for example, attempt to deserialize `struct` /// from attribute or attempt to deserialize binary data. - Unsupported(&'static str), + Unsupported(Cow<'static, str>), /// Too many events were skipped while deserializing a sequence, event limit /// exceeded. The limit was provided as an argument #[cfg(feature = "overlapped-lists")] @@ -214,7 +216,7 @@ pub mod serialize { } DeError::UnexpectedEof => write!(f, "Unexpected `Event::Eof`"), DeError::ExpectedStart => write!(f, "Expecting `Event::Start`"), - DeError::Unsupported(s) => write!(f, "Unsupported operation {}", s), + DeError::Unsupported(s) => write!(f, "Unsupported operation: {}", s), #[cfg(feature = "overlapped-lists")] DeError::TooManyEvents(s) => write!(f, "Deserializer buffers {} events, limit exceeded", s), } @@ -292,4 +294,11 @@ pub mod serialize { Self::InvalidFloat(e) } } + + impl From for DeError { + #[inline] + fn from(e: fmt::Error) -> Self { + Self::Custom(e.to_string()) + } + } } diff --git a/src/se/key.rs b/src/se/key.rs new file mode 100644 index 00000000..0adf84a1 --- /dev/null +++ b/src/se/key.rs @@ -0,0 +1,297 @@ +use crate::errors::serialize::DeError; +use serde::ser::{Impossible, Serialize, Serializer}; +use serde::serde_if_integer128; +use std::fmt::Write; + +/// A serializer, that ensures, that only plain types can be serialized, +/// so result can be used as an XML tag or attribute name. +/// +/// This serializer does not check that name does not contain characters that +/// [not allowed] in XML names, because in some cases it should pass names +/// that would be filtered on higher level. +/// +/// [not allowed]: https://www.w3.org/TR/REC-xml/#sec-common-syn +pub struct XmlNameSerializer { + /// Writer to which this serializer writes content + pub writer: W, +} + +impl XmlNameSerializer { + #[inline] + fn write_str(&mut self, value: &str) -> Result<(), DeError> { + Ok(self.writer.write_str(value)?) + } +} + +impl Serializer for XmlNameSerializer { + type Ok = W; + type Error = DeError; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + write_primitive!(); + + fn serialize_str(mut self, value: &str) -> Result { + self.write_str(value)?; + Ok(self.writer) + } + + /// We cannot store anything, so the absence of a unit and presence of it + /// does not differ, so serialization of unit returns `Err(Unsupported)` + fn serialize_unit(self) -> Result { + Err(DeError::Unsupported( + "unit type `()` cannot be serialized as an XML tag name".into(), + )) + } + + /// We cannot store both a variant discriminant and a variant value, + /// so serialization of enum newtype variant returns `Err(Unsupported)` + fn serialize_newtype_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _value: &T, + ) -> Result { + Err(DeError::Unsupported( + format!( + "enum newtype variant `{}::{}` cannot be serialized as an XML tag name", + name, variant + ) + .into(), + )) + } + + fn serialize_seq(self, _len: Option) -> Result { + Err(DeError::Unsupported( + "sequence cannot be serialized as an XML tag name".into(), + )) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(DeError::Unsupported( + "tuple cannot be serialized as an XML tag name".into(), + )) + } + + fn serialize_tuple_struct( + self, + name: &'static str, + _len: usize, + ) -> Result { + Err(DeError::Unsupported( + format!( + "tuple struct `{}` cannot be serialized as an XML tag name", + name + ) + .into(), + )) + } + + fn serialize_tuple_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + Err(DeError::Unsupported( + format!( + "enum tuple variant `{}::{}` cannot be serialized as an XML tag name", + name, variant + ) + .into(), + )) + } + + fn serialize_map(self, _len: Option) -> Result { + Err(DeError::Unsupported( + "map cannot be serialized as an XML tag name".into(), + )) + } + + fn serialize_struct( + self, + name: &'static str, + _len: usize, + ) -> Result { + Err(DeError::Unsupported( + format!("struct `{}` cannot be serialized as an XML tag name", name).into(), + )) + } + + fn serialize_struct_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + Err(DeError::Unsupported( + format!( + "enum struct variant `{}::{}` cannot be serialized as an XML tag name", + name, variant + ) + .into(), + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::utils::Bytes; + use pretty_assertions::assert_eq; + use serde::Serialize; + use std::collections::BTreeMap; + + #[derive(Debug, Serialize, PartialEq)] + struct Unit; + + #[derive(Debug, Serialize, PartialEq)] + #[serde(rename = "<\"&'>")] + struct UnitEscaped; + + #[derive(Debug, Serialize, PartialEq)] + struct Newtype(bool); + + #[derive(Debug, Serialize, PartialEq)] + struct Tuple(&'static str, usize); + + #[derive(Debug, Serialize, PartialEq)] + struct Struct { + key: &'static str, + val: usize, + } + + #[derive(Debug, Serialize, PartialEq)] + enum Enum { + Unit, + #[serde(rename = "<\"&'>")] + UnitEscaped, + Newtype(bool), + Tuple(&'static str, usize), + Struct { + key: &'static str, + val: usize, + }, + } + + /// Checks that given `$data` successfully serialized as `$expected` + macro_rules! serialize_as { + ($name:ident: $data:expr => $expected:literal) => { + #[test] + fn $name() { + let ser = XmlNameSerializer { + writer: String::new(), + }; + + let buffer = $data.serialize(ser).unwrap(); + assert_eq!(buffer, $expected); + } + }; + } + + /// Checks that attempt to serialize given `$data` results to a + /// serialization error `$kind` with `$reason` + macro_rules! err { + ($name:ident: $data:expr => $kind:ident($reason:literal)) => { + #[test] + fn $name() { + let mut buffer = String::new(); + let ser = XmlNameSerializer { + writer: &mut buffer, + }; + + match $data.serialize(ser).unwrap_err() { + DeError::$kind(e) => assert_eq!(e, $reason), + e => panic!( + "Expected `{}({})`, found `{:?}`", + stringify!($kind), + $reason, + e + ), + } + assert_eq!(buffer, ""); + } + }; + } + + serialize_as!(false_: false => "false"); + serialize_as!(true_: true => "true"); + + serialize_as!(i8_: -42i8 => "-42"); + serialize_as!(i16_: -4200i16 => "-4200"); + serialize_as!(i32_: -42000000i32 => "-42000000"); + serialize_as!(i64_: -42000000000000i64 => "-42000000000000"); + serialize_as!(isize_: -42000000000000isize => "-42000000000000"); + + serialize_as!(u8_: 42u8 => "42"); + serialize_as!(u16_: 4200u16 => "4200"); + serialize_as!(u32_: 42000000u32 => "42000000"); + serialize_as!(u64_: 42000000000000u64 => "42000000000000"); + serialize_as!(usize_: 42000000000000usize => "42000000000000"); + + serde_if_integer128! { + serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); + serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); + } + + serialize_as!(f32_: 4.2f32 => "4.2"); + serialize_as!(f64_: 4.2f64 => "4.2"); + + serialize_as!(char_non_escaped: 'h' => "h"); + serialize_as!(char_lt: '<' => "<"); + serialize_as!(char_gt: '>' => ">"); + serialize_as!(char_amp: '&' => "&"); + serialize_as!(char_apos: '\'' => "'"); + serialize_as!(char_quot: '"' => "\""); + + serialize_as!(str_valid_name: "valid-name" => "valid-name"); + serialize_as!(str_space: "string with spaces" => "string with spaces"); + serialize_as!(str_lt: "string<" => "string<"); + serialize_as!(str_gt: "string>" => "string>"); + serialize_as!(str_amp: "string&" => "string&"); + serialize_as!(str_apos: "string'" => "string'"); + serialize_as!(str_quot: "string\"" => "string\""); + + err!(bytes: Bytes(b"<\"escaped & bytes'>") + => Unsupported("`serialize_bytes` not supported yet")); + + serialize_as!(option_none: Option::<&str>::None => ""); + serialize_as!(option_some: Some("non-escaped-string") => "non-escaped-string"); + + err!(unit: () + => Unsupported("unit type `()` cannot be serialized as an XML tag name")); + serialize_as!(unit_struct: Unit => "Unit"); + serialize_as!(unit_struct_escaped: UnitEscaped => "<\"&'>"); + + serialize_as!(enum_unit: Enum::Unit => "Unit"); + serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<\"&'>"); + + serialize_as!(newtype: Newtype(true) => "true"); + err!(enum_newtype: Enum::Newtype(false) + => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an XML tag name")); + + err!(seq: vec![1, 2, 3] + => Unsupported("sequence cannot be serialized as an XML tag name")); + err!(tuple: ("<\"&'>", "with\t\r\n spaces", 3usize) + => Unsupported("tuple cannot be serialized as an XML tag name")); + err!(tuple_struct: Tuple("first", 42) + => Unsupported("tuple struct `Tuple` cannot be serialized as an XML tag name")); + err!(enum_tuple: Enum::Tuple("first", 42) + => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an XML tag name")); + + err!(map: BTreeMap::from([("_1", 2), ("_3", 4)]) + => Unsupported("map cannot be serialized as an XML tag name")); + err!(struct_: Struct { key: "answer", val: 42 } + => Unsupported("struct `Struct` cannot be serialized as an XML tag name")); + err!(enum_struct: Enum::Struct { key: "answer", val: 42 } + => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an XML tag name")); +} diff --git a/src/se/mod.rs b/src/se/mod.rs index eab194ba..42fed721 100644 --- a/src/se/mod.rs +++ b/src/se/mod.rs @@ -1,5 +1,83 @@ //! Module to handle custom serde `Serializer` +/// Implements writing primitives to the underlying writer. +/// Implementor must provide `write_str(self, &str) -> Result<(), DeError>` method +macro_rules! write_primitive { + ($method:ident ( $ty:ty )) => { + fn $method(mut self, value: $ty) -> Result { + self.write_str(&value.to_string())?; + Ok(self.writer) + } + }; + () => { + fn serialize_bool(mut self, value: bool) -> Result { + self.write_str(if value { "true" } else { "false" })?; + Ok(self.writer) + } + + write_primitive!(serialize_i8(i8)); + write_primitive!(serialize_i16(i16)); + write_primitive!(serialize_i32(i32)); + write_primitive!(serialize_i64(i64)); + + write_primitive!(serialize_u8(u8)); + write_primitive!(serialize_u16(u16)); + write_primitive!(serialize_u32(u32)); + write_primitive!(serialize_u64(u64)); + + serde_if_integer128! { + write_primitive!(serialize_i128(i128)); + write_primitive!(serialize_u128(u128)); + } + + write_primitive!(serialize_f32(f32)); + write_primitive!(serialize_f64(f64)); + + fn serialize_char(self, value: char) -> Result { + self.serialize_str(&value.to_string()) + } + + fn serialize_bytes(self, _value: &[u8]) -> Result { + //TODO: customization point - allow user to decide how to encode bytes + Err(DeError::Unsupported( + "`serialize_bytes` not supported yet".into(), + )) + } + + fn serialize_none(self) -> Result { + Ok(self.writer) + } + + fn serialize_some(self, value: &T) -> Result { + value.serialize(self) + } + + fn serialize_unit_struct(self, name: &'static str) -> Result { + self.serialize_str(name) + } + + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + self.serialize_str(variant) + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result { + value.serialize(self) + } + }; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +mod key; mod var; use self::var::{Map, Seq, Struct, Tuple}; @@ -209,7 +287,9 @@ impl<'r, 'w, W: Write> ser::Serializer for &'w mut Serializer<'r, W> { fn serialize_bytes(self, _value: &[u8]) -> Result { // TODO: I imagine you'd want to use base64 here. // Not sure how to roundtrip effectively though... - Err(DeError::Unsupported("serialize_bytes")) + Err(DeError::Unsupported( + "`serialize_bytes` not supported yet".into(), + )) } fn serialize_none(self) -> Result { @@ -329,873 +409,3 @@ impl<'r, 'w, W: Write> ser::Serializer for &'w mut Serializer<'r, W> { Ok(Struct::new(self, variant)) } } - -#[cfg(test)] -mod tests { - use super::*; - use pretty_assertions::assert_eq; - use serde::ser::SerializeMap; - use serde::{Serialize, 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_serialize_struct() { - #[derive(Serialize)] - struct Person { - name: String, - age: u32, - } - - let bob = Person { - name: "Bob".to_string(), - age: 42, - }; - let should_be = ""; - 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_struct_value_number() { - #[derive(Serialize)] - struct Person { - name: String, - #[serde(rename = "$value")] - age: u32, - } - - let bob = Person { - name: "Bob".to_string(), - age: 42, - }; - let should_be = "42"; - let got = to_string(&bob).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn test_serialize_struct_value_string() { - #[derive(Serialize)] - struct Person { - name: String, - #[serde(rename = "$value")] - age: String, - } - - let bob = Person { - name: "Bob".to_string(), - age: "42".to_string(), - }; - let should_be = "42"; - let got = to_string(&bob).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn test_serialize_map_entries() { - let should_be = "Bob5"; - 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 = "true"; - - { - 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!(); - } - - #[test] - fn unit() { - #[derive(Serialize)] - struct Unit; - - let data = Unit; - let should_be = ""; - let mut buffer = Vec::new(); - - { - let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); - data.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn newtype() { - #[derive(Serialize)] - struct Newtype(bool); - - let data = Newtype(true); - let should_be = "true"; - let mut buffer = Vec::new(); - - { - let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); - data.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn tuple() { - let data = (42.0, "answer"); - let should_be = "42answer"; - let mut buffer = Vec::new(); - - { - let mut ser = - Serializer::with_root(Writer::new_with_indent(&mut buffer, b' ', 4), Some("root")); - data.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn tuple_struct() { - #[derive(Serialize)] - struct Tuple(f32, &'static str); - - let data = Tuple(42.0, "answer"); - let should_be = "42answer"; - let mut buffer = Vec::new(); - - { - let mut ser = - Serializer::with_root(Writer::new_with_indent(&mut buffer, b' ', 4), Some("root")); - data.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn struct_() { - #[derive(Serialize)] - struct Struct { - float: f64, - string: String, - } - - let mut buffer = Vec::new(); - let should_be = r#""#; - - { - let mut ser = - Serializer::with_root(Writer::new_with_indent(&mut buffer, b' ', 4), Some("root")); - let node = Struct { - float: 42.0, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn nested_struct() { - #[derive(Serialize)] - struct Struct { - nested: Nested, - string: String, - } - - #[derive(Serialize)] - struct Nested { - float: f64, - } - - let mut buffer = Vec::new(); - let should_be = r#" -"#; - - { - let mut ser = - Serializer::with_root(Writer::new_with_indent(&mut buffer, b' ', 4), Some("root")); - let node = Struct { - nested: Nested { float: 42.0 }, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn flatten_struct() { - #[derive(Serialize)] - struct Struct { - #[serde(flatten)] - nested: Nested, - string: String, - } - - #[derive(Serialize)] - struct Nested { - float: f64, - } - - let mut buffer = Vec::new(); - let should_be = r#"42answer"#; - - { - let mut ser = - Serializer::with_root(Writer::new_with_indent(&mut buffer, b' ', 4), Some("root")); - let node = Struct { - nested: Nested { float: 42.0 }, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - mod enum_ { - use super::*; - - #[derive(Serialize)] - struct Nested { - float: f64, - } - - mod externally_tagged { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Serialize)] - enum Node { - Unit, - #[serde(rename = "$primitive=PrimitiveUnit")] - PrimitiveUnit, - Newtype(bool), - Tuple(f64, String), - Struct { - float: f64, - string: String, - }, - Holder { - nested: Nested, - string: String, - }, - Flatten { - #[serde(flatten)] - nested: Nested, - string: String, - }, - } - - #[test] - fn unit() { - let mut buffer = Vec::new(); - let should_be = ""; - - { - let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); - let node = Node::Unit; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn primitive_unit() { - let mut buffer = Vec::new(); - let should_be = "PrimitiveUnit"; - - { - let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); - let node = Node::PrimitiveUnit; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn newtype() { - let mut buffer = Vec::new(); - let should_be = "true"; - - { - let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); - let node = Node::Newtype(true); - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn struct_() { - let mut buffer = Vec::new(); - let should_be = r#""#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Struct { - float: 42.0, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn tuple_struct() { - let mut buffer = Vec::new(); - let should_be = "42answer"; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Tuple(42.0, "answer".to_string()); - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn nested_struct() { - let mut buffer = Vec::new(); - let should_be = r#" -"#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Holder { - nested: Nested { float: 42.0 }, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn flatten_struct() { - let mut buffer = Vec::new(); - let should_be = r#"42answer"#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Flatten { - nested: Nested { float: 42.0 }, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - } - - mod internally_tagged { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Serialize)] - #[serde(tag = "tag")] - enum Node { - Unit, - /// Primitives (such as `bool`) are not supported by the serde in the internally tagged mode - Newtype(NewtypeContent), - // Tuple(f64, String),// Tuples are not supported in the internally tagged mode - Struct { - float: f64, - string: String, - }, - Holder { - nested: Nested, - string: String, - }, - Flatten { - #[serde(flatten)] - nested: Nested, - string: String, - }, - } - - #[derive(Serialize)] - struct NewtypeContent { - value: bool, - } - - #[test] - fn unit() { - let mut buffer = Vec::new(); - let should_be = r#""#; - - { - let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); - let node = Node::Unit; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn newtype() { - let mut buffer = Vec::new(); - let should_be = r#""#; - - { - let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); - let node = Node::Newtype(NewtypeContent { value: true }); - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn struct_() { - let mut buffer = Vec::new(); - let should_be = r#""#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Struct { - float: 42.0, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn nested_struct() { - let mut buffer = Vec::new(); - let should_be = r#" -"#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Holder { - nested: Nested { float: 42.0 }, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn flatten_struct() { - let mut buffer = Vec::new(); - let should_be = - r#"Flatten42answer"#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Flatten { - nested: Nested { float: 42.0 }, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - } - - mod adjacently_tagged { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Serialize)] - #[serde(tag = "tag", content = "content")] - enum Node { - Unit, - Newtype(bool), - Tuple(f64, String), - Struct { - float: f64, - string: String, - }, - Holder { - nested: Nested, - string: String, - }, - Flatten { - #[serde(flatten)] - nested: Nested, - string: String, - }, - } - - #[test] - fn unit() { - let mut buffer = Vec::new(); - let should_be = r#""#; - - { - let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); - let node = Node::Unit; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn newtype() { - let mut buffer = Vec::new(); - let should_be = r#""#; - - { - let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); - let node = Node::Newtype(true); - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn tuple_struct() { - let mut buffer = Vec::new(); - let should_be = r#"42answer -"#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Tuple(42.0, "answer".to_string()); - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn struct_() { - let mut buffer = Vec::new(); - let should_be = r#" -"#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Struct { - float: 42.0, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn nested_struct() { - let mut buffer = Vec::new(); - let should_be = r#" -"#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Holder { - nested: Nested { float: 42.0 }, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn flatten_struct() { - let mut buffer = Vec::new(); - let should_be = r#"42answer -"#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Flatten { - nested: Nested { float: 42.0 }, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - } - - mod untagged { - use super::*; - use pretty_assertions::assert_eq; - - #[derive(Serialize)] - #[serde(untagged)] - enum Node { - Unit, - Newtype(bool), - Tuple(f64, String), - Struct { - float: f64, - string: String, - }, - Holder { - nested: Nested, - string: String, - }, - Flatten { - #[serde(flatten)] - nested: Nested, - string: String, - }, - } - - #[test] - fn unit() { - let mut buffer = Vec::new(); - // Unit variant consists just from the tag, and because tags - // are not written in untagged mode, nothing is written - let should_be = ""; - - { - let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); - let node = Node::Unit; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn newtype() { - let mut buffer = Vec::new(); - let should_be = "true"; - - { - let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); - let node = Node::Newtype(true); - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn tuple_struct() { - let mut buffer = Vec::new(); - let should_be = "42answer"; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Tuple(42.0, "answer".to_string()); - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn struct_() { - let mut buffer = Vec::new(); - let should_be = r#""#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Struct { - float: 42.0, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn nested_struct() { - let mut buffer = Vec::new(); - let should_be = r#" -"#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Holder { - nested: Nested { float: 42.0 }, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - - #[test] - fn flatten_struct() { - let mut buffer = Vec::new(); - let should_be = r#"42answer"#; - - { - let mut ser = Serializer::with_root( - Writer::new_with_indent(&mut buffer, b' ', 4), - Some("root"), - ); - let node = Node::Flatten { - nested: Nested { float: 42.0 }, - string: "answer".to_string(), - }; - node.serialize(&mut ser).unwrap(); - } - - let got = String::from_utf8(buffer).unwrap(); - assert_eq!(got, should_be); - } - } - } -} diff --git a/src/se/var.rs b/src/se/var.rs index c8e062eb..eb154406 100644 --- a/src/se/var.rs +++ b/src/se/var.rs @@ -2,6 +2,7 @@ use crate::{ de::{INNER_VALUE, UNFLATTEN_PREFIX}, errors::{serialize::DeError, Error}, events::{BytesEnd, BytesStart, Event}, + se::key::XmlNameSerializer, se::Serializer, writer::Writer, }; @@ -64,21 +65,27 @@ where key: &K, value: &V, ) -> Result<(), DeError> { - // 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.inner(), "<").map_err(Error::Io)?; - key.serialize(&mut *self.parent)?; - write!(self.parent.writer.inner(), ">").map_err(Error::Io)?; + let key = key.serialize(XmlNameSerializer { + writer: String::new(), + })?; + + let writer = self.parent.writer.inner(); + writer.write_all(b"<").map_err(Error::Io)?; + writer.write_all(key.as_bytes()).map_err(Error::Io)?; + writer.write_all(b">").map_err(Error::Io)?; value.serialize(&mut *self.parent)?; - write!(self.parent.writer.inner(), "").map_err(Error::Io)?; + let writer = self.parent.writer.inner(); + writer.write_all(b"").map_err(Error::Io)?; Ok(()) } } +//////////////////////////////////////////////////////////////////////////////////////////////////// + /// An implementation of `SerializeStruct` for serializing to XML. pub struct Struct<'r, 'w, W> where @@ -185,6 +192,8 @@ where } } +//////////////////////////////////////////////////////////////////////////////////////////////////// + /// An implementation of `SerializeSeq' for serializing to XML. pub struct Seq<'r, 'w, W> where @@ -223,6 +232,8 @@ where } } +//////////////////////////////////////////////////////////////////////////////////////////////////// + /// An implementation of `SerializeTuple`, `SerializeTupleStruct` and /// `SerializeTupleVariant` for serializing to XML. pub struct Tuple<'r, 'w, W> @@ -308,3 +319,24 @@ where ::end(self) } } + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#[test] +fn test_serialize_map_entries() { + use serde::ser::SerializeMap; + + 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(); + } + + assert_eq!( + String::from_utf8(buffer).unwrap(), + "Bob5" + ); +} diff --git a/src/utils.rs b/src/utils.rs index 130532a3..185cff40 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,6 +3,8 @@ use std::fmt::{self, Debug, Formatter}; #[cfg(feature = "serialize")] use serde::de::{Deserialize, Deserializer, Error, Visitor}; +#[cfg(feature = "serialize")] +use serde::ser::{Serialize, Serializer}; pub fn write_cow_string(f: &mut Formatter, cow_string: &Cow<[u8]>) -> fmt::Result { match cow_string { @@ -76,6 +78,16 @@ impl<'de> Deserialize<'de> for ByteBuf { } } +#[cfg(feature = "serialize")] +impl Serialize for ByteBuf { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bytes(&self.0) + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////// /// Wrapper around `&[u8]` that has a human-readable debug representation: @@ -117,6 +129,16 @@ impl<'de> Deserialize<'de> for Bytes<'de> { } } +#[cfg(feature = "serialize")] +impl<'de> Serialize for Bytes<'de> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bytes(self.0) + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////// #[cfg(test)] diff --git a/tests/serde-se.rs b/tests/serde-se.rs new file mode 100644 index 00000000..99d2b5ba --- /dev/null +++ b/tests/serde-se.rs @@ -0,0 +1,493 @@ +use quick_xml::se::{to_string, Serializer}; +use quick_xml::writer::Writer; + +use pretty_assertions::assert_eq; + +use serde::{Serialize, Serializer as SerSerializer}; + +#[test] +fn serialize_bool() { + let inputs = [(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(); + + assert_eq!(String::from_utf8(buffer).unwrap(), *should_be); + } +} + +#[test] +fn serialize_struct() { + #[derive(Serialize)] + struct Person { + name: String, + age: u32, + } + + let bob = Person { + name: "Bob".to_string(), + age: 42, + }; + + let mut buffer = Vec::new(); + let mut ser = Serializer::new(&mut buffer); + bob.serialize(&mut ser).unwrap(); + + assert_eq!( + String::from_utf8(buffer).unwrap(), + "" + ); +} + +#[test] +fn serialize_struct_value_number() { + #[derive(Serialize)] + struct Person { + name: String, + #[serde(rename = "$value")] + age: u32, + } + + let bob = Person { + name: "Bob".to_string(), + age: 42, + }; + assert_eq!(to_string(&bob).unwrap(), "42"); +} + +#[test] +fn serialize_struct_value_string() { + #[derive(Serialize)] + struct Person { + name: String, + #[serde(rename = "$value")] + age: String, + } + + let bob = Person { + name: "Bob".to_string(), + age: "42".to_string(), + }; + assert_eq!(to_string(&bob).unwrap(), "42"); +} + +#[test] +fn serialize_enum() { + #[derive(Serialize)] + #[allow(dead_code)] + enum Node { + Boolean(bool), + Number(f64), + String(String), + } + + let mut buffer = Vec::new(); + let mut ser = Serializer::new(&mut buffer); + let node = Node::Boolean(true); + node.serialize(&mut ser).unwrap(); + + assert_eq!( + String::from_utf8(buffer).unwrap(), + "true" + ); +} + +#[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(); + + println!("{}", String::from_utf8(buffer).unwrap()); + panic!(); +} + +#[derive(Serialize)] +struct Unit; + +#[derive(Serialize)] +struct Newtype(bool); + +#[derive(Serialize)] +struct Tuple(f32, &'static str); + +#[derive(Serialize)] +struct Struct { + float: f64, + string: &'static str, +} + +#[derive(Serialize)] +struct NestedStruct { + nested: Nested, + string: &'static str, +} + +#[derive(Serialize)] +struct FlattenStruct { + #[serde(flatten)] + nested: Nested, + string: &'static str, +} + +#[derive(Serialize)] +struct Nested { + float: f64, +} + +#[derive(Serialize)] +struct Empty {} + +#[derive(Serialize)] +struct Value { + #[serde(rename = "$value")] + float: f64, + string: &'static str, +} + +#[derive(Serialize)] +enum ExternallyTagged { + Unit, + #[serde(rename = "$primitive=PrimitiveUnit")] + PrimitiveUnit, + Newtype(bool), + Tuple(f64, &'static str), + Struct { + float: f64, + string: &'static str, + }, + Holder { + nested: Nested, + string: &'static str, + }, + Flatten { + #[serde(flatten)] + nested: Nested, + string: &'static str, + }, + Empty {}, + Value { + #[serde(rename = "$value")] + float: f64, + string: &'static str, + }, +} + +#[derive(Serialize)] +#[serde(tag = "tag")] +enum InternallyTagged { + Unit, + /// Primitives (such as `bool`) are not supported by the serde in the internally tagged mode + Newtype(Nested), + // Tuple(f64, &'static str),// Tuples are not supported in the internally tagged mode + Struct { + float: f64, + string: &'static str, + }, + Holder { + nested: Nested, + string: &'static str, + }, + Flatten { + #[serde(flatten)] + nested: Nested, + string: &'static str, + }, + Empty {}, + Value { + #[serde(rename = "$value")] + float: f64, + string: &'static str, + }, +} + +#[derive(Serialize)] +#[serde(tag = "tag", content = "content")] +enum AdjacentlyTagged { + Unit, + Newtype(bool), + Tuple(f64, &'static str), + Struct { + float: f64, + string: &'static str, + }, + Holder { + nested: Nested, + string: &'static str, + }, + Flatten { + #[serde(flatten)] + nested: Nested, + string: &'static str, + }, + Empty {}, + Value { + #[serde(rename = "$value")] + float: f64, + string: &'static str, + }, +} + +#[derive(Serialize)] +#[serde(untagged)] +enum Untagged { + Unit, + Newtype(bool), + Tuple(f64, &'static str), + Struct { + float: f64, + string: &'static str, + }, + Holder { + nested: Nested, + string: &'static str, + }, + Flatten { + #[serde(flatten)] + nested: Nested, + string: &'static str, + }, + Empty {}, + Value { + #[serde(rename = "$value")] + float: f64, + string: &'static str, + }, +} + +mod with_root { + use super::*; + use pretty_assertions::assert_eq; + + macro_rules! serialize_as { + ($name:ident: $data:expr => $expected:literal) => { + #[test] + fn $name() { + let mut buffer = Vec::new(); + let mut ser = Serializer::with_root(Writer::new(&mut buffer), Some("root")); + + $data.serialize(&mut ser).unwrap(); + assert_eq!(String::from_utf8(buffer).unwrap(), $expected); + } + }; + } + + serialize_as!(unit: + Unit + => ""); + serialize_as!(newtype: + Newtype(true) + => "true"); + serialize_as!(tuple: + (42.0, "answer") + => "42answer"); + serialize_as!(tuple_struct: + Tuple(42.0, "answer") + => "42answer"); + serialize_as!(struct_: + Struct { + float: 42.0, + string: "answer" + } + => r#""#); + serialize_as!(nested_struct: + NestedStruct { + nested: Nested { float: 42.0 }, + string: "answer", + } + => r#""#); + serialize_as!(flatten_struct: + FlattenStruct { + nested: Nested { float: 42.0 }, + string: "answer", + } + => r#"42answer"#); + serialize_as!(empty_struct: + Empty {} + => ""); + serialize_as!(value: + Value { + float: 42.0, + string: "answer" + } + => r#"42"#); + + mod enum_ { + use super::*; + + mod externally_tagged { + use super::*; + use pretty_assertions::assert_eq; + + serialize_as!(unit: + ExternallyTagged::Unit + => ""); + serialize_as!(primitive_unit: + ExternallyTagged::PrimitiveUnit + => "PrimitiveUnit"); + serialize_as!(newtype: + ExternallyTagged::Newtype(true) + => "true"); + serialize_as!(tuple_struct: + ExternallyTagged::Tuple(42.0, "answer") + => "42answer"); + serialize_as!(struct_: + ExternallyTagged::Struct { + float: 42.0, + string: "answer", + } + => r#""#); + serialize_as!(nested_struct: + ExternallyTagged::Holder { + nested: Nested { float: 42.0 }, + string: "answer", + } + => r#""#); + serialize_as!(flatten_struct: + ExternallyTagged::Flatten { + nested: Nested { float: 42.0 }, + string: "answer", + } + => r#"42answer"#); + serialize_as!(empty_struct: + ExternallyTagged::Empty {} + => ""); + serialize_as!(value: + ExternallyTagged::Value { + float: 42.0, + string: "answer" + } + => r#"42"#); + } + + mod internally_tagged { + use super::*; + use pretty_assertions::assert_eq; + + serialize_as!(unit: + InternallyTagged::Unit + => r#""#); + serialize_as!(newtype: + InternallyTagged::Newtype(Nested { float: 4.2 }) + => r#""#); + serialize_as!(struct_: + InternallyTagged::Struct { + float: 42.0, + string: "answer", + } + => r#""#); + serialize_as!(nested_struct: + InternallyTagged::Holder { + nested: Nested { float: 42.0 }, + string: "answer", + } + => r#""#); + serialize_as!(flatten_struct: + InternallyTagged::Flatten { + nested: Nested { float: 42.0 }, + string: "answer", + } + => r#"Flatten42answer"#); + serialize_as!(empty_struct: + InternallyTagged::Empty {} + => r#""#); + serialize_as!(value: + InternallyTagged::Value { + float: 42.0, + string: "answer" + } + => r#"42"#); + } + + mod adjacently_tagged { + use super::*; + use pretty_assertions::assert_eq; + + serialize_as!(unit: + AdjacentlyTagged::Unit + => r#""#); + serialize_as!(newtype: + AdjacentlyTagged::Newtype(true) + => r#""#); + serialize_as!(tuple_struct: + AdjacentlyTagged::Tuple(42.0, "answer") + => r#"42answer"#); + serialize_as!(struct_: + AdjacentlyTagged::Struct { + float: 42.0, + string: "answer", + } + => r#""#); + serialize_as!(nested_struct: + AdjacentlyTagged::Holder { + nested: Nested { float: 42.0 }, + string: "answer", + } + => r#""#); + serialize_as!(flatten_struct: + AdjacentlyTagged::Flatten { + nested: Nested { float: 42.0 }, + string: "answer", + } + => r#"42answer"#); + serialize_as!(empty_struct: + AdjacentlyTagged::Empty {} + => r#""#); + serialize_as!(value: + AdjacentlyTagged::Value { + float: 42.0, + string: "answer", + } + => r#"42"#); + } + + mod untagged { + use super::*; + use pretty_assertions::assert_eq; + + serialize_as!(unit: + Untagged::Unit + // Unit variant consists just from the tag, and because tags + // are not written in untagged mode, nothing is written + => ""); + serialize_as!(newtype: + Untagged::Newtype(true) + => "true"); + serialize_as!(tuple_struct: + Untagged::Tuple(42.0, "answer") + => "42answer"); + serialize_as!(struct_: + Untagged::Struct { + float: 42.0, + string: "answer", + } + => r#""#); + serialize_as!(nested_struct: + Untagged::Holder { + nested: Nested { float: 42.0 }, + string: "answer", + } + => r#""#); + serialize_as!(flatten_struct: + Untagged::Flatten { + nested: Nested { float: 42.0 }, + string: "answer", + } + => r#"42answer"#); + serialize_as!(empty_struct: + Untagged::Empty {} + => ""); + serialize_as!(value: + Untagged::Value { + float: 42.0, + string: "answer" + } + => r#"42"#); + } + } +}