//! Contains serializer for an XML element use crate::de::{TEXT_KEY, VALUE_KEY}; use crate::se::content::ContentSerializer; use crate::se::key::QNameSerializer; use crate::se::simple_type::{QuoteTarget, SimpleSeq, SimpleTypeSerializer}; use crate::se::text::TextSerializer; use crate::se::{SeError, WriteResult, XmlName}; use serde::ser::{ Impossible, Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, Serializer, }; use serde::serde_if_integer128; use std::fmt::Write; /// Writes simple type content between [`ElementSerializer::key`] tags. macro_rules! write_primitive { ($method:ident ( $ty:ty )) => { fn $method(self, value: $ty) -> Result { self.ser.write_wrapped(self.key, |ser| ser.$method(value)) } }; } //////////////////////////////////////////////////////////////////////////////////////////////////// /// A serializer used to serialize element with specified name. Unlike the [`ContentSerializer`], /// this serializer never uses variant names of enum variants, and because of that /// it is unable to serialize any enum values, except unit variants. /// /// Returns the classification of the last written type. /// /// This serializer is used for an ordinary fields in structs, which are not special /// fields named `$text` ([`TEXT_KEY`]) or `$value` ([`VALUE_KEY`]). `$text` field /// should be serialized using [`SimpleTypeSerializer`] and `$value` field should be /// serialized using [`ContentSerializer`]. /// /// This serializer does the following: /// - numbers converted to a decimal representation and serialized as `value`; /// - booleans serialized ether as `true` or `false`; /// - strings and characters are serialized as `value`. In particular, /// an empty string is serialized as ``; /// - `None` is serialized as ``; /// - `Some` and newtypes are serialized as an inner type using the same serializer; /// - units (`()`) and unit structs are serialized as ``; /// - sequences, tuples and tuple structs are serialized as repeated `` tag. /// In particular, empty sequence is serialized to nothing; /// - structs are serialized as a sequence of fields wrapped in a `` tag. Each /// field is serialized recursively using either `ElementSerializer`, [`ContentSerializer`] /// (`$value` fields), or [`SimpleTypeSerializer`] (`$text` fields). /// In particular, the empty struct is serialized as ``; /// - maps are serialized as a sequence of entries wrapped in a `` tag. If key is /// serialized to a special name, the same rules as for struct fields are applied. /// In particular, the empty map is serialized as ``; /// - enums: /// - unit variants are serialized as `variant`; /// - other variants are not supported ([`SeError::Unsupported`] is returned); /// /// Usage of empty tags depends on the [`ContentSerializer::expand_empty_elements`] setting. pub struct ElementSerializer<'w, 'k, W: Write> { /// The inner serializer that contains the settings and mostly do the actual work pub ser: ContentSerializer<'w, 'k, W>, /// Tag name used to wrap serialized types except enum variants which uses the variant name pub(super) key: XmlName<'k>, } impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> { type Ok = WriteResult; type Error = SeError; type SerializeSeq = Self; type SerializeTuple = Self; type SerializeTupleStruct = Self; type SerializeTupleVariant = Impossible; type SerializeMap = Map<'w, 'k, W>; type SerializeStruct = Struct<'w, 'k, W>; type SerializeStructVariant = Struct<'w, 'k, W>; write_primitive!(serialize_bool(bool)); 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)); write_primitive!(serialize_char(char)); write_primitive!(serialize_bytes(&[u8])); fn serialize_str(self, value: &str) -> Result { if value.is_empty() { self.ser.write_empty(self.key) } else { self.ser .write_wrapped(self.key, |ser| ser.serialize_str(value)) } } /// By serde contract we should serialize key of [`None`] values. If someone /// wants to skip the field entirely, he should use /// `#[serde(skip_serializing_if = "Option::is_none")]`. /// /// In XML when we serialize field, we write field name as: /// - element name, or /// - attribute name /// /// and field value as /// - content of the element, or /// - attribute value /// /// So serialization of `None` works the same as [serialization of `()`](#method.serialize_unit) fn serialize_none(self) -> Result { self.serialize_unit() } fn serialize_some(self, value: &T) -> Result { value.serialize(self) } fn serialize_unit(self) -> Result { self.ser.write_empty(self.key) } fn serialize_unit_struct(self, _name: &'static str) -> Result { self.ser.write_empty(self.key) } /// Writes a tag with name [`Self::key`] and content of unit variant inside. /// If variant is a special `$text` value, then empty tag `` is written. /// Otherwise a `variant` is written. fn serialize_unit_variant( self, name: &'static str, variant_index: u32, variant: &'static str, ) -> Result { if variant == TEXT_KEY { self.ser.write_empty(self.key) } else { self.ser.write_wrapped(self.key, |ser| { ser.serialize_unit_variant(name, variant_index, variant) }) } } fn serialize_newtype_struct( self, _name: &'static str, value: &T, ) -> Result { value.serialize(self) } /// Always returns [`SeError::Unsupported`]. Newtype variants can be serialized /// only in `$value` fields, which is serialized using [`ContentSerializer`]. #[inline] fn serialize_newtype_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, _value: &T, ) -> Result { Err(SeError::Unsupported( format!( "cannot serialize enum newtype variant `{}::{}`", name, variant ) .into(), )) } #[inline] fn serialize_seq(self, _len: Option) -> Result { Ok(self) } #[inline] fn serialize_tuple(self, len: usize) -> Result { self.serialize_seq(Some(len)) } #[inline] fn serialize_tuple_struct( self, _name: &'static str, len: usize, ) -> Result { self.serialize_tuple(len) } /// Always returns [`SeError::Unsupported`]. Tuple variants can be serialized /// only in `$value` fields, which is serialized using [`ContentSerializer`]. #[inline] fn serialize_tuple_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, _len: usize, ) -> Result { Err(SeError::Unsupported( format!( "cannot serialize enum tuple variant `{}::{}`", name, variant ) .into(), )) } fn serialize_map(self, _len: Option) -> Result { Ok(Map { ser: self.serialize_struct("", 0)?, key: None, }) } #[inline] fn serialize_struct( mut self, _name: &'static str, _len: usize, ) -> Result { self.ser.write_indent()?; self.ser.indent.increase(); self.ser.writer.write_char('<')?; self.ser.writer.write_str(self.key.0)?; Ok(Struct { ser: self, children: String::new(), write_indent: true, }) } /// Always returns [`SeError::Unsupported`]. Struct variants can be serialized /// only in `$value` fields, which is serialized using [`ContentSerializer`]. #[inline] fn serialize_struct_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, _len: usize, ) -> Result { Err(SeError::Unsupported( format!( "cannot serialize enum struct variant `{}::{}`", name, variant ) .into(), )) } } impl<'w, 'k, W: Write> SerializeSeq for ElementSerializer<'w, 'k, W> { type Ok = WriteResult; type Error = SeError; fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { value.serialize(ElementSerializer { ser: self.ser.new_seq_element_serializer(true), key: self.key, })?; // Write indent for the next element self.ser.write_indent = true; Ok(()) } #[inline] fn end(self) -> Result { Ok(WriteResult::Element) } } impl<'w, 'k, W: Write> SerializeTuple for ElementSerializer<'w, 'k, W> { type Ok = WriteResult; type Error = SeError; #[inline] fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { SerializeSeq::serialize_element(self, value) } #[inline] fn end(self) -> Result { SerializeSeq::end(self) } } impl<'w, 'k, W: Write> SerializeTupleStruct for ElementSerializer<'w, 'k, W> { type Ok = WriteResult; type Error = SeError; #[inline] fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { SerializeSeq::serialize_element(self, value) } #[inline] fn end(self) -> Result { SerializeSeq::end(self) } } //////////////////////////////////////////////////////////////////////////////////////////////////// /// A serializer for tuple variants. Tuples can be serialized in two modes: /// - wrapping each tuple field into a tag /// - without wrapping, fields are delimited by a space pub enum Tuple<'w, 'k, W: Write> { /// Serialize each tuple field as an element Element(ElementSerializer<'w, 'k, W>), /// Serialize tuple as an `xs:list`: space-delimited content of fields Text(SimpleSeq<&'w mut W>), } impl<'w, 'k, W: Write> SerializeTupleVariant for Tuple<'w, 'k, W> { type Ok = WriteResult; type Error = SeError; #[inline] fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { match self { Self::Element(ser) => SerializeTuple::serialize_element(ser, value), Self::Text(ser) => SerializeTuple::serialize_element(ser, value), } } #[inline] fn end(self) -> Result { match self { Self::Element(ser) => SerializeTuple::end(ser), // Do not write indent after `$text` fields because it may be interpreted as // part of content when deserialize Self::Text(ser) => SerializeTuple::end(ser).map(|_| WriteResult::SensitiveText), } } } //////////////////////////////////////////////////////////////////////////////////////////////////// /// A serializer for struct variants, which serializes the struct contents inside /// of wrapping tags (`<${tag}>...`). /// /// Returns the classification of the last written type. /// /// Serialization of each field depends on it representation: /// - attributes written directly to the higher serializer /// - elements buffered into internal buffer and at the end written into higher /// serializer pub struct Struct<'w, 'k, W: Write> { ser: ElementSerializer<'w, 'k, W>, /// Buffer to store serialized elements // TODO: Customization point: allow direct writing of elements, but all // attributes should be listed first. Fail, if attribute encountered after // element. Use feature to configure children: String, /// Whether need to write indent after the last written field write_indent: bool, } impl<'w, 'k, W: Write> Struct<'w, 'k, W> { #[inline] fn write_field(&mut self, key: &str, value: &T) -> Result<(), SeError> where T: ?Sized + Serialize, { //TODO: Customization point: allow user to determine if field is attribute or not if let Some(key) = key.strip_prefix('@') { let key = XmlName::try_from(key)?; self.write_attribute(key, value) } else { self.write_element(key, value) } } /// Writes `value` as an attribute #[inline] fn write_attribute(&mut self, key: XmlName, value: &T) -> Result<(), SeError> where T: ?Sized + Serialize, { //TODO: Customization point: each attribute on new line self.ser.ser.writer.write_char(' ')?; self.ser.ser.writer.write_str(key.0)?; self.ser.ser.writer.write_char('=')?; //TODO: Customization point: preferred quote style self.ser.ser.writer.write_char('"')?; value.serialize(SimpleTypeSerializer { writer: &mut self.ser.ser.writer, target: QuoteTarget::DoubleQAttr, level: self.ser.ser.level, })?; self.ser.ser.writer.write_char('"')?; Ok(()) } /// Writes `value` either as a text content, or as an element. /// /// If `key` has a magic value [`TEXT_KEY`], then `value` serialized as a /// [simple type]. /// /// If `key` has a magic value [`VALUE_KEY`], then `value` serialized as a /// [content] without wrapping in tags, otherwise it is wrapped in /// `<${key}>...`. /// /// [simple type]: SimpleTypeSerializer /// [content]: ContentSerializer fn write_element(&mut self, key: &str, value: &T) -> Result<(), SeError> where T: ?Sized + Serialize, { let ser = ContentSerializer { writer: &mut self.children, level: self.ser.ser.level, indent: self.ser.ser.indent.borrow(), // If previous field does not require indent, do not write it write_indent: self.write_indent, allow_primitive: true, expand_empty_elements: self.ser.ser.expand_empty_elements, }; if key == TEXT_KEY { value.serialize(TextSerializer(ser.into_simple_type_serializer()?))?; // Text was written so we don't need to indent next field self.write_indent = false; } else if key == VALUE_KEY { // If element was written then we need to indent next field unless it is a text field self.write_indent = value.serialize(ser)?.allow_indent(); } else { value.serialize(ElementSerializer { key: XmlName::try_from(key)?, ser, })?; // Element was written so we need to indent next field unless it is a text field self.write_indent = true; } Ok(()) } } impl<'w, 'k, W: Write> SerializeStruct for Struct<'w, 'k, W> { type Ok = WriteResult; type Error = SeError; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { self.write_field(key, value) } fn end(mut self) -> Result { self.ser.ser.indent.decrease(); if self.children.is_empty() { if self.ser.ser.expand_empty_elements { self.ser.ser.writer.write_str(">')?; } else { self.ser.ser.writer.write_str("/>")?; } } else { self.ser.ser.writer.write_char('>')?; self.ser.ser.writer.write_str(&self.children)?; if self.write_indent { self.ser.ser.indent.write_indent(&mut self.ser.ser.writer)?; } self.ser.ser.writer.write_str("')?; } Ok(WriteResult::Element) } } impl<'w, 'k, W: Write> SerializeStructVariant for Struct<'w, 'k, W> { type Ok = WriteResult; type Error = SeError; #[inline] fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { SerializeStruct::serialize_field(self, key, value) } #[inline] fn end(self) -> Result { SerializeStruct::end(self) } } //////////////////////////////////////////////////////////////////////////////////////////////////// pub struct Map<'w, 'k, W: Write> { ser: Struct<'w, 'k, W>, /// Key, serialized by `QNameSerializer` if consumer uses `serialize_key` + /// `serialize_value` calls instead of `serialize_entry` key: Option, } impl<'w, 'k, W: Write> Map<'w, 'k, W> { fn make_key(&mut self, key: &T) -> Result where T: ?Sized + Serialize, { key.serialize(QNameSerializer { writer: String::new(), }) } } impl<'w, 'k, W: Write> SerializeMap for Map<'w, 'k, W> { type Ok = WriteResult; type Error = SeError; fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { if let Some(_) = self.key.take() { return Err(SeError::Custom( "calling `serialize_key` twice without `serialize_value`".to_string(), )); } self.key = Some(self.make_key(key)?); Ok(()) } fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { if let Some(key) = self.key.take() { return self.ser.write_field(&key, value); } Err(SeError::Custom( "calling `serialize_value` without call of `serialize_key`".to_string(), )) } fn serialize_entry(&mut self, key: &K, value: &V) -> Result<(), Self::Error> where K: ?Sized + Serialize, V: ?Sized + Serialize, { let key = self.make_key(key)?; self.ser.write_field(&key, value) } fn end(mut self) -> Result { if let Some(key) = self.key.take() { return Err(SeError::Custom(format!( "calling `end` without call of `serialize_value` for key `{key}`" ))); } SerializeStruct::end(self.ser) } } //////////////////////////////////////////////////////////////////////////////////////////////////// #[cfg(test)] mod tests { use super::*; use crate::se::content::tests::*; use crate::se::{Indent, QuoteLevel}; use crate::utils::Bytes; use serde::Serialize; use std::collections::BTreeMap; #[derive(Debug, Serialize, PartialEq)] struct OptionalElements { a: Option<&'static str>, #[serde(skip_serializing_if = "Option::is_none")] b: Option<&'static str>, } #[derive(Debug, Serialize, PartialEq)] struct OptionalAttributes { #[serde(rename = "@a")] a: Option<&'static str>, #[serde(rename = "@b")] #[serde(skip_serializing_if = "Option::is_none")] b: Option<&'static str>, } mod without_indent { use super::*; use crate::se::content::tests::Struct; use pretty_assertions::assert_eq; /// Checks that given `$data` successfully serialized as `$expected` macro_rules! serialize_as { ($name:ident: $data:expr => $expected:expr) => { #[test] fn $name() { let mut buffer = String::new(); let ser = ElementSerializer { ser: ContentSerializer { writer: &mut buffer, level: QuoteLevel::Full, indent: Indent::None, write_indent: false, allow_primitive: true, expand_empty_elements: false, }, key: XmlName("root"), }; let result = $data.serialize(ser).unwrap(); assert_eq!(buffer, $expected); assert_eq!(result, WriteResult::Element); } }; } /// 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 = ElementSerializer { ser: ContentSerializer { writer: &mut buffer, level: QuoteLevel::Full, indent: Indent::None, write_indent: false, allow_primitive: true, expand_empty_elements: false, }, key: XmlName("root"), }; match $data.serialize(ser).unwrap_err() { SeError::$kind(e) => assert_eq!(e, $reason), e => panic!( "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e ), } // We can write something before fail // 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_non_escaped: "non-escaped string" => "non-escaped string"); serialize_as!(str_escaped: "<\"escaped & string'>" => "<"escaped & 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"); serialize_as!(option_some_empty_str: Some("") => ""); serialize_as!(unit: () => ""); serialize_as!(unit_struct: 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(42) => "42"); err!(enum_newtype: Enum::Newtype(42) => Unsupported("cannot serialize enum newtype variant `Enum::Newtype`")); serialize_as!(seq: vec![1, 2, 3] => "1\ 2\ 3"); serialize_as!(seq_empty: Vec::::new() => ""); serialize_as!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) => "<"&'>\ with\t\n\r spaces\ 3"); serialize_as!(tuple_struct: Tuple("first", 42) => "first\ 42"); err!(enum_tuple: Enum::Tuple("first", 42) => Unsupported("cannot serialize enum tuple variant `Enum::Tuple`")); serialize_as!(map: BTreeMap::from([("_1", 2), ("_3", 4)]) => "\ <_1>2\ <_3>4\ "); serialize_as!(struct_: Struct { key: "answer", val: (42, 42) } => "\ answer\ 42\ 42\ "); err!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) } => Unsupported("cannot serialize enum struct variant `Enum::Struct`")); /// Special field name `$text` should be serialized as text content. /// Sequences serialized as an `xs:list` content mod text_field { use super::*; /// `$text` key in a map mod map { use super::*; use pretty_assertions::assert_eq; macro_rules! text { ($name:ident: $data:expr) => { serialize_as!($name: BTreeMap::from([("$text", $data)]) => ""); }; ($name:ident: $data:expr => $expected:literal) => { serialize_as!($name: BTreeMap::from([("$text", $data)]) => concat!("", $expected,"")); }; } text!(false_: false => "false"); text!(true_: true => "true"); text!(i8_: -42i8 => "-42"); text!(i16_: -4200i16 => "-4200"); text!(i32_: -42000000i32 => "-42000000"); text!(i64_: -42000000000000i64 => "-42000000000000"); text!(isize_: -42000000000000isize => "-42000000000000"); text!(u8_: 42u8 => "42"); text!(u16_: 4200u16 => "4200"); text!(u32_: 42000000u32 => "42000000"); text!(u64_: 42000000000000u64 => "42000000000000"); text!(usize_: 42000000000000usize => "42000000000000"); serde_if_integer128! { text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); } text!(f32_: 4.2f32 => "4.2"); text!(f64_: 4.2f64 => "4.2"); text!(char_non_escaped: 'h' => "h"); text!(char_lt: '<' => "<"); text!(char_gt: '>' => ">"); text!(char_amp: '&' => "&"); text!(char_apos: '\'' => "'"); text!(char_quot: '"' => """); text!(char_space: ' ' => " "); text!(str_non_escaped: "non-escaped string" => "non-escaped string"); text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); err!(bytes: Text { before: "answer", content: Bytes(b"<\"escaped & bytes'>"), after: "answer", } => Unsupported("`serialize_bytes` not supported yet")); text!(option_none: Option::<&str>::None); text!(option_some: Some("non-escaped string") => "non-escaped string"); text!(option_some_empty_str: Some("")); text!(unit: ()); text!(unit_struct: Unit); text!(unit_struct_escaped: UnitEscaped); text!(enum_unit: Enum::Unit => "Unit"); text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>"); text!(newtype: Newtype(42) => "42"); // We have no space where name of a variant can be stored err!(enum_newtype: Text { before: "answer", content: Enum::Newtype(42), after: "answer", } => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value")); // Sequences are serialized separated by spaces, all spaces inside are escaped text!(seq: vec![1, 2, 3] => "1 2 3"); text!(seq_empty: Vec::::new()); text!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) => "<"&'> \ with spaces \ 3"); text!(tuple_struct: Tuple("first", 42) => "first 42"); // We have no space where name of a variant can be stored err!(enum_tuple: Text { before: "answer", content: Enum::Tuple("first", 42), after: "answer", } => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); // Complex types cannot be serialized in `$text` field err!(map: Text { before: "answer", content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } => Unsupported("cannot serialize map as text content value")); err!(struct_: Text { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } => Unsupported("cannot serialize struct `Struct` as text content value")); err!(enum_struct: Text { before: "answer", content: Enum::Struct { key: "answer", val: (42, 42) }, after: "answer", } => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); } /// `$text` field inside a struct mod struct_ { use super::*; use pretty_assertions::assert_eq; macro_rules! text { ($name:ident: $data:expr => $expected:literal) => { serialize_as!($name: Text { before: "answer", content: $data, after: "answer", } => concat!( "answer", $expected, "answer", )); }; } text!(false_: false => "false"); text!(true_: true => "true"); text!(i8_: -42i8 => "-42"); text!(i16_: -4200i16 => "-4200"); text!(i32_: -42000000i32 => "-42000000"); text!(i64_: -42000000000000i64 => "-42000000000000"); text!(isize_: -42000000000000isize => "-42000000000000"); text!(u8_: 42u8 => "42"); text!(u16_: 4200u16 => "4200"); text!(u32_: 42000000u32 => "42000000"); text!(u64_: 42000000000000u64 => "42000000000000"); text!(usize_: 42000000000000usize => "42000000000000"); serde_if_integer128! { text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); } text!(f32_: 4.2f32 => "4.2"); text!(f64_: 4.2f64 => "4.2"); text!(char_non_escaped: 'h' => "h"); text!(char_lt: '<' => "<"); text!(char_gt: '>' => ">"); text!(char_amp: '&' => "&"); text!(char_apos: '\'' => "'"); text!(char_quot: '"' => """); text!(char_space: ' ' => " "); text!(str_non_escaped: "non-escaped string" => "non-escaped string"); text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); err!(bytes: Text { before: "answer", content: Bytes(b"<\"escaped & bytes'>"), after: "answer", } => Unsupported("`serialize_bytes` not supported yet")); text!(option_none: Option::<&str>::None => ""); text!(option_some: Some("non-escaped string") => "non-escaped string"); text!(option_some_empty_str: Some("") => ""); text!(unit: () => ""); text!(unit_struct: Unit => ""); text!(unit_struct_escaped: UnitEscaped => ""); text!(enum_unit: Enum::Unit => "Unit"); text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>"); text!(newtype: Newtype(42) => "42"); // We have no space where name of a variant can be stored err!(enum_newtype: Text { before: "answer", content: Enum::Newtype(42), after: "answer", } => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value")); // Sequences are serialized separated by spaces, all spaces inside are escaped text!(seq: vec![1, 2, 3] => "1 2 3"); text!(seq_empty: Vec::::new() => ""); text!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) => "<"&'> \ with spaces \ 3"); text!(tuple_struct: Tuple("first", 42) => "first 42"); // We have no space where name of a variant can be stored err!(enum_tuple: Text { before: "answer", content: Enum::Tuple("first", 42), after: "answer", } => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); // Complex types cannot be serialized in `$text` field err!(map: Text { before: "answer", content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } => Unsupported("cannot serialize map as text content value")); err!(struct_: Text { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } => Unsupported("cannot serialize struct `Struct` as text content value")); err!(enum_struct: Text { before: "answer", content: Enum::Struct { key: "answer", val: (42, 42) }, after: "answer", } => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); } } /// Special field name `$value` should be serialized using name, provided /// by the type of value instead of a key. Sequences serialized as a list /// of tags with that name (each element can have their own name) mod value_field { use super::*; /// `$value` key in a map mod map { use super::*; use pretty_assertions::assert_eq; macro_rules! value { ($name:ident: $data:expr) => { serialize_as!($name: BTreeMap::from([("$value", $data)]) => ""); }; ($name:ident: $data:expr => $expected:literal) => { serialize_as!($name: BTreeMap::from([("$value", $data)]) => concat!("", $expected,"")); }; } value!(false_: false => "false"); value!(true_: true => "true"); value!(i8_: -42i8 => "-42"); value!(i16_: -4200i16 => "-4200"); value!(i32_: -42000000i32 => "-42000000"); value!(i64_: -42000000000000i64 => "-42000000000000"); value!(isize_: -42000000000000isize => "-42000000000000"); value!(u8_: 42u8 => "42"); value!(u16_: 4200u16 => "4200"); value!(u32_: 42000000u32 => "42000000"); value!(u64_: 42000000000000u64 => "42000000000000"); value!(usize_: 42000000000000usize => "42000000000000"); serde_if_integer128! { value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); } value!(f32_: 4.2f32 => "4.2"); value!(f64_: 4.2f64 => "4.2"); value!(char_non_escaped: 'h' => "h"); value!(char_lt: '<' => "<"); value!(char_gt: '>' => ">"); value!(char_amp: '&' => "&"); value!(char_apos: '\'' => "'"); value!(char_quot: '"' => """); value!(char_space: ' ' => " "); value!(str_non_escaped: "non-escaped string" => "non-escaped string"); value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); err!(bytes: BTreeMap::from([("$value", Bytes(b"<\"escaped & bytes'>"))]) => Unsupported("`serialize_bytes` not supported yet")); value!(option_none: Option::<&str>::None); value!(option_some: Some("non-escaped string") => "non-escaped string"); value!(option_some_empty_str: Some("")); value!(unit: ()); value!(unit_struct: Unit); value!(unit_struct_escaped: UnitEscaped); value!(enum_unit: Enum::Unit => ""); err!(enum_unit_escaped: BTreeMap::from([("$value", Enum::UnitEscaped)]) => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`")); value!(newtype: Newtype(42) => "42"); value!(enum_newtype: Enum::Newtype(42) => "42"); // Note that sequences of primitives serialized without delimiters! err!(seq: BTreeMap::from([("$value", vec![1, 2, 3])]) => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); value!(seq_empty: Vec::::new()); err!(tuple: BTreeMap::from([("$value", ("<\"&'>", "with\t\n\r spaces", 3usize))]) => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); err!(tuple_struct: BTreeMap::from([("$value", Tuple("first", 42))]) => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); value!(enum_tuple: Enum::Tuple("first", 42) => "first\ 42"); // We cannot wrap map or struct in any container and should not // flatten it, so it is impossible to serialize maps and structs err!(map: BTreeMap::from([("$value", BTreeMap::from([("_1", 2), ("_3", 4)]))]) => Unsupported("serialization of map types is not supported in `$value` field")); err!(struct_: BTreeMap::from([("$value", Struct { key: "answer", val: (42, 42) })]) => Unsupported("serialization of struct `Struct` is not supported in `$value` field")); value!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) } => "\ answer\ 42\ 42\ "); } /// `$value` field inside a struct mod struct_ { use super::*; use pretty_assertions::assert_eq; macro_rules! value { ($name:ident: $data:expr => $expected:literal) => { serialize_as!($name: Value { before: "answer", content: $data, after: "answer", } => concat!( "answer", $expected, "answer", )); }; } value!(false_: false => "false"); value!(true_: true => "true"); value!(i8_: -42i8 => "-42"); value!(i16_: -4200i16 => "-4200"); value!(i32_: -42000000i32 => "-42000000"); value!(i64_: -42000000000000i64 => "-42000000000000"); value!(isize_: -42000000000000isize => "-42000000000000"); value!(u8_: 42u8 => "42"); value!(u16_: 4200u16 => "4200"); value!(u32_: 42000000u32 => "42000000"); value!(u64_: 42000000000000u64 => "42000000000000"); value!(usize_: 42000000000000usize => "42000000000000"); serde_if_integer128! { value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); } value!(f32_: 4.2f32 => "4.2"); value!(f64_: 4.2f64 => "4.2"); value!(char_non_escaped: 'h' => "h"); value!(char_lt: '<' => "<"); value!(char_gt: '>' => ">"); value!(char_amp: '&' => "&"); value!(char_apos: '\'' => "'"); value!(char_quot: '"' => """); value!(char_space: ' ' => " "); value!(str_non_escaped: "non-escaped string" => "non-escaped string"); value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); err!(bytes: Value { before: "answer", content: Bytes(b"<\"escaped & bytes'>"), after: "answer", } => Unsupported("`serialize_bytes` not supported yet")); value!(option_none: Option::<&str>::None => ""); value!(option_some: Some("non-escaped string") => "non-escaped string"); value!(option_some_empty_str: Some("") => ""); value!(unit: () => ""); value!(unit_struct: Unit => ""); value!(unit_struct_escaped: UnitEscaped => ""); value!(enum_unit: Enum::Unit => ""); err!(enum_unit_escaped: Value { before: "answer", content: Enum::UnitEscaped, after: "answer", } => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`")); value!(newtype: Newtype(42) => "42"); value!(enum_newtype: Enum::Newtype(42) => "42"); // Note that sequences of primitives serialized without delimiters! err!(seq: Value { before: "answer", content: vec![1, 2, 3], after: "answer", } => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); value!(seq_empty: Vec::::new() => ""); err!(tuple: Value { before: "answer", content: ("<\"&'>", "with\t\n\r spaces", 3usize), after: "answer", } => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); err!(tuple_struct: Value { before: "answer", content: Tuple("first", 42), after: "answer", } => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); value!(enum_tuple: Enum::Tuple("first", 42) => "first\ 42"); // We cannot wrap map or struct in any container and should not // flatten it, so it is impossible to serialize maps and structs err!(map: Value { before: "answer", content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } => Unsupported("serialization of map types is not supported in `$value` field")); err!(struct_: Value { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } => Unsupported("serialization of struct `Struct` is not supported in `$value` field")); value!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) } => "\ answer\ 42\ 42\ "); } } mod attributes { use super::*; use pretty_assertions::assert_eq; serialize_as!(map_attr: BTreeMap::from([("@key1", 1), ("@key2", 2)]) => r#""#); serialize_as!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)]) => r#"2"#); serialize_as!(struct_: Attributes { key: "answer", val: (42, 42) } => r#""#); serialize_as!(struct_before: AttributesBefore { key: "answer", val: 42 } => r#"42"#); serialize_as!(struct_after: AttributesAfter { key: "answer", val: 42 } => r#"answer"#); err!(enum_: Enum::Attributes { key: "answer", val: (42, 42) } => Unsupported("cannot serialize enum struct variant `Enum::Attributes`")); /// Test for https://github.com/tafia/quick-xml/issues/252 mod optional { use super::*; use pretty_assertions::assert_eq; serialize_as!(none: OptionalAttributes { a: None, b: None } => r#""#); serialize_as!(some_empty_str: OptionalAttributes { a: Some(""), b: Some(""), } => r#""#); serialize_as!(some_non_empty: OptionalAttributes { a: Some("1"), b: Some("2"), } => r#""#); } } /// Test for https://github.com/tafia/quick-xml/issues/252 mod optional { use super::*; use pretty_assertions::assert_eq; serialize_as!(none: OptionalElements { a: None, b: None } => "\ \ "); serialize_as!(some_empty_str: OptionalElements { a: Some(""), b: Some(""), } => "\ \ \ "); serialize_as!(some_non_empty: OptionalElements { a: Some("1"), b: Some("2"), } => "\ 1\ 2\ "); } } mod with_indent { use super::*; use crate::se::content::tests::Struct; use crate::writer::Indentation; use pretty_assertions::assert_eq; /// Checks that given `$data` successfully serialized as `$expected`. /// Writes `$data` using [`ElementSerializer`] with indent of two spaces. macro_rules! serialize_as { ($name:ident: $data:expr => $expected:expr) => { #[test] fn $name() { let mut buffer = String::new(); let ser = ElementSerializer { ser: ContentSerializer { writer: &mut buffer, level: QuoteLevel::Full, indent: Indent::Owned(Indentation::new(b' ', 2)), write_indent: false, allow_primitive: true, expand_empty_elements: false, }, key: XmlName("root"), }; let result = $data.serialize(ser).unwrap(); assert_eq!(buffer, $expected); assert_eq!(result, WriteResult::Element); } }; } /// 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 = ElementSerializer { ser: ContentSerializer { writer: &mut buffer, level: QuoteLevel::Full, indent: Indent::Owned(Indentation::new(b' ', 2)), write_indent: false, allow_primitive: true, expand_empty_elements: false, }, key: XmlName("root"), }; match $data.serialize(ser).unwrap_err() { SeError::$kind(e) => assert_eq!(e, $reason), e => panic!( "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e ), } // We can write something before fail // 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!(char_space: ' ' => " "); serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string"); serialize_as!(str_escaped: "<\"escaped & string'>" => "<"escaped & 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"); serialize_as!(option_some_empty: Some("") => ""); serialize_as!(unit: () => ""); serialize_as!(unit_struct: 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(42) => "42"); err!(enum_newtype: Enum::Newtype(42) => Unsupported("cannot serialize enum newtype variant `Enum::Newtype`")); serialize_as!(seq: vec![1, 2, 3] => "1\n\ 2\n\ 3"); serialize_as!(seq_empty: Vec::::new() => ""); serialize_as!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) => "<"&'>\n\ with\t\n\r spaces\n\ 3"); serialize_as!(tuple_struct: Tuple("first", 42) => "first\n\ 42"); err!(enum_tuple: Enum::Tuple("first", 42) => Unsupported("cannot serialize enum tuple variant `Enum::Tuple`")); serialize_as!(map: BTreeMap::from([("_1", 2), ("_3", 4)]) => "\n \ <_1>2\n \ <_3>4\n\ "); serialize_as!(struct_: Struct { key: "answer", val: (42, 42) } => "\n \ answer\n \ 42\n \ 42\n\ "); err!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) } => Unsupported("cannot serialize enum struct variant `Enum::Struct`")); /// Special field name `$text` should be serialized as text content. /// Sequences serialized as an `xs:list` content mod text_field { use super::*; /// `$text` key in a map mod map { use super::*; use pretty_assertions::assert_eq; macro_rules! text { ($name:ident: $data:expr) => { serialize_as!($name: // Serialization started from ElementSerializer::serialize_map BTreeMap::from([("$text", $data)]) => ""); }; ($name:ident: $data:expr => $expected:literal) => { serialize_as!($name: // Serialization started from ElementSerializer::serialize_map BTreeMap::from([("$text", $data)]) => concat!("", $expected,"")); }; } text!(false_: false => "false"); text!(true_: true => "true"); text!(i8_: -42i8 => "-42"); text!(i16_: -4200i16 => "-4200"); text!(i32_: -42000000i32 => "-42000000"); text!(i64_: -42000000000000i64 => "-42000000000000"); text!(isize_: -42000000000000isize => "-42000000000000"); text!(u8_: 42u8 => "42"); text!(u16_: 4200u16 => "4200"); text!(u32_: 42000000u32 => "42000000"); text!(u64_: 42000000000000u64 => "42000000000000"); text!(usize_: 42000000000000usize => "42000000000000"); serde_if_integer128! { text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); } text!(f32_: 4.2f32 => "4.2"); text!(f64_: 4.2f64 => "4.2"); text!(char_non_escaped: 'h' => "h"); text!(char_lt: '<' => "<"); text!(char_gt: '>' => ">"); text!(char_amp: '&' => "&"); text!(char_apos: '\'' => "'"); text!(char_quot: '"' => """); text!(char_space: ' ' => " "); text!(str_non_escaped: "non-escaped string" => "non-escaped string"); text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); err!(bytes: Text { before: "answer", content: Bytes(b"<\"escaped & bytes'>"), after: "answer", } => Unsupported("`serialize_bytes` not supported yet")); text!(option_none: Option::<&str>::None); text!(option_some: Some("non-escaped string") => "non-escaped string"); text!(option_some_empty_str: Some("")); text!(unit: ()); text!(unit_struct: Unit); text!(unit_struct_escaped: UnitEscaped); text!(enum_unit: Enum::Unit => "Unit"); text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>"); text!(newtype: Newtype(42) => "42"); // We have no space where name of a variant can be stored err!(enum_newtype: Text { before: "answer", content: Enum::Newtype(42), after: "answer", } => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value")); // Sequences are serialized separated by spaces, all spaces inside are escaped text!(seq: vec![1, 2, 3] => "1 2 3"); text!(seq_empty: Vec::::new()); text!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) => "<"&'> \ with spaces \ 3"); text!(tuple_struct: Tuple("first", 42) => "first 42"); // We have no space where name of a variant can be stored err!(enum_tuple: Text { before: "answer", content: Enum::Tuple("first", 42), after: "answer", } => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); // Complex types cannot be serialized in `$text` field err!(map: Text { before: "answer", content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } => Unsupported("cannot serialize map as text content value")); err!(struct_: Text { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } => Unsupported("cannot serialize struct `Struct` as text content value")); err!(enum_struct: Text { before: "answer", content: Enum::Struct { key: "answer", val: (42, 42) }, after: "answer", } => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); } /// `$text` field inside a struct mod struct_ { use super::*; use pretty_assertions::assert_eq; macro_rules! text { ($name:ident: $data:expr => $expected:literal) => { serialize_as!($name: // Serialization started from ElementSerializer::serialize_struct Text { before: "answer", content: $data, after: "answer", } => concat!( "\n answer", $expected, "answer\n", )); }; } text!(false_: false => "false"); text!(true_: true => "true"); text!(i8_: -42i8 => "-42"); text!(i16_: -4200i16 => "-4200"); text!(i32_: -42000000i32 => "-42000000"); text!(i64_: -42000000000000i64 => "-42000000000000"); text!(isize_: -42000000000000isize => "-42000000000000"); text!(u8_: 42u8 => "42"); text!(u16_: 4200u16 => "4200"); text!(u32_: 42000000u32 => "42000000"); text!(u64_: 42000000000000u64 => "42000000000000"); text!(usize_: 42000000000000usize => "42000000000000"); serde_if_integer128! { text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); } text!(f32_: 4.2f32 => "4.2"); text!(f64_: 4.2f64 => "4.2"); text!(char_non_escaped: 'h' => "h"); text!(char_lt: '<' => "<"); text!(char_gt: '>' => ">"); text!(char_amp: '&' => "&"); text!(char_apos: '\'' => "'"); text!(char_quot: '"' => """); text!(char_space: ' ' => " "); text!(str_non_escaped: "non-escaped string" => "non-escaped string"); text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); err!(bytes: Text { before: "answer", content: Bytes(b"<\"escaped & bytes'>"), after: "answer", } => Unsupported("`serialize_bytes` not supported yet")); text!(option_none: Option::<&str>::None => ""); text!(option_some: Some("non-escaped string") => "non-escaped string"); text!(option_some_empty_str: Some("") => ""); text!(unit: () => ""); text!(unit_struct: Unit => ""); text!(unit_struct_escaped: UnitEscaped => ""); text!(enum_unit: Enum::Unit => "Unit"); text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>"); text!(newtype: Newtype(42) => "42"); // We have no space where name of a variant can be stored err!(enum_newtype: Text { before: "answer", content: Enum::Newtype(42), after: "answer", } => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value")); // Sequences are serialized separated by spaces, all spaces inside are escaped text!(seq: vec![1, 2, 3] => "1 2 3"); text!(seq_empty: Vec::::new() => ""); text!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) => "<"&'> \ with spaces \ 3"); text!(tuple_struct: Tuple("first", 42) => "first 42"); // We have no space where name of a variant can be stored err!(enum_tuple: Text { before: "answer", content: Enum::Tuple("first", 42), after: "answer", } => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); // Complex types cannot be serialized in `$text` field err!(map: Text { before: "answer", content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } => Unsupported("cannot serialize map as text content value")); err!(struct_: Text { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } => Unsupported("cannot serialize struct `Struct` as text content value")); err!(enum_struct: Text { before: "answer", content: Enum::Struct { key: "answer", val: (42, 42) }, after: "answer", } => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); } } /// Special field name `$value` should be serialized using name, provided /// by the type of value instead of a key. Sequences serialized as a list /// of tags with that name (each element can have their own name) mod value_field { use super::*; /// `$value` key in a map mod map { use super::*; use pretty_assertions::assert_eq; macro_rules! value { ($name:ident: $data:expr) => { serialize_as!($name: // Serialization started from ElementSerializer::serialize_map BTreeMap::from([("$value", $data)]) => ""); }; ($name:ident: $data:expr => $expected:literal) => { serialize_as!($name: // Serialization started from ElementSerializer::serialize_map BTreeMap::from([("$value", $data)]) => concat!("", $expected,"")); }; } value!(false_: false => "false"); value!(true_: true => "true"); value!(i8_: -42i8 => "-42"); value!(i16_: -4200i16 => "-4200"); value!(i32_: -42000000i32 => "-42000000"); value!(i64_: -42000000000000i64 => "-42000000000000"); value!(isize_: -42000000000000isize => "-42000000000000"); value!(u8_: 42u8 => "42"); value!(u16_: 4200u16 => "4200"); value!(u32_: 42000000u32 => "42000000"); value!(u64_: 42000000000000u64 => "42000000000000"); value!(usize_: 42000000000000usize => "42000000000000"); serde_if_integer128! { value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); } value!(f32_: 4.2f32 => "4.2"); value!(f64_: 4.2f64 => "4.2"); value!(char_non_escaped: 'h' => "h"); value!(char_lt: '<' => "<"); value!(char_gt: '>' => ">"); value!(char_amp: '&' => "&"); value!(char_apos: '\'' => "'"); value!(char_quot: '"' => """); value!(char_space: ' ' => " "); value!(str_non_escaped: "non-escaped string" => "non-escaped string"); value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); err!(bytes: BTreeMap::from([("$value", Bytes(b"<\"escaped & bytes'>"))]) => Unsupported("`serialize_bytes` not supported yet")); value!(option_none: Option::<&str>::None); value!(option_some: Some("non-escaped string") => "non-escaped string"); value!(option_some_empty_str: Some("")); value!(unit: ()); value!(unit_struct: Unit); value!(unit_struct_escaped: UnitEscaped); value!(enum_unit: Enum::Unit => "\n \n"); err!(enum_unit_escaped: BTreeMap::from([("$value", Enum::UnitEscaped)]) => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`")); value!(newtype: Newtype(42) => "42"); value!(enum_newtype: Enum::Newtype(42) => "\n 42\n"); err!(seq: BTreeMap::from([("$value", vec![1, 2, 3])]) => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); value!(seq_empty: Vec::::new()); err!(tuple: BTreeMap::from([("$value", ("<\"&'>", "with\t\n\r spaces", 3usize))]) => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); err!(tuple_struct: BTreeMap::from([("$value", Tuple("first", 42))]) => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); value!(enum_tuple: Enum::Tuple("first", 42) => "\n \ first\n \ 42\n"); // We cannot wrap map or struct in any container and should not // flatten it, so it is impossible to serialize maps and structs err!(map: BTreeMap::from([("$value", BTreeMap::from([("_1", 2), ("_3", 4)]))]) => Unsupported("serialization of map types is not supported in `$value` field")); err!(struct_: BTreeMap::from([("$value", Struct { key: "answer", val: (42, 42) })]) => Unsupported("serialization of struct `Struct` is not supported in `$value` field")); value!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) } => "\n \ \n \ answer\n \ 42\n \ 42\n \ \n"); } /// `$value` field inside a struct mod struct_ { use super::*; use pretty_assertions::assert_eq; macro_rules! value { ($name:ident: $data:expr => $expected:literal) => { serialize_as!($name: // Serialization started from ElementSerializer::serialize_struct Value { before: "answer", content: $data, after: "answer", } => concat!( "\n answer", $expected, "answer\n", )); }; } value!(false_: false => "false"); value!(true_: true => "true"); value!(i8_: -42i8 => "-42"); value!(i16_: -4200i16 => "-4200"); value!(i32_: -42000000i32 => "-42000000"); value!(i64_: -42000000000000i64 => "-42000000000000"); value!(isize_: -42000000000000isize => "-42000000000000"); value!(u8_: 42u8 => "42"); value!(u16_: 4200u16 => "4200"); value!(u32_: 42000000u32 => "42000000"); value!(u64_: 42000000000000u64 => "42000000000000"); value!(usize_: 42000000000000usize => "42000000000000"); serde_if_integer128! { value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); } value!(f32_: 4.2f32 => "4.2"); value!(f64_: 4.2f64 => "4.2"); value!(char_non_escaped: 'h' => "h"); value!(char_lt: '<' => "<"); value!(char_gt: '>' => ">"); value!(char_amp: '&' => "&"); value!(char_apos: '\'' => "'"); value!(char_quot: '"' => """); value!(char_space: ' ' => " "); value!(str_non_escaped: "non-escaped string" => "non-escaped string"); value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); err!(bytes: Value { before: "answer", content: Bytes(b"<\"escaped & bytes'>"), after: "answer", } => Unsupported("`serialize_bytes` not supported yet")); value!(option_none: Option::<&str>::None => ""); value!(option_some: Some("non-escaped string") => "non-escaped string"); value!(option_some_empty_str: Some("") => ""); value!(unit: () => "\n "); value!(unit_struct: Unit => "\n "); value!(unit_struct_escaped: UnitEscaped => "\n "); value!(enum_unit: Enum::Unit => "\n \n "); err!(enum_unit_escaped: Value { before: "answer", content: Enum::UnitEscaped, after: "answer", } => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`")); value!(newtype: Newtype(42) => "42"); value!(enum_newtype: Enum::Newtype(42) => "\n 42\n "); err!(seq: Value { before: "answer", content: vec![1, 2, 3], after: "answer", } => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); value!(seq_empty: Vec::::new() => ""); err!(tuple: Value { before: "answer", content: ("<\"&'>", "with\t\n\r spaces", 3usize), after: "answer", } => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); err!(tuple_struct: Value { before: "answer", content: Tuple("first", 42), after: "answer", } => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); value!(enum_tuple: Enum::Tuple("first", 42) => "\n \ first\n \ 42\n "); // We cannot wrap map or struct in any container and should not // flatten it, so it is impossible to serialize maps and structs err!(map: Value { before: "answer", content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } => Unsupported("serialization of map types is not supported in `$value` field")); err!(struct_: Value { before: "answer", content: Struct { key: "answer", val: (42, 42) }, after: "answer", } => Unsupported("serialization of struct `Struct` is not supported in `$value` field")); value!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) } => "\n \ \n \ answer\n \ 42\n \ 42\n \ \n "); } } mod attributes { use super::*; use pretty_assertions::assert_eq; serialize_as!(map_attr: BTreeMap::from([("@key1", 1), ("@key2", 2)]) => r#""#); serialize_as!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)]) => "\n \ 2\n\ "); serialize_as!(struct_: Attributes { key: "answer", val: (42, 42) } => r#""#); serialize_as!(struct_before: AttributesBefore { key: "answer", val: 42 } => "\n \ 42\n\ "); serialize_as!(struct_after: AttributesAfter { key: "answer", val: 42 } => "\n \ answer\n\ "); err!(enum_: Enum::Attributes { key: "answer", val: (42, 42) } => Unsupported("cannot serialize enum struct variant `Enum::Attributes`")); /// Test for https://github.com/tafia/quick-xml/issues/252 mod optional { use super::*; use pretty_assertions::assert_eq; serialize_as!(none: OptionalAttributes { a: None, b: None } => r#""#); serialize_as!(some_empty_str: OptionalAttributes { a: Some(""), b: Some("") } => r#""#); serialize_as!(some_non_empty: OptionalAttributes { a: Some("a"), b: Some("b") } => r#""#); } } /// Test for https://github.com/tafia/quick-xml/issues/252 mod optional { use super::*; use pretty_assertions::assert_eq; serialize_as!(none: OptionalElements { a: None, b: None } => "\n \ \n\ "); serialize_as!(some_empty_str: OptionalElements { a: Some(""), b: Some("") } => "\n \ \n \ \n\ "); serialize_as!(some_non_empty: OptionalElements { a: Some("a"), b: Some("b") } => "\n \ a\n \ b\n\ "); } } mod expand_empty_elements { use super::*; use pretty_assertions::assert_eq; /// Checks that given `$data` successfully serialized as `$expected` macro_rules! serialize_as { ($name:ident: $data:expr => $expected:expr) => { #[test] fn $name() { let mut buffer = String::new(); let ser = ElementSerializer { ser: ContentSerializer { writer: &mut buffer, level: QuoteLevel::Full, indent: Indent::None, write_indent: false, allow_primitive: true, expand_empty_elements: true, }, key: XmlName("root"), }; let result = $data.serialize(ser).unwrap(); assert_eq!(buffer, $expected); assert_eq!(result, WriteResult::Element); } }; } serialize_as!(option_some_empty: Some("") => ""); serialize_as!(option_some_empty_str: Some("") => ""); serialize_as!(unit: () => ""); serialize_as!(unit_struct: Unit => ""); serialize_as!(unit_struct_escaped: UnitEscaped => ""); serialize_as!(enum_unit: Enum::Unit => "Unit"); serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>"); } }