From 478e19a836bab054c38237214a30f1312d3ac672 Mon Sep 17 00:00:00 2001 From: Mingun Date: Tue, 10 Oct 2023 23:36:51 +0500 Subject: [PATCH] Fix serialization of `$text` variants in `$text` fields Fixed (1): without_root::enum_::externally_tagged::text_variant::text_field::uni --- src/se/content.rs | 20 ++--- src/se/element.rs | 43 +++++----- src/se/mod.rs | 1 + src/se/simple_type.rs | 21 ++++- src/se/text.rs | 192 ++++++++++++++++++++++++++++++++++++++++++ tests/serde-se.rs | 34 ++++---- 6 files changed, 262 insertions(+), 49 deletions(-) create mode 100644 src/se/text.rs diff --git a/src/se/content.rs b/src/se/content.rs index 3b851ce5..984689f0 100644 --- a/src/se/content.rs +++ b/src/se/content.rs @@ -781,7 +781,7 @@ pub(super) mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); + => 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"); @@ -798,7 +798,7 @@ pub(super) mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -807,21 +807,21 @@ pub(super) mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("cannot serialize map as an attribute or text content value")); + => 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 an attribute or text content value")); + => 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 an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); } /// `$value` field inside a struct variant of an enum @@ -1234,7 +1234,7 @@ pub(super) mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); + => 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"); @@ -1251,7 +1251,7 @@ pub(super) mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -1260,21 +1260,21 @@ pub(super) mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("cannot serialize map as an attribute or text content value")); + => 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 an attribute or text content value")); + => 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 an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); } /// `$value` field inside a struct variant of an enum diff --git a/src/se/element.rs b/src/se/element.rs index c39d5122..afad2a97 100644 --- a/src/se/element.rs +++ b/src/se/element.rs @@ -5,6 +5,7 @@ use crate::errors::serialize::DeError; use crate::se::content::ContentSerializer; use crate::se::key::QNameSerializer; use crate::se::simple_type::{QuoteTarget, SimpleSeq, SimpleTypeSerializer}; +use crate::se::text::TextSerializer; use crate::se::{Indent, XmlName}; use serde::ser::{ Impossible, Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, @@ -438,7 +439,7 @@ impl<'w, 'k, W: Write> Struct<'w, 'k, W> { }; if key == TEXT_KEY { - value.serialize(ser.into_simple_type_serializer())?; + value.serialize(TextSerializer(ser.into_simple_type_serializer()))?; } else if key == VALUE_KEY { value.serialize(ser)?; } else { @@ -821,7 +822,7 @@ mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); + => 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"); @@ -838,7 +839,7 @@ mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -847,21 +848,21 @@ mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("cannot serialize map as an attribute or text content value")); + => 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 an attribute or text content value")); + => 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 an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); } /// `$text` field inside a struct @@ -948,7 +949,7 @@ mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); + => 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"); @@ -965,7 +966,7 @@ mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -974,21 +975,21 @@ mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("cannot serialize map as an attribute or text content value")); + => 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 an attribute or text content value")); + => 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 an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); } } @@ -1527,7 +1528,7 @@ mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); + => 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"); @@ -1544,7 +1545,7 @@ mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -1553,21 +1554,21 @@ mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("cannot serialize map as an attribute or text content value")); + => 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 an attribute or text content value")); + => 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 an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); } /// `$text` field inside a struct @@ -1666,7 +1667,7 @@ mod tests { content: Enum::Newtype(42), after: "answer", } - => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value")); + => 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"); @@ -1683,7 +1684,7 @@ mod tests { content: Enum::Tuple("first", 42), after: "answer", } - => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value")); + => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); // Complex types cannot be serialized in `$text` field err!(map: @@ -1692,21 +1693,21 @@ mod tests { content: BTreeMap::from([("_1", 2), ("_3", 4)]), after: "answer", } - => Unsupported("cannot serialize map as an attribute or text content value")); + => 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 an attribute or text content value")); + => 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 an attribute or text content value")); + => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); } } diff --git a/src/se/mod.rs b/src/se/mod.rs index b45ba683..839187f3 100644 --- a/src/se/mod.rs +++ b/src/se/mod.rs @@ -77,6 +77,7 @@ mod content; mod element; pub(crate) mod key; pub(crate) mod simple_type; +mod text; use self::content::ContentSerializer; use self::element::{ElementSerializer, Map, Struct, Tuple}; diff --git a/src/se/simple_type.rs b/src/se/simple_type.rs index dad26b1b..e396606d 100644 --- a/src/se/simple_type.rs +++ b/src/se/simple_type.rs @@ -7,7 +7,8 @@ use crate::errors::serialize::DeError; use crate::escapei::_escape; use crate::se::{Indent, QuoteLevel}; use serde::ser::{ - Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct, Serializer, + Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct, + SerializeTupleVariant, Serializer, }; use serde::serde_if_integer128; use std::borrow::Cow; @@ -612,6 +613,24 @@ impl<'i, W: Write> SerializeTupleStruct for SimpleSeq<'i, W> { } } +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) + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////// #[cfg(test)] diff --git a/src/se/text.rs b/src/se/text.rs new file mode 100644 index 00000000..4dec138d --- /dev/null +++ b/src/se/text.rs @@ -0,0 +1,192 @@ +//! Contains serializer for a special `&text` field + +use crate::de::TEXT_KEY; +use crate::errors::serialize::DeError; +use crate::se::simple_type::{SimpleSeq, SimpleTypeSerializer}; +use serde::ser::{Impossible, Serialize, Serializer}; +use serde::serde_if_integer128; +use std::fmt::Write; + +macro_rules! write_primitive { + ($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> 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!(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_str(&str)); + write_primitive!(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(), + )) + } +} diff --git a/tests/serde-se.rs b/tests/serde-se.rs index f8c43d07..4b7fd43e 100644 --- a/tests/serde-se.rs +++ b/tests/serde-se.rs @@ -627,18 +627,18 @@ mod without_root { serialize_as_only!(unit: Root { field: Unit::Text } => ""); err!(newtype: Root { field: Newtype::Text("newtype text") } - => Unsupported("cannot serialize enum newtype variant `Newtype::$text` as an attribute or text content value"), + => Unsupported("cannot serialize enum newtype variant `Newtype::$text` as text content value"), " Unsupported("cannot serialize enum tuple variant `Tuple::$text` as an attribute or text content value"), + => Unsupported("cannot serialize enum tuple variant `Tuple::$text` as text content value"), " Unsupported("cannot serialize enum struct variant `Struct::$text` as an attribute or text content value"), + => Unsupported("cannot serialize enum struct variant `Struct::$text` as text content value"), " "Unit"); err!(newtype: Root { field: ExternallyTagged::Newtype(true) } - => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype` as an attribute or text content value"), + => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype` as text content value"), " Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple` as an attribute or text content value"), + => Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple` as text content value"), " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct` as an attribute or text content value"), + => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct` as text content value"), " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Holder` as an attribute or text content value"), + => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Holder` as text content value"), " Unsupported("cannot serialize enum newtype variant `ExternallyTaggedWorkaround::Flatten` as an attribute or text content value"), + => Unsupported("cannot serialize enum newtype variant `ExternallyTaggedWorkaround::Flatten` as text content value"), " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty` as an attribute or text content value"), + => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty` as text content value"), " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Text` as an attribute or text content value"), + => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Text` as text content value"), " "Unit"); err!(newtype: Root { field: Inner { inner: ExternallyTagged::Newtype(true) } } - => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype` as an attribute or text content value"), + => Unsupported("cannot serialize enum newtype variant `ExternallyTagged::Newtype` as text content value"), " Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple` as an attribute or text content value"), + => Unsupported("cannot serialize enum tuple variant `ExternallyTagged::Tuple` as text content value"), " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct` as an attribute or text content value"), + => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Struct` as text content value"), " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Holder` as an attribute or text content value"), + => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Holder` as text content value"), " Unsupported("cannot serialize enum newtype variant `ExternallyTaggedWorkaround::Flatten` as an attribute or text content value"), + => Unsupported("cannot serialize enum newtype variant `ExternallyTaggedWorkaround::Flatten` as text content value"), " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty` as an attribute or text content value"), + => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Empty` as text content value"), " Unsupported("cannot serialize enum struct variant `ExternallyTagged::Text` as an attribute or text content value"), + => Unsupported("cannot serialize enum struct variant `ExternallyTagged::Text` as text content value"), "