From 11fa348e9fd087f5d38f8d3d4c115eba40a4b32a Mon Sep 17 00:00:00 2001 From: Mingun Date: Mon, 16 Oct 2023 01:50:07 +0500 Subject: [PATCH] Change serialization of enums in ElementSerializer and document it Now only unit variants can be serialized, all other returns error failures (4): without_root::enum_::externally_tagged::normal_field2::unit without_root::enum_::externally_tagged::normal_field::unit without_root::enum_::externally_tagged::text_variant::normal_field::unit without_root::enum_::externally_tagged::text_variant::text_field::unit Fixed (24): --lib (14, all) se::element::tests::expand_empty_elements::enum_unit se::element::tests::expand_empty_elements::enum_unit_escaped se::element::tests::with_indent::attributes::enum_ se::element::tests::with_indent::enum_newtype se::element::tests::with_indent::enum_struct se::element::tests::with_indent::enum_tuple se::element::tests::with_indent::enum_unit se::element::tests::with_indent::enum_unit_escaped se::element::tests::without_indent::attributes::enum_ se::element::tests::without_indent::enum_newtype se::element::tests::without_indent::enum_struct se::element::tests::without_indent::enum_tuple se::element::tests::without_indent::enum_unit se::element::tests::without_indent::enum_unit_escaped serde-se (10): without_root::enum_::externally_tagged::normal_field2::empty_struct without_root::enum_::externally_tagged::normal_field2::newtype without_root::enum_::externally_tagged::normal_field2::struct_ without_root::enum_::externally_tagged::normal_field2::tuple without_root::enum_::externally_tagged::normal_field::empty_struct without_root::enum_::externally_tagged::normal_field::newtype without_root::enum_::externally_tagged::normal_field::struct_ without_root::enum_::externally_tagged::normal_field::tuple without_root::enum_::externally_tagged::text_variant::normal_field::newtype without_root::enum_::externally_tagged::text_variant::normal_field::tuple --- src/se/element.rs | 131 ++++++++++++++++++++++++++++------------------ tests/serde-se.rs | 2 +- 2 files changed, 81 insertions(+), 52 deletions(-) diff --git a/src/se/element.rs b/src/se/element.rs index 45037d64..c39d5122 100644 --- a/src/se/element.rs +++ b/src/se/element.rs @@ -7,12 +7,13 @@ use crate::se::key::QNameSerializer; use crate::se::simple_type::{QuoteTarget, SimpleSeq, SimpleTypeSerializer}; use crate::se::{Indent, XmlName}; use serde::ser::{ - Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, - SerializeTupleStruct, SerializeTupleVariant, Serializer, + Impossible, Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, + SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, Serializer, }; use serde::serde_if_integer128; use std::fmt::Write; +/// Writes simple type content between [`ElementSerializer::key`] tags. macro_rules! write_primitive { ($method:ident ( $ty:ty )) => { fn $method(self, value: $ty) -> Result { @@ -23,8 +24,39 @@ macro_rules! write_primitive { //////////////////////////////////////////////////////////////////////////////////////////////////// -/// A serializer used to serialize element with specified name. +/// 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>, @@ -37,7 +69,7 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> { type SerializeSeq = Self; type SerializeTuple = Self; type SerializeTupleStruct = Self; - type SerializeTupleVariant = Tuple<'w, 'k, W>; + type SerializeTupleVariant = Impossible; type SerializeMap = Map<'w, 'k, W>; type SerializeStruct = Struct<'w, 'k, W>; type SerializeStructVariant = Struct<'w, 'k, W>; @@ -103,24 +135,21 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> { 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_index: u32, variant: &'static str, ) -> Result { if variant == TEXT_KEY { - // We should write some text but we don't known what text to write - Err(DeError::Unsupported( - format!( - "cannot serialize enum unit variant `{}::$text` as text content value", - name - ) - .into(), - )) + self.ser.write_empty(self.key) } else { - let name = XmlName::try_from(variant)?; - self.ser.write_empty(name) + self.ser.write_wrapped(self.key, |ser| { + ser.serialize_unit_variant(name, variant_index, variant) + }) } } @@ -132,20 +161,23 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> { 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( - mut self, - _name: &'static str, + self, + name: &'static str, _variant_index: u32, variant: &'static str, - value: &T, + _value: &T, ) -> Result { - if variant == TEXT_KEY { - value.serialize(self.ser.into_simple_type_serializer())?; - Ok(()) - } else { - self.key = XmlName::try_from(variant)?; - value.serialize(self) - } + Err(DeError::Unsupported( + format!( + "cannot serialize enum newtype variant `{}::{}`", + name, variant + ) + .into(), + )) } #[inline] @@ -167,23 +199,23 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> { 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( - mut self, + self, name: &'static str, _variant_index: u32, variant: &'static str, - len: usize, + _len: usize, ) -> Result { - if variant == TEXT_KEY { - self.ser - .into_simple_type_serializer() - .serialize_tuple_struct(name, len) - .map(Tuple::Text) - } else { - self.key = XmlName::try_from(variant)?; - self.serialize_tuple_struct(name, len).map(Tuple::Element) - } + Err(DeError::Unsupported( + format!( + "cannot serialize enum tuple variant `{}::{}`", + name, variant + ) + .into(), + )) } fn serialize_map(self, _len: Option) -> Result { @@ -210,26 +242,23 @@ impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> { }) } + /// Always returns [`DeError::Unsupported`]. Struct variants can be serialized + /// only in `$value` fields, which is serialized using [`ContentSerializer`]. #[inline] fn serialize_struct_variant( - mut self, + self, name: &'static str, _variant_index: u32, variant: &'static str, - len: usize, + _len: usize, ) -> Result { - if variant == TEXT_KEY { - Err(DeError::Unsupported( - format!( - "cannot serialize enum struct variant `{}::$text` as text content value", - name - ) - .into(), - )) - } else { - self.key = XmlName::try_from(variant)?; - self.serialize_struct(name, len) - } + Err(DeError::Unsupported( + format!( + "cannot serialize enum struct variant `{}::{}`", + name, variant + ) + .into(), + )) } } @@ -245,7 +274,7 @@ impl<'w, 'k, W: Write> SerializeSeq for ElementSerializer<'w, 'k, W> { ser: self.ser.new_seq_element_serializer(), key: self.key, })?; - // Write indent for next element + // Write indent for the next element self.ser.write_indent = true; Ok(()) } diff --git a/tests/serde-se.rs b/tests/serde-se.rs index 58bd8196..f8c43d07 100644 --- a/tests/serde-se.rs +++ b/tests/serde-se.rs @@ -536,7 +536,7 @@ mod without_root { float: 42.0, string: "answer" }} - => Unsupported("cannot serialize enum struct variant `Struct::$text` as text content value"), + => Unsupported("cannot serialize enum struct variant `Struct::$text`"), "