From 56246ea7477a1247d849fe160af9956d3808829f Mon Sep 17 00:00:00 2001 From: Egor Larionov Date: Sun, 21 Jul 2024 09:09:16 -0700 Subject: [PATCH] add serializer for binary xml text + tweak binary deserializer Added reading the config to determine trimming options. --- Cargo.toml | 1 + src/de/mod.rs | 72 +- src/se/content.rs | 2 +- src/se/element.rs | 2 +- src/se/io.rs | 1791 +-------------------------------- src/se/io/content.rs | 1447 ++++++++++++++++++++++++++ src/se/io/element.rs | 2064 ++++++++++++++++++++++++++++++++++++++ src/se/io/simple_type.rs | 309 ++++++ src/se/io/text.rs | 185 ++++ src/se/mod.rs | 7 +- src/se/simple_type.rs | 2 +- 11 files changed, 4118 insertions(+), 1764 deletions(-) create mode 100644 src/se/io/content.rs create mode 100644 src/se/io/element.rs create mode 100644 src/se/io/simple_type.rs create mode 100644 src/se/io/text.rs diff --git a/Cargo.toml b/Cargo.toml index a86063ce..e8344fd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ serde = { version = ">=1.0.139", optional = true } tokio = { version = "1.10", optional = true, default-features = false, features = ["io-util"] } memchr = "2.1" arbitrary = { version = "1", features = ["derive"], optional = true } +ref-cast = "1" [dev-dependencies] criterion = "0.4" diff --git a/src/de/mod.rs b/src/de/mod.rs index dee0645e..4186a125 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -2005,7 +2005,7 @@ use crate::{ errors::Error, events::{BytesCData, BytesEnd, BytesStart, BytesText, Event}, name::QName, - reader::Reader, + reader::{Config, Reader}, }; use serde::de::{self, Deserialize, DeserializeOwned, DeserializeSeed, SeqAccess, Visitor}; use std::borrow::Cow; @@ -2169,6 +2169,31 @@ struct XmlReader<'i, R: XmlRead<'i>, E: EntityResolver = PredefinedEntityResolve entity_resolver: E, } +fn trim_cow<'a, F>(value: Cow<'a, str>, trim: F) -> Cow<'a, str> +where + F: FnOnce(&str) -> &str, +{ + match value { + Cow::Borrowed(bytes) => Cow::Borrowed(trim(bytes)), + Cow::Owned(mut bytes) => { + let trimmed = trim(&bytes); + if trimmed.len() != bytes.len() { + bytes = trimmed.to_string(); + } + Cow::Owned(bytes) + } + } +} + +/// Removes trailing XML whitespace bytes from text content. +/// +/// Returns `true` if content is empty after that +fn inplace_trim_end(mut s: &mut Cow) -> bool { + let c: Cow = replace(&mut s, Cow::Borrowed("")); + *s = trim_cow(c, str::trim_end); + s.is_empty() +} + impl<'i, R: XmlRead<'i>, E: EntityResolver> XmlReader<'i, R, E> { fn new(mut reader: R, entity_resolver: E) -> Self { // Lookahead by one event immediately, so we do not need to check in the @@ -2206,20 +2231,23 @@ impl<'i, R: XmlRead<'i>, E: EntityResolver> XmlReader<'i, R, E> { /// Read all consequent [`Text`] and [`CData`] events until non-text event /// occurs. Content of all events would be appended to `result` and returned /// as [`DeEvent::Text`]. + /// + /// If the resulting text empty, this function returns None to avoid creating an empty Event. /// /// [`Text`]: PayloadEvent::Text /// [`CData`]: PayloadEvent::CData - fn drain_text(&mut self, mut result: Cow<'i, str>) -> Result, DeError> { + fn drain_text(&mut self, mut result: Cow<'i, str>) -> Result>, DeError> { loop { if self.current_event_is_last_text() { break; } - match self.next_impl()? { PayloadEvent::Text(mut e) => { if self.current_event_is_last_text() { // FIXME: Actually, we should trim after decoding text, but now we trim before - e.inplace_trim_end(); + if self.reader.config().trim_text_end { + e.inplace_trim_end(); + } } result .to_mut() @@ -2228,10 +2256,12 @@ impl<'i, R: XmlRead<'i>, E: EntityResolver> XmlReader<'i, R, E> { PayloadEvent::CData(e) => result.to_mut().push_str(&e.decode()?), // SAFETY: current_event_is_last_text checks that event is Text or CData - _ => unreachable!("Only `Text` and `CData` events can come here"), + e => { + unreachable!("Only `Text` and `CData` events can come here: {:?}", &e); + } } } - Ok(DeEvent::Text(Text { text: result })) + Ok(Some(DeEvent::Text(Text { text: result }))) } /// Return an input-borrowing event. @@ -2241,17 +2271,24 @@ impl<'i, R: XmlRead<'i>, E: EntityResolver> XmlReader<'i, R, E> { PayloadEvent::Start(e) => Ok(DeEvent::Start(e)), PayloadEvent::End(e) => Ok(DeEvent::End(e)), PayloadEvent::Text(mut e) => { - if self.current_event_is_last_text() && e.inplace_trim_end() { - // FIXME: Actually, we should trim after decoding text, but now we trim before - continue; + if self.current_event_is_last_text() { + if self.reader.config().trim_text_end && e.inplace_trim_end() { + continue; + } } + match e.unescape_with(|entity| self.entity_resolver.resolve(entity)).map(|res| self.drain_text(res)) { - Ok(x) => x, + Ok(Ok(None)) => continue, + Ok(Ok(Some(x))) => Ok(x), + Ok(Err(x)) => Err(x), // failed to escape treat as binary blob. Err(_) => Ok(DeEvent::Binary(Binary { text: e.into_inner() })), } } - PayloadEvent::CData(e) => self.drain_text(e.decode()?), + PayloadEvent::CData(e) => match self.drain_text(e.decode()?).transpose() { + None => continue, + Some(x) => x, + }, PayloadEvent::DocType(e) => { self.entity_resolver .capture(e) @@ -2834,6 +2871,8 @@ where pub fn from_str_with_resolver(source: &'de str, entity_resolver: E) -> Self { let mut reader = Reader::from_str(source); let config = reader.config_mut(); + config.trim_text_start = true; + config.trim_text_end = true; config.expand_empty_elements = true; Self::new( @@ -3135,6 +3174,9 @@ pub trait XmlRead<'i> { /// A copy of the reader's decoder used to decode strings. fn decoder(&self) -> Decoder; + + /// Returns a reference to the reader config. + fn config(&self) -> &Config; } /// XML input source that reads from a std::io input stream. @@ -3204,6 +3246,10 @@ impl<'i, R: BufRead> XmlRead<'i> for IoReader { fn decoder(&self) -> Decoder { self.reader.decoder() } + + fn config(&self) -> &Config{ + self.reader.config() + } } /// XML input source that reads from a slice of bytes and can borrow from it. @@ -3269,6 +3315,10 @@ impl<'de> XmlRead<'de> for SliceReader<'de> { fn decoder(&self) -> Decoder { self.reader.decoder() } + + fn config(&self) -> &Config { + self.reader.config() + } } #[cfg(test)] diff --git a/src/se/content.rs b/src/se/content.rs index 91e02880..31f3e634 100644 --- a/src/se/content.rs +++ b/src/se/content.rs @@ -58,7 +58,7 @@ macro_rules! write_primitive { /// with indent, sequence of strings become one big string with additional content /// and it would be impossible to distinguish between content of the original /// strings and inserted indent characters. -pub struct ContentSerializer<'w, 'i, W: Write> { +pub struct ContentSerializer<'w, 'i, W> { pub writer: &'w mut W, /// Defines which XML characters need to be escaped in text content pub level: QuoteLevel, diff --git a/src/se/element.rs b/src/se/element.rs index 35aa28f4..c9e0d1a9 100644 --- a/src/se/element.rs +++ b/src/se/element.rs @@ -56,7 +56,7 @@ macro_rules! write_primitive { /// - other variants are not supported ([`DeError::Unsupported`] is returned); /// /// Usage of empty tags depends on the [`ContentSerializer::expand_empty_elements`] setting. -pub struct ElementSerializer<'w, 'k, W: Write> { +pub struct ElementSerializer<'w, 'k, W> { /// 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 diff --git a/src/se/io.rs b/src/se/io.rs index 4bbe56e2..95d92c7f 100644 --- a/src/se/io.rs +++ b/src/se/io.rs @@ -1,7 +1,17 @@ #![allow(missing_docs)] //! Serializers to an std::io output stream. +mod simple_type; +mod element; +mod text; +mod content; +use simple_type::*; +use element::*; +use text::*; +use content::*; + +use ref_cast::RefCast; use std::str::from_utf8; use serde::{serde_if_integer128, Serialize}; @@ -14,73 +24,23 @@ use crate::{de::TEXT_KEY, writer::Indentation, DeError}; use super::{simple_type::QuoteTarget, Indent, QuoteLevel, XmlName}; -/// 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(mut self, value: &[u8]) -> Result { - self.writer.write(&value)?; - Ok(self.writer) - } - - fn serialize_none(self) -> Result { - Ok(self.writer) - } - - fn serialize_some(self, value: &T) -> Result { - value.serialize(self) - } - - fn serialize_unit_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - ) -> Result { - self.serialize_str(variant) - } +/// Wrapper for a std::io::Write writer that also implements std::fmt::Write for +/// compatibility with original serializers that work only with +/// std::fmt::Write writers. +#[derive(RefCast)] +#[repr(transparent)] +pub(crate) struct FmtWriter { + pub(crate) writer: W +} - fn serialize_newtype_struct( - self, - _name: &'static str, - value: &T, - ) -> Result { - value.serialize(self) - } - }; +impl std::fmt::Write for FmtWriter { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + self.writer.write(s.as_bytes()).map_err(|_| std::fmt::Error)?; + Ok(()) + } + fn write_char(&mut self, c: char) -> std::fmt::Result { + std::fmt::Write::write_str(self, c.encode_utf8(&mut [0; 4])) + } } pub trait Write: std::io::Write { @@ -91,37 +51,6 @@ pub trait Write: std::io::Write { fn write_char(&mut self, c: char) -> Result<(), DeError> { self.write_str(c.encode_utf8(&mut [0; 4])) } - // fn write_fmt(&mut self, args: Arguments<'_>) -> Result { - // // We use a specialization for `Sized` types to avoid an indirection - // // through `&mut self` - // trait SpecWriteFmt { - // fn spec_write_fmt(self, args: Arguments<'_>) -> Result; - // } - - // impl SpecWriteFmt for &mut W { - // #[inline] - // default fn spec_write_fmt(mut self, args: Arguments<'_>) -> Result { - // if let Some(s) = args.as_statically_known_str() { - // self.write_str(s) - // } else { - // write(&mut self, args) - // } - // } - // } - - // impl SpecWriteFmt for &mut W { - // #[inline] - // fn spec_write_fmt(self, args: Arguments<'_>) -> Result { - // if let Some(s) = args.as_statically_known_str() { - // self.write_str(s) - // } else { - // write(self, args) - // } - // } - // } - - // self.spec_write_fmt(args) - // } } impl Write for W { @@ -155,14 +84,14 @@ macro_rules! forward { }; } -/// An IO Serializer -pub struct Serializer<'w, 'r, W: Write> { +/// A Serializer +pub struct Serializer<'w, 'r, W> { ser: ContentSerializer<'w, 'r, W>, /// Name of the root tag. If not specified, deduced from the structure name root_tag: Option>, } -impl<'w, 'r, W: Write> Serializer<'w, 'r, W> { +impl<'w, 'r, W: std::io::Write> Serializer<'w, 'r, W> { /// Creates a new `Serializer` that uses struct name as a root tag name. /// /// Note, that attempt to serialize a non-struct (including unit structs @@ -309,17 +238,19 @@ impl<'w, 'r, W: Write> Serializer<'w, 'r, W> { /// is not defined. Returns an error if root tag is not defined and a `key` /// does not conform [XML rules](XmlName::try_from) for names. fn ser_name(self, key: &'static str) -> Result, DeError> { - Ok(ElementSerializer { - ser: self.ser, - key: match self.root_tag { - Some(key) => key, - None => XmlName::try_from(key)?, - }, - }) + Ok(self.ser.into_element_serializer(match self.root_tag { + Some(key) => key, + None => XmlName::try_from(key)?, + })) + } + + /// Get writer. + pub fn get_mut(&mut self) -> &mut W { + self.ser.writer } } -impl<'w, 'r, W: Write> ser::Serializer for Serializer<'w, 'r, W> { +impl<'w, 'r, W: std::io::Write> ser::Serializer for Serializer<'w, 'r, W> { type Ok = (); type Error = DeError; @@ -411,11 +342,7 @@ impl<'w, 'r, W: Write> ser::Serializer for Serializer<'w, 'r, W> { value.serialize(self.ser.into_simple_type_serializer())?; Ok(()) } else { - let ser = ElementSerializer { - ser: self.ser, - key: XmlName::try_from(variant)?, - }; - value.serialize(ser) + value.serialize(self.ser.try_into_element_serializer(variant)?) } } @@ -448,10 +375,7 @@ impl<'w, 'r, W: Write> ser::Serializer for Serializer<'w, 'r, W> { .serialize_tuple_struct(name, len) .map(Tuple::Text) } else { - let ser = ElementSerializer { - ser: self.ser, - key: XmlName::try_from(variant)?, - }; + let ser = self.ser.try_into_element_serializer(variant)?; ser.serialize_tuple_struct(name, len).map(Tuple::Element) } } @@ -484,1639 +408,8 @@ impl<'w, 'r, W: Write> ser::Serializer for Serializer<'w, 'r, W> { .into(), )) } else { - let ser = ElementSerializer { - ser: self.ser, - key: XmlName::try_from(variant)?, - }; - ser.serialize_struct(name, len) - } - } -} - - -macro_rules! write_primitive_content { - ($method:ident ( $ty:ty )) => { - #[inline] - fn $method(self, value: $ty) -> Result { - self.into_simple_type_serializer().$method(value)?; - Ok(()) - } - }; -} - - -/// A serializer used to serialize content of an element. It does not write -/// surrounding tags. Unlike the [`ElementSerializer`], this serializer serializes -/// enums using variant names as tag names, i. e. as `...` -/// -/// This serializer does the following: -/// - numbers converted to a decimal representation and serialized as naked strings; -/// - booleans serialized ether as `"true"` or `"false"`; -/// - strings and characters are serialized as naked strings; -/// - `None` does not write anything; -/// - `Some` and newtypes are serialized as an inner type using the same serializer; -/// - units (`()`) and unit structs does not write anything; -/// - sequences, tuples and tuple structs are serialized without delimiters. -/// `[1, 2, 3]` would be serialized as `123` (if not using indent); -/// - structs and maps are not supported ([`DeError::Unsupported`] is returned); -/// - enums: -/// - unit variants are serialized as self-closed ``; -/// - newtype variants are serialized as inner value wrapped in `...`; -/// - tuple variants are serialized as sequences where each element is wrapped -/// in `...`; -/// - struct variants are serialized as a sequence of fields wrapped in -/// `...`. Each field is serialized recursively using -/// either [`ElementSerializer`], `ContentSerializer` (`$value` fields), or -/// [`SimpleTypeSerializer`] (`$text` fields). In particular, the empty struct -/// is serialized as ``; -/// -/// Usage of empty tags depends on the [`Self::expand_empty_elements`] setting. -/// -/// The difference between this serializer and [`SimpleTypeSerializer`] is in how -/// sequences and maps are serialized. Unlike `SimpleTypeSerializer` it supports -/// any types in sequences and serializes them as list of elements, but that has -/// drawbacks. Sequence of primitives would be serialized without delimiters and -/// it will be impossible to distinguish between them. Even worse, when serializing -/// with indent, sequence of strings become one big string with additional content -/// and it would be impossible to distinguish between content of the original -/// strings and inserted indent characters. -pub struct ContentSerializer<'w, 'i, W: Write> { - pub writer: &'w mut W, - /// Defines which XML characters need to be escaped in text content - pub level: QuoteLevel, - /// Current indentation level. Note, that `Indent::None` means that there is - /// no indentation at all, but `write_indent == false` means only, that indent - /// writing is disabled in this instantiation of `ContentSerializer`, but - /// child serializers should have access to the actual state of indentation. - pub(super) indent: Indent<'i>, - /// If `true`, then current indent will be written before writing the content, - /// but only if content is not empty. - pub write_indent: bool, - // If `true`, then empty elements will be serialized as `` - // instead of ``. - pub expand_empty_elements: bool, - //TODO: add settings to disallow consequent serialization of primitives -} - -impl<'w, 'i, W: Write> ContentSerializer<'w, 'i, W> { - /// Turns this serializer into serializer of a text content - #[inline] - pub fn into_simple_type_serializer(self) -> SimpleTypeSerializer<'i, &'w mut W> { - //TODO: Customization point: choose between CDATA and Text representation - SimpleTypeSerializer { - writer: self.writer, - target: QuoteTarget::Text, - level: self.level, - indent: if self.write_indent { - self.indent - } else { - Indent::None - }, - } - } - - /// Creates new serializer that shares state with this serializer and - /// writes to the same underlying writer - #[inline] - pub fn new_seq_element_serializer(&mut self) -> ContentSerializer { - ContentSerializer { - writer: self.writer, - level: self.level, - indent: self.indent.borrow(), - write_indent: self.write_indent, - expand_empty_elements: self.expand_empty_elements, - } - } - - /// Writes `name` as self-closed tag - #[inline] - pub(super) fn write_empty(mut self, name: XmlName) -> Result<(), DeError> { - self.write_indent()?; - if self.expand_empty_elements { - self.writer.write_char('<')?; - self.writer.write_str(name.0)?; - self.writer.write_str(">')?; - } else { - self.writer.write_str("<")?; - self.writer.write_str(name.0)?; - self.writer.write_str("/>")?; - } - Ok(()) - } - - /// Writes simple type content between `name` tags - pub(super) fn write_wrapped(mut self, name: XmlName, serialize: S) -> Result<(), DeError> - where - S: for<'a> FnOnce(SimpleTypeSerializer<'i, &'a mut W>) -> Result<&'a mut W, DeError>, - { - self.write_indent()?; - self.writer.write_char('<')?; - self.writer.write_str(name.0)?; - self.writer.write_char('>')?; - - let writer = serialize(self.into_simple_type_serializer())?; - - writer.write_str("')?; - Ok(()) - } - - pub(super) fn write_indent(&mut self) -> Result<(), DeError> { - if self.write_indent { - self.indent.write_io_indent(&mut self.writer)?; - self.write_indent = false; - } - Ok(()) - } -} - -impl<'w, 'i, W: Write> ser::Serializer for ContentSerializer<'w, 'i, W> { - type Ok = (); - type Error = DeError; - - type SerializeSeq = Self; - type SerializeTuple = Self; - type SerializeTupleStruct = Self; - type SerializeTupleVariant = Tuple<'w, 'i, W>; - type SerializeMap = Impossible; - type SerializeStruct = Impossible; - type SerializeStructVariant = Struct<'w, 'i, W>; - - write_primitive_content!(serialize_bool(bool)); - - write_primitive_content!(serialize_i8(i8)); - write_primitive_content!(serialize_i16(i16)); - write_primitive_content!(serialize_i32(i32)); - write_primitive_content!(serialize_i64(i64)); - - write_primitive_content!(serialize_u8(u8)); - write_primitive_content!(serialize_u16(u16)); - write_primitive_content!(serialize_u32(u32)); - write_primitive_content!(serialize_u64(u64)); - - serde_if_integer128! { - write_primitive_content!(serialize_i128(i128)); - write_primitive_content!(serialize_u128(u128)); - } - - write_primitive_content!(serialize_f32(f32)); - write_primitive_content!(serialize_f64(f64)); - - write_primitive_content!(serialize_char(char)); - write_primitive_content!(serialize_bytes(&[u8])); - write_primitive_content!(serialize_str(&str)); - - /// Does not write anything - #[inline] - fn serialize_none(self) -> Result { - Ok(()) - } - - fn serialize_some(self, value: &T) -> Result { - value.serialize(self) - } - - /// Does not write anything - #[inline] - fn serialize_unit(self) -> Result { - Ok(()) - } - - /// Does not write anything - #[inline] - fn serialize_unit_struct(self, _name: &'static str) -> Result { - Ok(()) - } - - /// If `variant` is a special `$text` variant, then do nothing, otherwise - /// checks `variant` for XML name validity and writes ``. - fn serialize_unit_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - ) -> Result { - if variant == TEXT_KEY { - Ok(()) - } else { - let name = XmlName::try_from(variant)?; - self.write_empty(name) - } - } - - fn serialize_newtype_struct( - self, - _name: &'static str, - value: &T, - ) -> Result { - value.serialize(self) - } - - /// If `variant` is a special `$text` variant, then writes `value` as a `xs:simpleType`, - /// otherwise checks `variant` for XML name validity and writes `value` as a new - /// `` element. - fn serialize_newtype_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - value: &T, - ) -> Result { - if variant == TEXT_KEY { - value.serialize(self.into_simple_type_serializer())?; - Ok(()) - } else { - value.serialize(ElementSerializer { - key: XmlName::try_from(variant)?, - ser: self, - }) - } - } - - #[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) - } - - /// Serializes variant as a tuple with name `variant`, producing - /// - /// ```xml - /// - /// - /// - /// - /// ``` - #[inline] - fn serialize_tuple_variant( - self, - name: &'static str, - _variant_index: u32, - variant: &'static str, - len: usize, - ) -> Result { - if variant == TEXT_KEY { - self.into_simple_type_serializer() - .serialize_tuple_struct(name, len) - .map(Tuple::Text) - } else { - let ser = ElementSerializer { - key: XmlName::try_from(variant)?, - ser: self, - }; - ser.serialize_tuple_struct(name, len).map(Tuple::Element) - } - } - - fn serialize_map(self, _len: Option) -> Result { - Err(DeError::Unsupported( - "serialization of map types is not supported in `$value` field".into(), - )) - } - - #[inline] - fn serialize_struct( - self, - name: &'static str, - _len: usize, - ) -> Result { - Err(DeError::Unsupported( - format!("serialization of struct `{name}` is not supported in `$value` field").into(), - )) - } - - /// Serializes variant as an element with name `variant`, producing - /// - /// ```xml - /// - /// - /// - /// ``` - /// - /// If struct has no fields which is represented by nested elements or a text, - /// it may be serialized as self-closed element ``. - #[inline] - fn serialize_struct_variant( - self, - name: &'static str, - _variant_index: u32, - variant: &'static str, - len: usize, - ) -> Result { - if variant == TEXT_KEY { - Err(DeError::Unsupported( - format!("cannot serialize `$text` struct variant of `{}` enum", name).into(), - )) - } else { - let ser = ElementSerializer { - key: XmlName::try_from(variant)?, - ser: self, - }; + let ser = self.ser.try_into_element_serializer(variant)?; ser.serialize_struct(name, len) } } -} - -impl<'w, 'i, W: Write> SerializeSeq for ContentSerializer<'w, 'i, W> { - type Ok = (); - type Error = DeError; - - fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> - where - T: ?Sized + Serialize, - { - value.serialize(self.new_seq_element_serializer())?; - // Write indent for next element - self.write_indent = true; - Ok(()) - } - - #[inline] - fn end(self) -> Result { - Ok(()) - } -} - -impl<'w, 'i, W: Write> SerializeTuple for ContentSerializer<'w, 'i, W> { - type Ok = (); - type Error = DeError; - - #[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, 'i, W: Write> SerializeTupleStruct for ContentSerializer<'w, 'i, W> { - type Ok = (); - type Error = DeError; - - #[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) - } -} - - - -macro_rules! write_primitive_elem { - ($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. -/// -/// 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 ([`DeError::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> ser::Serializer for ElementSerializer<'w, 'k, W> { - type Ok = (); - type Error = DeError; - - 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_elem!(serialize_bool(bool)); - - write_primitive_elem!(serialize_i8(i8)); - write_primitive_elem!(serialize_i16(i16)); - write_primitive_elem!(serialize_i32(i32)); - write_primitive_elem!(serialize_i64(i64)); - - write_primitive_elem!(serialize_u8(u8)); - write_primitive_elem!(serialize_u16(u16)); - write_primitive_elem!(serialize_u32(u32)); - write_primitive_elem!(serialize_u64(u64)); - - serde_if_integer128! { - write_primitive_elem!(serialize_i128(i128)); - write_primitive_elem!(serialize_u128(u128)); - } - - write_primitive_elem!(serialize_f32(f32)); - write_primitive_elem!(serialize_f64(f64)); - - write_primitive_elem!(serialize_char(char)); - write_primitive_elem!(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 [`DeError::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(DeError::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 [`DeError::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(DeError::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: Vec::new(), - }) - } - - /// Always returns [`DeError::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(DeError::Unsupported( - format!( - "cannot serialize enum struct variant `{}::{}`", - name, variant - ) - .into(), - )) - } -} - -impl<'w, 'k, W: Write> SerializeSeq for ElementSerializer<'w, 'k, W> { - type Ok = (); - type Error = DeError; - - fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> - where - T: ?Sized + Serialize, - { - value.serialize(ElementSerializer { - ser: self.ser.new_seq_element_serializer(), - key: self.key, - })?; - // Write indent for the next element - self.ser.write_indent = true; - Ok(()) - } - - #[inline] - fn end(self) -> Result { - Ok(()) - } -} - -impl<'w, 'k, W: Write> SerializeTuple for ElementSerializer<'w, 'k, W> { - type Ok = (); - type Error = DeError; - - #[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 = (); - type Error = DeError; - - #[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<'k, &'w mut W>), -} - -impl<'w, 'k, W: Write> SerializeTupleVariant for Tuple<'w, 'k, W> { - type Ok = (); - type Error = DeError; - - #[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), - Self::Text(ser) => SerializeTuple::end(ser).map(|_| ()), - } - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// A serializer for struct variants, which serializes the struct contents inside -/// of wrapping tags (`<${tag}>...`). -/// -/// 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: Vec, -} - -impl<'w, 'k, W: Write> Struct<'w, 'k, W> { - #[inline] - fn write_field(&mut self, key: &str, value: &T) -> Result<(), DeError> - 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<(), DeError> - 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, - indent: Indent::None, - })?; - 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<(), DeError> - where - T: ?Sized + Serialize, - { - let ser = ContentSerializer { - writer: &mut self.children, - level: self.ser.ser.level, - indent: self.ser.ser.indent.borrow(), - write_indent: true, - expand_empty_elements: self.ser.ser.expand_empty_elements, - }; - - if key == TEXT_KEY { - value.serialize(TextSerializer(ser.into_simple_type_serializer()))?; - } else if key == VALUE_KEY { - value.serialize(ser)?; - } else { - value.serialize(ElementSerializer { - key: XmlName::try_from(key)?, - ser, - })?; - } - Ok(()) - } -} - -impl<'w, 'k, W: Write> SerializeStruct for Struct<'w, 'k, W> { - type Ok = (); - type Error = DeError; - - 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(&self.children)?; - - self.ser.ser.indent.write_io_indent(&mut self.ser.ser.writer)?; - - self.ser.ser.writer.write_str("')?; - } - Ok(()) - } -} - -impl<'w, 'k, W: Write> SerializeStructVariant for Struct<'w, 'k, W> { - type Ok = (); - type Error = DeError; - - #[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(super::key::QNameSerializer { - writer: String::new(), - }) - } -} - -impl<'w, 'k, W: Write> SerializeMap for Map<'w, 'k, W> { - type Ok = (); - type Error = DeError; - - fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> - where - T: ?Sized + Serialize, - { - if let Some(_) = self.key.take() { - return Err(DeError::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(DeError::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(DeError::Custom(format!( - "calling `end` without call of `serialize_value` for key `{key}`" - ))); - } - SerializeStruct::end(self.ser) - } -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -/// -macro_rules! write_atomic { - ($method:ident ( $ty:ty )) => { - fn $method(mut self, value: $ty) -> Result { - self.write_str(&value.to_string())?; - Ok(true) - } - }; -} - - -/// A serializer that handles ordinary [simple type definition][item] with -/// `{variety} = atomic`, or an ordinary [simple type] definition with -/// `{variety} = union` whose basic members are all atomic. -/// -/// This serializer can serialize only primitive types: -/// - numbers -/// - booleans -/// - strings -/// - units -/// - options -/// - unit variants of enums -/// -/// Identifiers represented as strings and serialized accordingly. -/// -/// Serialization of all other types returns [`Unsupported`][DeError::Unsupported] error. -/// -/// This serializer returns `true` if something was written and `false` otherwise. -/// -/// [item]: https://www.w3.org/TR/xmlschema11-1/#std-item_type_definition -/// [simple type]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition -pub struct AtomicSerializer<'i, W: Write> { - pub writer: W, - pub target: QuoteTarget, - /// Defines which XML characters need to be escaped - pub level: QuoteLevel, - /// When `Some`, the indent that should be written before the content - /// if content is not an empty string. - /// When `None` an `xs:list` delimiter (a space) should be written - pub(crate) indent: Option>, -} - -impl<'i, W: Write> AtomicSerializer<'i, W> { - fn write_str(&mut self, value: &str) -> Result<(), DeError> { - if let Some(indent) = self.indent.as_mut() { - indent.write_io_indent(&mut self.writer)?; - } else { - // TODO: Customization point -- possible non-XML compatible extension to specify delimiter char - self.writer.write_char(' ')?; - } - Ok(self.writer.write_str(value)?) - } -} - -impl<'i, W: Write> ser::Serializer for AtomicSerializer<'i, W> { - type Ok = bool; - type Error = DeError; - - type SerializeSeq = Impossible; - type SerializeTuple = Impossible; - type SerializeTupleStruct = Impossible; - type SerializeTupleVariant = Impossible; - type SerializeMap = Impossible; - type SerializeStruct = Impossible; - type SerializeStructVariant = Impossible; - - fn serialize_bool(mut self, value: bool) -> Result { - self.write_str(if value { "true" } else { "false" })?; - Ok(true) - } - - write_atomic!(serialize_i8(i8)); - write_atomic!(serialize_i16(i16)); - write_atomic!(serialize_i32(i32)); - write_atomic!(serialize_i64(i64)); - - write_atomic!(serialize_u8(u8)); - write_atomic!(serialize_u16(u16)); - write_atomic!(serialize_u32(u32)); - write_atomic!(serialize_u64(u64)); - - serde_if_integer128! { - write_atomic!(serialize_i128(i128)); - write_atomic!(serialize_u128(u128)); - } - - write_atomic!(serialize_f32(f32)); - write_atomic!(serialize_f64(f64)); - - fn serialize_char(self, value: char) -> Result { - self.serialize_str(&value.to_string()) - } - - fn serialize_str(mut self, value: &str) -> Result { - if !value.is_empty() { - self.write_str(&super::simple_type::escape_item(value, self.target, self.level))?; - } - Ok(!value.is_empty()) - } - - fn serialize_bytes(mut self, value: &[u8]) -> Result { - if !value.is_empty() { - self.writer.write(&value)?; - } - Ok(!value.is_empty()) - } - - fn serialize_none(self) -> Result { - Ok(false) - } - - fn serialize_some(self, value: &T) -> Result { - value.serialize(self) - } - - /// 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( - "cannot serialize unit type `()` as an `xs:list` item".into(), - )) - } - - /// 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_struct(self, name: &'static str) -> Result { - Err(DeError::Unsupported( - format!( - "cannot serialize unit struct `{}` as an `xs:list` item", - name - ) - .into(), - )) - } - - 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) - } - - /// 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!( - "cannot serialize enum newtype variant `{}::{}` as an `xs:list` item", - name, variant - ) - .into(), - )) - } - - fn serialize_seq(self, _len: Option) -> Result { - Err(DeError::Unsupported( - "cannot serialize sequence as an `xs:list` item".into(), - )) - } - - fn serialize_tuple(self, _len: usize) -> Result { - Err(DeError::Unsupported( - "cannot serialize tuple as an `xs:list` item".into(), - )) - } - - fn serialize_tuple_struct( - self, - name: &'static str, - _len: usize, - ) -> Result { - Err(DeError::Unsupported( - format!( - "cannot serialize tuple struct `{}` as an `xs:list` item", - name - ) - .into(), - )) - } - - fn serialize_tuple_variant( - self, - name: &'static str, - _variant_index: u32, - variant: &'static str, - _len: usize, - ) -> Result { - Err(DeError::Unsupported( - format!( - "cannot serialize enum tuple variant `{}::{}` as an `xs:list` item", - name, variant - ) - .into(), - )) - } - - fn serialize_map(self, _len: Option) -> Result { - Err(DeError::Unsupported( - "cannot serialize map as an `xs:list` item".into(), - )) - } - - fn serialize_struct( - self, - name: &'static str, - _len: usize, - ) -> Result { - Err(DeError::Unsupported( - format!("cannot serialize struct `{}` as an `xs:list` item", name).into(), - )) - } - - fn serialize_struct_variant( - self, - name: &'static str, - _variant_index: u32, - variant: &'static str, - _len: usize, - ) -> Result { - Err(DeError::Unsupported( - format!( - "cannot serialize enum struct variant `{}::{}` as an `xs:list` item", - name, variant - ) - .into(), - )) - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// A serializer for a values representing XSD [simple types], which used in: -/// - attribute values (`<... ...="value" ...>`) -/// - text content (`<...>text`) -/// - CDATA content (`<...>`) -/// -/// [simple types]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition -pub struct SimpleTypeSerializer<'i, W: Write> { - /// Writer to which this serializer writes content - pub writer: W, - /// Target for which element is serializing. Affects additional characters to escape. - pub target: QuoteTarget, - /// Defines which XML characters need to be escaped - pub level: QuoteLevel, - /// Indent that should be written before the content if content is not an empty string - pub(crate) indent: Indent<'i>, -} - -impl<'i, W: Write> SimpleTypeSerializer<'i, W> { - fn write_str(&mut self, value: &str) -> Result<(), DeError> { - self.indent.write_io_indent(&mut self.writer)?; - Ok(self.writer.write_str(value)?) - } -} - -impl<'i, W: Write> ser::Serializer for SimpleTypeSerializer<'i, W> { - type Ok = W; - type Error = DeError; - - type SerializeSeq = SimpleSeq<'i, W>; - type SerializeTuple = SimpleSeq<'i, W>; - type SerializeTupleStruct = SimpleSeq<'i, W>; - type SerializeTupleVariant = Impossible; - type SerializeMap = Impossible; - type SerializeStruct = Impossible; - type SerializeStructVariant = Impossible; - - write_primitive!(); - - fn serialize_str(mut self, value: &str) -> Result { - if !value.is_empty() { - self.write_str(&super::simple_type::escape_list(value, self.target, self.level))?; - } - Ok(self.writer) - } - - /// Does not write anything - fn serialize_unit(self) -> Result { - Ok(self.writer) - } - - /// Does not write anything - fn serialize_unit_struct(self, _name: &'static str) -> Result { - Ok(self.writer) - } - - /// 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!("cannot serialize enum newtype variant `{}::{}` as an attribute or text content value", name, variant).into(), - )) - } - - #[inline] - fn serialize_seq(self, _len: Option) -> Result { - Ok(SimpleSeq { - writer: self.writer, - target: self.target, - level: self.level, - indent: self.indent, - is_empty: true, - }) - } - - #[inline] - fn serialize_tuple(self, _len: usize) -> Result { - self.serialize_seq(None) - } - - #[inline] - fn serialize_tuple_struct( - self, - _name: &'static str, - _len: usize, - ) -> Result { - self.serialize_seq(None) - } - - fn serialize_tuple_variant( - self, - name: &'static str, - _variant_index: u32, - variant: &'static str, - _len: usize, - ) -> Result { - Err(DeError::Unsupported( - format!("cannot serialize enum tuple variant `{}::{}` as an attribute or text content value", name, variant).into(), - )) - } - - fn serialize_map(self, _len: Option) -> Result { - Err(DeError::Unsupported( - "cannot serialize map as an attribute or text content value".into(), - )) - } - - fn serialize_struct( - self, - name: &'static str, - _len: usize, - ) -> Result { - Err(DeError::Unsupported( - format!( - "cannot serialize struct `{}` as an attribute or text content value", - name - ) - .into(), - )) - } - - fn serialize_struct_variant( - self, - name: &'static str, - _variant_index: u32, - variant: &'static str, - _len: usize, - ) -> Result { - Err(DeError::Unsupported( - format!("cannot serialize enum struct variant `{}::{}` as an attribute or text content value", name, variant).into(), - )) - } -} - -/// Serializer for a sequence of atomic values delimited by space -pub struct SimpleSeq<'i, W: Write> { - writer: W, - target: QuoteTarget, - level: QuoteLevel, - /// Indent that should be written before the content if content is not an empty string - indent: Indent<'i>, - /// If `true`, nothing was written yet to the `writer` - is_empty: bool, -} - -impl<'i, W: Write> SerializeSeq for SimpleSeq<'i, W> { - type Ok = W; - type Error = DeError; - - fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> - where - T: ?Sized + Serialize, - { - // Write indent for the first element and delimiter for others - let indent = if self.is_empty { - Some(self.indent.borrow()) - } else { - None - }; - if value.serialize(AtomicSerializer { - writer: &mut self.writer, - target: self.target, - level: self.level, - indent, - })? { - self.is_empty = false; - } - Ok(()) - } - - #[inline] - fn end(self) -> Result { - Ok(self.writer) - } -} - -impl<'i, W: Write> SerializeTuple for SimpleSeq<'i, W> { - type Ok = W; - type Error = DeError; - - #[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<'i, W: Write> SerializeTupleStruct for SimpleSeq<'i, W> { - type Ok = W; - type Error = DeError; - - #[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) - } -} - -impl<'i, W: Write> SerializeTupleVariant for SimpleSeq<'i, W> { - type Ok = W; - type Error = DeError; - - #[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) - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -/// - - - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -macro_rules! write_primitive_text { - ($method:ident ( $ty:ty )) => { - #[inline] - fn $method(self, value: $ty) -> Result { - self.0.$method(value) - } - }; -} - -/// A serializer used to serialize a `$text` field of a struct or map. -/// -/// This serializer a very similar to [`SimpleTypeSerializer`], but different -/// from it in how it processes unit enum variants. Unlike [`SimpleTypeSerializer`] -/// this serializer does not write anything for the unit variant. -pub struct TextSerializer<'i, W: Write>(pub SimpleTypeSerializer<'i, W>); - -impl<'i, W: Write> ser::Serializer for TextSerializer<'i, W> { - type Ok = W; - type Error = DeError; - - type SerializeSeq = SimpleSeq<'i, W>; - type SerializeTuple = SimpleSeq<'i, W>; - type SerializeTupleStruct = SimpleSeq<'i, W>; - type SerializeTupleVariant = SimpleSeq<'i, W>; - type SerializeMap = Impossible; - type SerializeStruct = Impossible; - type SerializeStructVariant = Impossible; - - write_primitive_text!(serialize_bool(bool)); - - write_primitive_text!(serialize_i8(i8)); - write_primitive_text!(serialize_i16(i16)); - write_primitive_text!(serialize_i32(i32)); - write_primitive_text!(serialize_i64(i64)); - - write_primitive_text!(serialize_u8(u8)); - write_primitive_text!(serialize_u16(u16)); - write_primitive_text!(serialize_u32(u32)); - write_primitive_text!(serialize_u64(u64)); - - serde_if_integer128! { - write_primitive_text!(serialize_i128(i128)); - write_primitive_text!(serialize_u128(u128)); - } - - write_primitive_text!(serialize_f32(f32)); - write_primitive_text!(serialize_f64(f64)); - - write_primitive_text!(serialize_char(char)); - write_primitive_text!(serialize_str(&str)); - write_primitive_text!(serialize_bytes(&[u8])); - - #[inline] - fn serialize_none(self) -> Result { - self.0.serialize_none() - } - - fn serialize_some(self, value: &T) -> Result { - value.serialize(self) - } - - #[inline] - fn serialize_unit(self) -> Result { - self.0.serialize_unit() - } - - #[inline] - fn serialize_unit_struct(self, name: &'static str) -> Result { - self.0.serialize_unit_struct(name) - } - - #[inline] - fn serialize_unit_variant( - self, - name: &'static str, - variant_index: u32, - variant: &'static str, - ) -> Result { - if variant == TEXT_KEY { - Ok(self.0.writer) - } else { - self.0.serialize_unit_variant(name, variant_index, variant) - } - } - - fn serialize_newtype_struct( - self, - _name: &'static str, - value: &T, - ) -> Result { - value.serialize(self) - } - - #[inline] - fn serialize_newtype_variant( - self, - name: &'static str, - _variant_index: u32, - variant: &'static str, - _value: &T, - ) -> Result { - Err(DeError::Unsupported( - format!( - "cannot serialize enum newtype variant `{}::{}` as text content value", - name, variant - ) - .into(), - )) - } - - #[inline] - fn serialize_seq(self, len: Option) -> Result { - self.0.serialize_seq(len) - } - - #[inline] - fn serialize_tuple(self, len: usize) -> Result { - self.0.serialize_tuple(len) - } - - #[inline] - fn serialize_tuple_struct( - self, - name: &'static str, - len: usize, - ) -> Result { - self.0.serialize_tuple_struct(name, len) - } - - #[inline] - fn serialize_tuple_variant( - self, - name: &'static str, - _variant_index: u32, - variant: &'static str, - _len: usize, - ) -> Result { - Err(DeError::Unsupported( - format!( - "cannot serialize enum tuple variant `{}::{}` as text content value", - name, variant - ) - .into(), - )) - } - - #[inline] - fn serialize_map(self, _len: Option) -> Result { - Err(DeError::Unsupported( - "cannot serialize map as text content value".into(), - )) - } - - #[inline] - fn serialize_struct( - self, - name: &'static str, - _len: usize, - ) -> Result { - Err(DeError::Unsupported( - format!("cannot serialize struct `{}` as text content value", name).into(), - )) - } - - #[inline] - fn serialize_struct_variant( - self, - name: &'static str, - _variant_index: u32, - variant: &'static str, - _len: usize, - ) -> Result { - Err(DeError::Unsupported( - format!( - "cannot serialize enum struct variant `{}::{}` as text content value", - name, variant - ) - .into(), - )) - } } \ No newline at end of file diff --git a/src/se/io/content.rs b/src/se/io/content.rs new file mode 100644 index 00000000..5b8e882e --- /dev/null +++ b/src/se/io/content.rs @@ -0,0 +1,1447 @@ +use super::*; +use serde::ser::Serializer; + + +macro_rules! write_primitive { + ($method:ident ( $ty:ty )) => { + #[inline] + fn $method(self, value: $ty) -> Result { + self.into_simple_type_serializer().$method(value)?; + Ok(()) + } + }; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// A serializer used to serialize content of an element. It does not write +/// surrounding tags. Unlike the [`ElementSerializer`], this serializer serializes +/// enums using variant names as tag names, i. e. as `...` +/// +/// This serializer does the following: +/// - numbers converted to a decimal representation and serialized as naked strings; +/// - booleans serialized ether as `"true"` or `"false"`; +/// - strings and characters are serialized as naked strings; +/// - `None` does not write anything; +/// - `Some` and newtypes are serialized as an inner type using the same serializer; +/// - units (`()`) and unit structs does not write anything; +/// - sequences, tuples and tuple structs are serialized without delimiters. +/// `[1, 2, 3]` would be serialized as `123` (if not using indent); +/// - structs and maps are not supported ([`DeError::Unsupported`] is returned); +/// - enums: +/// - unit variants are serialized as self-closed ``; +/// - newtype variants are serialized as inner value wrapped in `...`; +/// - tuple variants are serialized as sequences where each element is wrapped +/// in `...`; +/// - struct variants are serialized as a sequence of fields wrapped in +/// `...`. Each field is serialized recursively using +/// either [`ElementSerializer`], `ContentSerializer` (`$value` fields), or +/// [`SimpleTypeSerializer`] (`$text` fields). In particular, the empty struct +/// is serialized as ``; +/// +/// Usage of empty tags depends on the [`Self::expand_empty_elements`] setting. +/// +/// The difference between this serializer and [`SimpleTypeSerializer`] is in how +/// sequences and maps are serialized. Unlike `SimpleTypeSerializer` it supports +/// any types in sequences and serializes them as list of elements, but that has +/// drawbacks. Sequence of primitives would be serialized without delimiters and +/// it will be impossible to distinguish between them. Even worse, when serializing +/// with indent, sequence of strings become one big string with additional content +/// and it would be impossible to distinguish between content of the original +/// strings and inserted indent characters. +pub struct ContentSerializer<'w, 'i, W> { + pub writer: &'w mut W, + /// Defines which XML characters need to be escaped in text content + pub level: QuoteLevel, + /// Current indentation level. Note, that `Indent::None` means that there is + /// no indentation at all, but `write_indent == false` means only, that indent + /// writing is disabled in this instantiation of `ContentSerializer`, but + /// child serializers should have access to the actual state of indentation. + pub(super) indent: Indent<'i>, + /// If `true`, then current indent will be written before writing the content, + /// but only if content is not empty. + pub write_indent: bool, + // If `true`, then empty elements will be serialized as `` + // instead of ``. + pub expand_empty_elements: bool, + //TODO: add settings to disallow consequent serialization of primitives +} + +impl<'w, 'i, W: Write> ContentSerializer<'w, 'i, W> { + /// Turns this serializer into serializer of a text content + #[inline] + pub fn try_into_element_serializer(self, key: &'static str) -> Result, DeError> { + Ok(self.into_element_serializer(XmlName::try_from(key)?)) + } + + /// Turns this serializer into serializer of a text content + #[inline] + pub(crate) fn into_element_serializer(self, key: XmlName<'i>) -> ElementSerializer<'w, 'i, W> { + ElementSerializer { + ser: self, + key, + } + } +} + + +impl<'w, 'i, W: Write> ContentSerializer<'w, 'i, W> { + /// Turns this serializer into serializer of a text content + #[inline] + pub fn into_simple_type_serializer(self) -> SimpleTypeSerializer<'i, &'w mut W> { + //TODO: Customization point: choose between CDATA and Text representation + SimpleTypeSerializer { + writer: self.writer, + target: QuoteTarget::Text, + level: self.level, + indent: if self.write_indent { + self.indent + } else { + Indent::None + }, + } + } + + /// Creates new serializer that shares state with this serializer and + /// writes to the same underlying writer + #[inline] + pub fn new_seq_element_serializer(&mut self) -> ContentSerializer { + ContentSerializer { + writer: self.writer, + level: self.level, + indent: self.indent.borrow(), + write_indent: self.write_indent, + expand_empty_elements: self.expand_empty_elements, + } + } + + /// Writes `name` as self-closed tag + #[inline] + pub(super) fn write_empty(mut self, name: XmlName) -> Result<(), DeError> { + self.write_indent()?; + if self.expand_empty_elements { + self.writer.write_char('<')?; + self.writer.write_str(name.0)?; + self.writer.write_str(">')?; + } else { + self.writer.write_str("<")?; + self.writer.write_str(name.0)?; + self.writer.write_str("/>")?; + } + Ok(()) + } + + /// Writes simple type content between `name` tags + pub(super) fn write_wrapped(mut self, name: XmlName, serialize: S) -> Result<(), DeError> + where + S: for<'a> FnOnce(SimpleTypeSerializer<'i, &'a mut W>) -> Result<&'a mut W, DeError>, + { + self.write_indent()?; + self.writer.write_char('<')?; + self.writer.write_str(name.0)?; + self.writer.write_char('>')?; + + let writer = serialize(self.into_simple_type_serializer())?; + + writer.write_str("')?; + Ok(()) + } + + pub(super) fn write_indent(&mut self) -> Result<(), DeError> { + if self.write_indent { + self.indent.write_io_indent(&mut self.writer)?; + self.write_indent = false; + } + Ok(()) + } +} + +impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> { + type Ok = (); + type Error = DeError; + + type SerializeSeq = Self; + type SerializeTuple = Self; + type SerializeTupleStruct = Self; + type SerializeTupleVariant = Tuple<'w, 'i, W>; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Struct<'w, 'i, 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)); + + #[inline] + fn serialize_bytes(self, v: &[u8]) -> Result { + self.writer.write(v)?; + Ok(()) + } + + #[inline] + fn serialize_str(self, value: &str) -> Result { + if !value.is_empty() { + self.into_simple_type_serializer().serialize_str(value)?; + } + Ok(()) + } + + /// Does not write anything + #[inline] + fn serialize_none(self) -> Result { + Ok(()) + } + + fn serialize_some(self, value: &T) -> Result { + value.serialize(self) + } + + /// Does not write anything + #[inline] + fn serialize_unit(self) -> Result { + Ok(()) + } + + /// Does not write anything + #[inline] + fn serialize_unit_struct(self, _name: &'static str) -> Result { + Ok(()) + } + + /// If `variant` is a special `$text` variant, then do nothing, otherwise + /// checks `variant` for XML name validity and writes ``. + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + if variant == TEXT_KEY { + Ok(()) + } else { + let name = XmlName::try_from(variant)?; + self.write_empty(name) + } + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result { + value.serialize(self) + } + + /// If `variant` is a special `$text` variant, then writes `value` as a `xs:simpleType`, + /// otherwise checks `variant` for XML name validity and writes `value` as a new + /// `` element. + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result { + if variant == TEXT_KEY { + value.serialize(self.into_simple_type_serializer())?; + Ok(()) + } else { + value.serialize(self.into_element_serializer(XmlName::try_from(variant)?)) + } + } + + #[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) + } + + /// Serializes variant as a tuple with name `variant`, producing + /// + /// ```xml + /// + /// + /// + /// + /// ``` + #[inline] + fn serialize_tuple_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + if variant == TEXT_KEY { + self.into_simple_type_serializer() + .serialize_tuple_struct(name, len) + .map(Tuple::Text) + } else { + let ser = self.into_element_serializer(XmlName::try_from(variant)?); + ser.serialize_tuple_struct(name, len).map(Tuple::Element) + } + } + + fn serialize_map(self, _len: Option) -> Result { + Err(DeError::Unsupported( + "serialization of map types is not supported in `$value` field".into(), + )) + } + + #[inline] + fn serialize_struct( + self, + name: &'static str, + _len: usize, + ) -> Result { + Err(DeError::Unsupported( + format!("serialization of struct `{name}` is not supported in `$value` field").into(), + )) + } + + /// Serializes variant as an element with name `variant`, producing + /// + /// ```xml + /// + /// + /// + /// ``` + /// + /// If struct has no fields which is represented by nested elements or a text, + /// it may be serialized as self-closed element ``. + #[inline] + fn serialize_struct_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + if variant == TEXT_KEY { + Err(DeError::Unsupported( + format!("cannot serialize `$text` struct variant of `{}` enum", name).into(), + )) + } else { + let ser = self.into_element_serializer(XmlName::try_from(variant)?); + ser.serialize_struct(name, len) + } + } +} + +impl<'w, 'i, W: Write> SerializeSeq for ContentSerializer<'w, 'i, W> { + type Ok = (); + type Error = DeError; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + value.serialize(self.new_seq_element_serializer())?; + // Write indent for next element + self.write_indent = true; + Ok(()) + } + + #[inline] + fn end(self) -> Result { + Ok(()) + } +} + +impl<'w, 'i, W: Write> SerializeTuple for ContentSerializer<'w, 'i, W> { + type Ok = (); + type Error = DeError; + + #[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, 'i, W: Write> SerializeTupleStruct for ContentSerializer<'w, 'i, W> { + type Ok = (); + type Error = DeError; + + #[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) + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Make tests public to reuse types in `elements::tests` module +#[cfg(test)] +pub(super) mod tests { + use super::*; + use crate::utils::Bytes; + use serde::Serialize; + use std::collections::BTreeMap; + + #[derive(Debug, Serialize, PartialEq)] + pub struct Unit; + + #[derive(Debug, Serialize, PartialEq)] + #[serde(rename = "<\"&'>")] + pub struct UnitEscaped; + + #[derive(Debug, Serialize, PartialEq)] + pub struct Newtype(pub usize); + + #[derive(Debug, Serialize, PartialEq)] + pub struct Tuple(pub &'static str, pub usize); + + #[derive(Debug, Serialize, PartialEq)] + pub struct Struct { + pub key: &'static str, + pub val: (usize, usize), + } + + /// Struct with a special `$text` field + #[derive(Debug, Serialize, PartialEq)] + pub struct Text { + pub before: &'static str, + #[serde(rename = "$text")] + pub content: T, + pub after: &'static str, + } + + /// Struct with a special `$value` field + #[derive(Debug, Serialize, PartialEq)] + pub struct Value { + pub before: &'static str, + #[serde(rename = "$value")] + pub content: T, + pub after: &'static str, + } + + /// Attributes identified by starting with `@` character + #[derive(Debug, Serialize, PartialEq)] + pub struct Attributes { + #[serde(rename = "@key")] + pub key: &'static str, + #[serde(rename = "@val")] + pub val: (usize, usize), + } + #[derive(Debug, Serialize, PartialEq)] + pub struct AttributesBefore { + #[serde(rename = "@key")] + pub key: &'static str, + pub val: usize, + } + #[derive(Debug, Serialize, PartialEq)] + pub struct AttributesAfter { + pub key: &'static str, + #[serde(rename = "@val")] + pub val: usize, + } + + #[derive(Debug, Serialize, PartialEq)] + pub enum Enum { + Unit, + /// Variant name becomes a tag name, but the name of variant is invalid + /// XML name. Serialization of this element should be forbidden + #[serde(rename = "<\"&'>")] + UnitEscaped, + Newtype(usize), + Tuple(&'static str, usize), + Struct { + key: &'static str, + /// Should be serialized as elements + val: (usize, usize), + }, + Attributes { + #[serde(rename = "@key")] + key: &'static str, + #[serde(rename = "@val")] + val: (usize, usize), + }, + AttributesBefore { + #[serde(rename = "@key")] + key: &'static str, + val: usize, + }, + AttributesAfter { + key: &'static str, + #[serde(rename = "@val")] + val: usize, + }, + } + + #[derive(Debug, Serialize, PartialEq)] + pub enum SpecialEnum { + /// Struct variant with a special `$text` field + Text { + before: &'static str, + #[serde(rename = "$text")] + content: T, + after: &'static str, + }, + /// Struct variant with a special `$value` field + Value { + before: &'static str, + #[serde(rename = "$value")] + content: T, + after: &'static str, + }, + } + + mod without_indent { + use super::Struct; + 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 = Vec::new(); + let ser = ContentSerializer { + writer: &mut buffer, + level: QuoteLevel::Full, + indent: Indent::None, + write_indent: false, + expand_empty_elements: false, + }; + + $data.serialize(ser).unwrap(); + assert_eq!(buffer, $expected.as_bytes()); + } + }; + } + + /// 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 = Vec::new(); + let ser = ContentSerializer { + writer: &mut buffer, + level: QuoteLevel::Full, + indent: Indent::None, + write_indent: false, + expand_empty_elements: false, + }; + + match $data.serialize(ser).unwrap_err() { + DeError::$kind(e) => assert_eq!(e, $reason), + e => panic!( + "Expected `Err({}({}))`, but got `{:?}`", + stringify!($kind), + $reason, + e + ), + } + // We could write something before fail + // assert_eq!(buffer, ""); + } + }; + } + + // Primitives is serialized in the same way as for SimpleTypeSerializer + 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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + 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::::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 => ""); + + // Unlike SimpleTypeSerializer, enumeration values serialized as tags + serialize_as!(enum_unit: Enum::Unit => ""); + err!(enum_unit_escaped: Enum::UnitEscaped + => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`")); + + // Newtypes recursively applies ContentSerializer + serialize_as!(newtype: Newtype(42) => "42"); + serialize_as!(enum_newtype: Enum::Newtype(42) => "42"); + + // Note that sequences of primitives serialized without delimiters! + serialize_as!(seq: vec![1, 2, 3] => "123"); + serialize_as!(seq_empty: Vec::::new() => ""); + serialize_as!(tuple: ("<\"&'>", "with\t\r\n spaces", 3usize) + => "<"&'>\ + with\t\r\n spaces\ + 3"); + serialize_as!(tuple_struct: Tuple("first", 42) + => "first\ + 42"); + serialize_as!(enum_tuple: Enum::Tuple("first", 42) + => "first\ + 42"); + + // Structured types cannot be serialized without surrounding tag, which + // only `enum` can provide + err!(map: BTreeMap::from([("_1", 2), ("_3", 4)]) + => Unsupported("serialization of map types is not supported in `$value` field")); + err!(struct_: Struct { key: "answer", val: (42, 42) } + => Unsupported("serialization of struct `Struct` is not supported in `$value` field")); + serialize_as!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) } + => "\ + answer\ + 42\ + 42\ + "); + + /// Special field name `$text` should be serialized as a text content + mod text_field { + use super::*; + use pretty_assertions::assert_eq; + + err!(map: BTreeMap::from([("$text", 2), ("_3", 4)]) + => Unsupported("serialization of map types is not supported in `$value` field")); + err!(struct_: + Text { + before: "answer", + content: (42, 42), + after: "answer", + } + => Unsupported("serialization of struct `Text` is not supported in `$value` field")); + serialize_as!(enum_struct: + SpecialEnum::Text { + before: "answer", + content: (42, 42), + after: "answer", + } + => "\ + answer\ + 42 42\ + answer\ + "); + } + + /// `$text` field inside a struct variant of an enum + mod enum_with_text_field { + use super::*; + use pretty_assertions::assert_eq; + + macro_rules! text { + ($name:ident: $data:expr => $expected:literal) => { + serialize_as!($name: + SpecialEnum::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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + text!(char_space: ' ' => " "); + + text!(str_non_escaped: "non-escaped string" => "non-escaped string"); + text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); + + err!(bytes: + SpecialEnum::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: + SpecialEnum::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: + SpecialEnum::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: + SpecialEnum::Text { + before: "answer", + content: BTreeMap::from([("_1", 2), ("_3", 4)]), + after: "answer", + } + => Unsupported("cannot serialize map as text content value")); + err!(struct_: + SpecialEnum::Text { + before: "answer", + content: Struct { key: "answer", val: (42, 42) }, + after: "answer", + } + => Unsupported("cannot serialize struct `Struct` as text content value")); + err!(enum_struct: + SpecialEnum::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")); + } + + /// `$value` field inside a struct variant of an enum + mod enum_with_value_field { + use super::*; + use pretty_assertions::assert_eq; + + macro_rules! value { + ($name:ident: $data:expr => $expected:literal) => { + serialize_as!($name: + SpecialEnum::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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + value!(char_space: ' ' => " "); + + value!(str_non_escaped: "non-escaped string" => "non-escaped string"); + value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); + + err!(bytes: + SpecialEnum::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: + SpecialEnum::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! + value!(seq: vec![1, 2, 3] => "123"); + value!(seq_empty: Vec::::new() => ""); + value!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) + => "<"&'>\ + with\t\n\r spaces\ + 3"); + value!(tuple_struct: Tuple("first", 42) => "first42"); + 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: + SpecialEnum::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_: + SpecialEnum::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; + + err!(map_attr: BTreeMap::from([("@key1", 1), ("@key2", 2)]) + => Unsupported("serialization of map types is not supported in `$value` field")); + err!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)]) + => Unsupported("serialization of map types is not supported in `$value` field")); + + err!(struct_: Attributes { key: "answer", val: (42, 42) } + => Unsupported("serialization of struct `Attributes` is not supported in `$value` field")); + err!(struct_before: AttributesBefore { key: "answer", val: 42 } + => Unsupported("serialization of struct `AttributesBefore` is not supported in `$value` field")); + err!(struct_after: AttributesAfter { key: "answer", val: 42 } + => Unsupported("serialization of struct `AttributesAfter` is not supported in `$value` field")); + + serialize_as!(enum_: Enum::Attributes { key: "answer", val: (42, 42) } + => r#""#); + serialize_as!(enum_before: Enum::AttributesBefore { key: "answer", val: 42 } + => r#"42"#); + serialize_as!(enum_after: Enum::AttributesAfter { key: "answer", val: 42 } + => r#"answer"#); + } + } + + mod with_indent { + use super::Struct; + use super::*; + use crate::writer::Indentation; + 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 = Vec::new(); + let ser = ContentSerializer { + writer: &mut buffer, + level: QuoteLevel::Full, + indent: Indent::Owned(Indentation::new(b' ', 2)), + write_indent: false, + expand_empty_elements: false, + }; + + $data.serialize(ser).unwrap(); + assert_eq!(buffer, $expected.as_bytes()); + } + }; + } + + /// 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 = Vec::new(); + let ser = ContentSerializer { + writer: &mut buffer, + level: QuoteLevel::Full, + indent: Indent::Owned(Indentation::new(b' ', 2)), + write_indent: false, + expand_empty_elements: false, + }; + + match $data.serialize(ser).unwrap_err() { + DeError::$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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + 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::::None => ""); + serialize_as!(option_some: Some(Enum::Unit) => ""); + + serialize_as!(unit: () => ""); + serialize_as!(unit_struct: Unit => ""); + serialize_as!(unit_struct_escaped: UnitEscaped => ""); + + // Unlike SimpleTypeSerializer, enumeration values serialized as tags + serialize_as!(enum_unit: Enum::Unit => ""); + err!(enum_unit_escaped: Enum::UnitEscaped + => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`")); + + // Newtypes recursively applies ContentSerializer + serialize_as!(newtype: Newtype(42) => "42"); + serialize_as!(enum_newtype: Enum::Newtype(42) => "42"); + + // Note that sequences of primitives serialized without delimiters other that indent! + serialize_as!(seq: vec![1, 2, 3] + => "1\n\ + 2\n\ + 3"); + serialize_as!(seq_empty: Vec::::new() => ""); + serialize_as!(tuple: ("<\"&'>", "with\t\r\n spaces", 3usize) + => "<"&'>\n\ + with\t\r\n spaces\n\ + 3"); + serialize_as!(tuple_struct: Tuple("first", 42) + => "first\n\ + 42"); + serialize_as!(enum_tuple: Enum::Tuple("first", 42) + => "first\n\ + 42"); + + // Structured types cannot be serialized without surrounding tag, which + // only `enum` can provide + err!(map: BTreeMap::from([("_1", 2), ("_3", 4)]) + => Unsupported("serialization of map types is not supported in `$value` field")); + err!(struct_: Struct { key: "answer", val: (42, 42) } + => Unsupported("serialization of struct `Struct` is not supported in `$value` field")); + serialize_as!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) } + => "\n \ + answer\n \ + 42\n \ + 42\n\ + "); + + /// Special field name `$text` should be serialized as text content + mod text_field { + use super::*; + use pretty_assertions::assert_eq; + + err!(map: BTreeMap::from([("$text", 2), ("_3", 4)]) + => Unsupported("serialization of map types is not supported in `$value` field")); + err!(struct_: + Text { + before: "answer", + content: (42, 42), + after: "answer", + } + => Unsupported("serialization of struct `Text` is not supported in `$value` field")); + serialize_as!(enum_struct: + SpecialEnum::Text { + before: "answer", + content: (42, 42), + after: "answer", + } + => "\n \ + answer\n \ + 42 42\n \ + answer\n\ + "); + } + + /// `$text` field inside a struct variant of an enum + mod enum_with_text_field { + use super::*; + use pretty_assertions::assert_eq; + + macro_rules! text { + ($name:ident: $data:expr) => { + serialize_as!($name: + SpecialEnum::Text { + before: "answer", + content: $data, + after: "answer", + } + => "\n \ + answer\n \ + answer\n\ + "); + }; + ($name:ident: $data:expr => $expected:literal) => { + serialize_as!($name: + SpecialEnum::Text { + before: "answer", + content: $data, + after: "answer", + } + => concat!( + "\n answer\n ", + $expected, + "\n 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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + text!(char_space: ' ' => " "); + + text!(str_non_escaped: "non-escaped string" => "non-escaped string"); + text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); + + err!(bytes: + SpecialEnum::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: + SpecialEnum::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: + SpecialEnum::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: + SpecialEnum::Text { + before: "answer", + content: BTreeMap::from([("_1", 2), ("_3", 4)]), + after: "answer", + } + => Unsupported("cannot serialize map as text content value")); + err!(struct_: + SpecialEnum::Text { + before: "answer", + content: Struct { key: "answer", val: (42, 42) }, + after: "answer", + } + => Unsupported("cannot serialize struct `Struct` as text content value")); + err!(enum_struct: + SpecialEnum::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")); + } + + /// `$value` field inside a struct variant of an enum + mod enum_with_value_field { + use super::*; + use pretty_assertions::assert_eq; + + macro_rules! value { + ($name:ident: $data:expr) => { + serialize_as!($name: + SpecialEnum::Value { + before: "answer", + content: $data, + after: "answer", + } + => "\n \ + answer\n \ + answer\n\ + "); + }; + ($name:ident: $data:expr => $expected:literal) => { + serialize_as!($name: + SpecialEnum::Value { + before: "answer", + content: $data, + after: "answer", + } + => concat!( + "\n answer\n ", + $expected, + "\n 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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + value!(char_space: ' ' => " "); + + value!(str_non_escaped: "non-escaped string" => "non-escaped string"); + value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); + + err!(bytes: + SpecialEnum::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: + SpecialEnum::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! + value!(seq: vec![1, 2, 3] => "1\n 2\n 3"); + value!(seq_empty: Vec::::new()); + value!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) + => "<"&'>\n \ + with\t\n\r spaces\n \ + 3"); + value!(tuple_struct: Tuple("first", 42) => "first\n 42"); + value!(enum_tuple: Enum::Tuple("first", 42) + => "first\n \ + 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: + SpecialEnum::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_: + SpecialEnum::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 \ + answer\n \ + 42\n \ + 42\n \ + "); + } + + mod attributes { + use super::*; + use pretty_assertions::assert_eq; + + err!(map_attr: BTreeMap::from([("@key1", 1), ("@key2", 2)]) + => Unsupported("serialization of map types is not supported in `$value` field")); + err!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)]) + => Unsupported("serialization of map types is not supported in `$value` field")); + + err!(struct_: Attributes { key: "answer", val: (42, 42) } + => Unsupported("serialization of struct `Attributes` is not supported in `$value` field")); + err!(struct_before: AttributesBefore { key: "answer", val: 42 } + => Unsupported("serialization of struct `AttributesBefore` is not supported in `$value` field")); + err!(struct_after: AttributesAfter { key: "answer", val: 42 } + => Unsupported("serialization of struct `AttributesAfter` is not supported in `$value` field")); + + serialize_as!(enum_: Enum::Attributes { key: "answer", val: (42, 42) } + => r#""#); + serialize_as!(enum_before: Enum::AttributesBefore { key: "answer", val: 42 } + => "\n \ + 42\n\ + "); + serialize_as!(enum_after: Enum::AttributesAfter { key: "answer", val: 42 } + => "\n \ + answer\n\ + "); + } + } +} diff --git a/src/se/io/element.rs b/src/se/io/element.rs new file mode 100644 index 00000000..49703f6a --- /dev/null +++ b/src/se/io/element.rs @@ -0,0 +1,2064 @@ +use super::*; +use serde::ser::Serializer; +use std::io::Write; + +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. +/// +/// 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 ([`DeError::Unsupported`] is returned); +/// +/// Usage of empty tags depends on the [`ContentSerializer::expand_empty_elements`] setting. +pub struct ElementSerializer<'w, 'k, W> { + /// 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: super::Write> Serializer for ElementSerializer<'w, 'k, W> { + type Ok = (); + type Error = DeError; + + 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 [`DeError::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(DeError::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 [`DeError::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(DeError::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: Vec::new(), + }) + } + + /// Always returns [`DeError::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(DeError::Unsupported( + format!( + "cannot serialize enum struct variant `{}::{}`", + name, variant + ) + .into(), + )) + } +} + +impl<'w, 'k, W: Write> SerializeSeq for ElementSerializer<'w, 'k, W> { + type Ok = (); + type Error = DeError; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + value.serialize(ElementSerializer { + ser: self.ser.new_seq_element_serializer(), + key: self.key, + })?; + // Write indent for the next element + self.ser.write_indent = true; + Ok(()) + } + + #[inline] + fn end(self) -> Result { + Ok(()) + } +} + +impl<'w, 'k, W: Write> SerializeTuple for ElementSerializer<'w, 'k, W> { + type Ok = (); + type Error = DeError; + + #[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 = (); + type Error = DeError; + + #[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<'k, &'w mut W>), +} + +impl<'w, 'k, W: Write> SerializeTupleVariant for Tuple<'w, 'k, W> { + type Ok = (); + type Error = DeError; + + #[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), + Self::Text(ser) => SerializeTuple::end(ser).map(|_| ()), + } + } +} + +// //////////////////////////////////////////////////////////////////////////////////////////////////// + +/// A serializer for struct variants, which serializes the struct contents inside +/// of wrapping tags (`<${tag}>...`). +/// +/// 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> { + 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: Vec, +} + +impl<'w, 'k, W: super::Write> Struct<'w, 'k, W> { + #[inline] + fn write_field(&mut self, key: &str, value: &T) -> Result<(), DeError> + 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<(), DeError> + 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, + indent: Indent::None, + })?; + 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<(), DeError> + where + T: ?Sized + Serialize, + { + let ser = ContentSerializer { + writer: &mut self.children, + level: self.ser.ser.level, + indent: self.ser.ser.indent.borrow(), + write_indent: true, + expand_empty_elements: self.ser.ser.expand_empty_elements, + }; + + if key == TEXT_KEY { + value.serialize(TextSerializer(ser.into_simple_type_serializer()))?; + } else if key == VALUE_KEY { + value.serialize(ser)?; + } else { + value.serialize(ElementSerializer { + key: XmlName::try_from(key)?, + ser, + })?; + } + Ok(()) + } +} + +impl<'w, 'k, W: super::Write> SerializeStruct for Struct<'w, 'k, W> { + type Ok = (); + type Error = DeError; + + 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(&self.children)?; + + self.ser.ser.indent.write_io_indent(&mut self.ser.ser.writer)?; + + self.ser.ser.writer.write_str("')?; + } + Ok(()) + } +} + +impl<'w, 'k, W: Write> SerializeStructVariant for Struct<'w, 'k, W> { + type Ok = (); + type Error = DeError; + + #[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(crate::se::key::QNameSerializer { + writer: String::new(), + }) + } +} + +impl<'w, 'k, W: Write> SerializeMap for Map<'w, 'k, W> { + type Ok = (); + type Error = DeError; + + fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + if let Some(_) = self.key.take() { + return Err(DeError::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(DeError::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(DeError::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 = Vec::new(); + let ser = ElementSerializer { + ser: ContentSerializer { + writer: &mut buffer, + level: QuoteLevel::Full, + indent: Indent::None, + write_indent: false, + expand_empty_elements: false, + }, + key: XmlName("root"), + }; + + $data.serialize(ser).unwrap(); + assert_eq!(buffer, $expected.as_bytes()); + } + }; + } + + /// 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 = Vec::new(); + let ser = ElementSerializer { + ser: ContentSerializer { + writer: &mut buffer, + level: QuoteLevel::Full, + indent: Indent::None, + write_indent: false, + expand_empty_elements: false, + }, + key: XmlName("root"), + }; + + match $data.serialize(ser).unwrap_err() { + DeError::$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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + 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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + 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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + 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! + value!(seq: vec![1, 2, 3] => "123"); + value!(seq_empty: Vec::::new()); + value!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) + => "<"&'>\ + with\t\n\r spaces\ + 3"); + value!(tuple_struct: Tuple("first", 42) => "first42"); + 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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + 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! + value!(seq: vec![1, 2, 3] => "123"); + value!(seq_empty: Vec::::new() => ""); + value!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) + => "<"&'>\ + with\t\n\r spaces\ + 3"); + value!(tuple_struct: Tuple("first", 42) => "first42"); + 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` + macro_rules! serialize_as { + ($name:ident: $data:expr => $expected:expr) => { + #[test] + fn $name() { + let mut buffer = Vec::new(); + let ser = ElementSerializer { + ser: ContentSerializer { + writer: &mut buffer, + level: QuoteLevel::Full, + indent: Indent::Owned(Indentation::new(b' ', 2)), + write_indent: false, + expand_empty_elements: false, + }, + key: XmlName("root"), + }; + + $data.serialize(ser).unwrap(); + assert_eq!(buffer, $expected.as_bytes()); + } + }; + } + + /// 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 = Vec::new(); + let ser = ElementSerializer { + ser: ContentSerializer { + writer: &mut buffer, + level: QuoteLevel::Full, + indent: Indent::Owned(Indentation::new(b' ', 2)), + write_indent: false, + expand_empty_elements: false, + }, + key: XmlName("root"), + }; + + match $data.serialize(ser).unwrap_err() { + DeError::$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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + 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: + BTreeMap::from([("$text", $data)]) + => ""); + }; + ($name:ident: $data:expr => $expected:literal) => { + serialize_as!($name: + BTreeMap::from([("$text", $data)]) + => concat!("\n ", $expected,"\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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + 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) => { + serialize_as!($name: + Text { + before: "answer", + content: $data, + after: "answer", + } + => "\n \ + answer\n \ + answer\n\ + "); + }; + ($name:ident: $data:expr => $expected:literal) => { + serialize_as!($name: + Text { + before: "answer", + content: $data, + after: "answer", + } + => concat!( + "\n answer\n ", + $expected, + "\n 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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + 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!("\n ", $expected,"\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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + 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"); + + value!(seq: vec![1, 2, 3] => "1\n 2\n 3"); + value!(seq_empty: Vec::::new()); + value!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) + => "<"&'>\n \ + with\t\n\r spaces\n \ + 3"); + value!(tuple_struct: Tuple("first", 42) => "first\n 42"); + value!(enum_tuple: Enum::Tuple("first", 42) + => "first\n \ + 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) } + => "\n \ + answer\n \ + 42\n \ + 42\n \ + "); + } + + /// `$value` field inside a struct + mod struct_ { + use super::*; + use pretty_assertions::assert_eq; + + macro_rules! value { + ($name:ident: $data:expr) => { + serialize_as!($name: + Value { + before: "answer", + content: $data, + after: "answer", + } + => "\n \ + answer\n \ + answer\n\ + "); + }; + ($name:ident: $data:expr => $expected:literal) => { + serialize_as!($name: + Value { + before: "answer", + content: $data, + after: "answer", + } + => concat!( + "\n answer\n ", + $expected, + "\n 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: '"' => """); + //TODO: add a setting to escape leading/trailing spaces, in order to + // pretty-print does not change the content + 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! + value!(seq: vec![1, 2, 3] => "1\n 2\n 3"); + value!(seq_empty: Vec::::new()); + value!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) + => "<"&'>\n \ + with\t\n\r spaces\n \ + 3"); + value!(tuple_struct: Tuple("first", 42) => "first\n 42"); + value!(enum_tuple: Enum::Tuple("first", 42) + => "first\n \ + 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) } + => "\n \ + answer\n \ + 42\n \ + 42\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 = Vec::new(); + let ser = ElementSerializer { + ser: ContentSerializer { + writer: &mut buffer, + level: QuoteLevel::Full, + indent: Indent::None, + write_indent: false, + expand_empty_elements: true, + }, + key: XmlName("root"), + }; + + $data.serialize(ser).unwrap(); + assert_eq!(buffer, $expected.as_bytes()); + } + }; + } + + 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 => "<"&'>"); + } +} diff --git a/src/se/io/simple_type.rs b/src/se/io/simple_type.rs new file mode 100644 index 00000000..7bf0349a --- /dev/null +++ b/src/se/io/simple_type.rs @@ -0,0 +1,309 @@ +use super::super::simple_type::*; +use super::*; + +/// 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(mut self, value: &[u8]) -> Result { + self.writer.write(&value)?; + Ok(self.writer) + } + + fn serialize_none(self) -> Result { + Ok(self.writer) + } + + fn serialize_some(self, value: &T) -> Result { + value.serialize(self) + } + + 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) + } + }; +} + + +/// A serializer for a values representing XSD [simple types], which used in: +/// - attribute values (`<... ...="value" ...>`) +/// - text content (`<...>text`) +/// - CDATA content (`<...>`) +/// +/// [simple types]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition +pub struct SimpleTypeSerializer<'i, W: Write> { + /// Writer to which this serializer writes content + pub writer: W, + /// Target for which element is serializing. Affects additional characters to escape. + pub target: QuoteTarget, + /// Defines which XML characters need to be escaped + pub level: QuoteLevel, + /// Indent that should be written before the content if content is not an empty string + pub(crate) indent: Indent<'i>, +} + +impl<'i, W: Write> SimpleTypeSerializer<'i, W> { + fn write_str(&mut self, value: &str) -> Result<(), DeError> { + self.indent.write_io_indent(&mut self.writer)?; + Ok(self.writer.write_str(value)?) + } +} + +impl<'i, W: Write> ser::Serializer for SimpleTypeSerializer<'i, W> { + type Ok = W; + type Error = DeError; + + type SerializeSeq = SimpleSeq<'i, W>; + type SerializeTuple = SimpleSeq<'i, W>; + type SerializeTupleStruct = SimpleSeq<'i, W>; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + write_primitive!(); + + fn serialize_str(mut self, value: &str) -> Result { + if !value.is_empty() { + self.write_str(&super::simple_type::escape_list(value, self.target, self.level))?; + } + Ok(self.writer) + } + + /// Does not write anything + fn serialize_unit(self) -> Result { + Ok(self.writer) + } + + /// Does not write anything + fn serialize_unit_struct(self, _name: &'static str) -> Result { + Ok(self.writer) + } + + /// 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!("cannot serialize enum newtype variant `{}::{}` as an attribute or text content value", name, variant).into(), + )) + } + + #[inline] + fn serialize_seq(self, _len: Option) -> Result { + Ok(SimpleSeq { + writer: self.writer, + target: self.target, + level: self.level, + indent: self.indent, + is_empty: true, + }) + } + + #[inline] + fn serialize_tuple(self, _len: usize) -> Result { + self.serialize_seq(None) + } + + #[inline] + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + self.serialize_seq(None) + } + + fn serialize_tuple_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + Err(DeError::Unsupported( + format!("cannot serialize enum tuple variant `{}::{}` as an attribute or text content value", name, variant).into(), + )) + } + + fn serialize_map(self, _len: Option) -> Result { + Err(DeError::Unsupported( + "cannot serialize map as an attribute or text content value".into(), + )) + } + + fn serialize_struct( + self, + name: &'static str, + _len: usize, + ) -> Result { + Err(DeError::Unsupported( + format!( + "cannot serialize struct `{}` as an attribute or text content value", + name + ) + .into(), + )) + } + + fn serialize_struct_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + Err(DeError::Unsupported( + format!("cannot serialize enum struct variant `{}::{}` as an attribute or text content value", name, variant).into(), + )) + } +} + +/// Serializer for a sequence of atomic values delimited by space +pub struct SimpleSeq<'i, W: Write> { + writer: W, + target: QuoteTarget, + level: QuoteLevel, + /// Indent that should be written before the content if content is not an empty string + indent: Indent<'i>, + /// If `true`, nothing was written yet to the `writer` + is_empty: bool, +} + +impl<'i, W: Write> SerializeSeq for SimpleSeq<'i, W> { + type Ok = W; + type Error = DeError; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + // Write indent for the first element and delimiter for others + let indent = if self.is_empty { + Some(self.indent.borrow()) + } else { + None + }; + if value.serialize(super::simple_type::AtomicSerializer { + writer: FmtWriter::ref_cast_mut(&mut self.writer), + target: self.target, + level: self.level, + indent, + })? { + self.is_empty = false; + } + Ok(()) + } + + #[inline] + fn end(self) -> Result { + Ok(self.writer) + } +} + +impl<'i, W: Write> SerializeTuple for SimpleSeq<'i, W> { + type Ok = W; + type Error = DeError; + + #[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<'i, W: Write> SerializeTupleStruct for SimpleSeq<'i, W> { + type Ok = W; + type Error = DeError; + + #[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) + } +} + +impl<'i, W: Write> SerializeTupleVariant for SimpleSeq<'i, W> { + type Ok = W; + type Error = DeError; + + #[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) + } +} + +// //////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/src/se/io/text.rs b/src/se/io/text.rs new file mode 100644 index 00000000..fc2953a0 --- /dev/null +++ b/src/se/io/text.rs @@ -0,0 +1,185 @@ +use super::*; + +// //////////////////////////////////////////////////////////////////////////////////////////////////// + +macro_rules! write_primitive_text { + ($method:ident ( $ty:ty )) => { + #[inline] + fn $method(self, value: $ty) -> Result { + self.0.$method(value) + } + }; +} + +/// A serializer used to serialize a `$text` field of a struct or map. +/// +/// This serializer a very similar to [`SimpleTypeSerializer`], but different +/// from it in how it processes unit enum variants. Unlike [`SimpleTypeSerializer`] +/// this serializer does not write anything for the unit variant. +pub struct TextSerializer<'i, W: Write>(pub SimpleTypeSerializer<'i, W>); + +impl<'i, W: Write> ser::Serializer for TextSerializer<'i, W> { + type Ok = W; + type Error = DeError; + + type SerializeSeq = SimpleSeq<'i, W>; + type SerializeTuple = SimpleSeq<'i, W>; + type SerializeTupleStruct = SimpleSeq<'i, W>; + type SerializeTupleVariant = SimpleSeq<'i, W>; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + write_primitive_text!(serialize_bool(bool)); + + write_primitive_text!(serialize_i8(i8)); + write_primitive_text!(serialize_i16(i16)); + write_primitive_text!(serialize_i32(i32)); + write_primitive_text!(serialize_i64(i64)); + + write_primitive_text!(serialize_u8(u8)); + write_primitive_text!(serialize_u16(u16)); + write_primitive_text!(serialize_u32(u32)); + write_primitive_text!(serialize_u64(u64)); + + serde_if_integer128! { + write_primitive_text!(serialize_i128(i128)); + write_primitive_text!(serialize_u128(u128)); + } + + write_primitive_text!(serialize_f32(f32)); + write_primitive_text!(serialize_f64(f64)); + + write_primitive_text!(serialize_char(char)); + write_primitive_text!(serialize_str(&str)); + write_primitive_text!(serialize_bytes(&[u8])); + + #[inline] + fn serialize_none(self) -> Result { + self.0.serialize_none() + } + + fn serialize_some(self, value: &T) -> Result { + value.serialize(self) + } + + #[inline] + fn serialize_unit(self) -> Result { + self.0.serialize_unit() + } + + #[inline] + fn serialize_unit_struct(self, name: &'static str) -> Result { + self.0.serialize_unit_struct(name) + } + + #[inline] + fn serialize_unit_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + ) -> Result { + if variant == TEXT_KEY { + Ok(self.0.writer) + } else { + self.0.serialize_unit_variant(name, variant_index, variant) + } + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result { + value.serialize(self) + } + + #[inline] + fn serialize_newtype_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _value: &T, + ) -> Result { + Err(DeError::Unsupported( + format!( + "cannot serialize enum newtype variant `{}::{}` as text content value", + name, variant + ) + .into(), + )) + } + + #[inline] + fn serialize_seq(self, len: Option) -> Result { + self.0.serialize_seq(len) + } + + #[inline] + fn serialize_tuple(self, len: usize) -> Result { + self.0.serialize_tuple(len) + } + + #[inline] + fn serialize_tuple_struct( + self, + name: &'static str, + len: usize, + ) -> Result { + self.0.serialize_tuple_struct(name, len) + } + + #[inline] + fn serialize_tuple_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + Err(DeError::Unsupported( + format!( + "cannot serialize enum tuple variant `{}::{}` as text content value", + name, variant + ) + .into(), + )) + } + + #[inline] + fn serialize_map(self, _len: Option) -> Result { + Err(DeError::Unsupported( + "cannot serialize map as text content value".into(), + )) + } + + #[inline] + fn serialize_struct( + self, + name: &'static str, + _len: usize, + ) -> Result { + Err(DeError::Unsupported( + format!("cannot serialize struct `{}` as text content value", name).into(), + )) + } + + #[inline] + fn serialize_struct_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + Err(DeError::Unsupported( + format!( + "cannot serialize enum struct variant `{}::{}` as text content value", + name, variant + ) + .into(), + )) + } +} \ No newline at end of file diff --git a/src/se/mod.rs b/src/se/mod.rs index c4699c62..608efbb2 100644 --- a/src/se/mod.rs +++ b/src/se/mod.rs @@ -368,7 +368,7 @@ const fn is_xml11_name_char(ch: char) -> bool { /// Helper struct to self-defense from errors #[derive(Clone, Copy, Debug, PartialEq)] -pub(self) struct XmlName<'n>(&'n str); +pub(crate) struct XmlName<'n>(&'n str); impl<'n> XmlName<'n> { /// Checks correctness of the XML name according to [XML 1.1 specification] @@ -614,6 +614,11 @@ impl<'w, 'r, W: Write> Serializer<'w, 'r, W> { }, }) } + + /// Get writer. + pub fn get_mut(&mut self) -> &mut W { + self.ser.writer + } } impl<'w, 'r, W: Write> ser::Serializer for Serializer<'w, 'r, W> { diff --git a/src/se/simple_type.rs b/src/se/simple_type.rs index 807c6d12..2d06e500 100644 --- a/src/se/simple_type.rs +++ b/src/se/simple_type.rs @@ -402,7 +402,7 @@ impl<'i, W: Write> Serializer for AtomicSerializer<'i, W> { /// - CDATA content (`<...>`) /// /// [simple types]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition -pub struct SimpleTypeSerializer<'i, W: Write> { +pub struct SimpleTypeSerializer<'i, W> { /// Writer to which this serializer writes content pub writer: W, /// Target for which element is serializing. Affects additional characters to escape.