From 58a7101f68ecacc7de1f711f1c25c7ea458c9fdd Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 12 Sep 2023 19:54:54 -0600 Subject: [PATCH] fix(serde): Support struct variants as table of a table --- crates/toml/src/value.rs | 46 +++++++++++++++++++++++++--- crates/toml/tests/testsuite/serde.rs | 19 +++++++++--- crates/toml_edit/src/ser/map.rs | 39 ++++++++++++++++++++++- crates/toml_edit/src/ser/value.rs | 10 +++--- 4 files changed, 99 insertions(+), 15 deletions(-) diff --git a/crates/toml/src/value.rs b/crates/toml/src/value.rs index b1c7b829..b98196ad 100644 --- a/crates/toml/src/value.rs +++ b/crates/toml/src/value.rs @@ -887,7 +887,7 @@ impl ser::Serializer for ValueSerializer { type SerializeTupleVariant = ValueSerializeTupleVariant; type SerializeMap = ValueSerializeMap; type SerializeStruct = ValueSerializeMap; - type SerializeStructVariant = ser::Impossible; + type SerializeStructVariant = ValueSerializeStructVariant; fn serialize_bool(self, value: bool) -> Result { Ok(Value::Boolean(value)) @@ -1054,12 +1054,12 @@ impl ser::Serializer for ValueSerializer { fn serialize_struct_variant( self, - name: &'static str, + _name: &'static str, _variant_index: u32, - _variant: &'static str, - _len: usize, + variant: &'static str, + len: usize, ) -> Result { - Err(crate::ser::Error::unsupported_type(Some(name))) + Ok(ValueSerializeStructVariant::struct_(variant, len)) } } @@ -1469,6 +1469,7 @@ impl<'a, 'de> de::Visitor<'de> for DatetimeOrTable<'a> { } type ValueSerializeTupleVariant = ValueSerializeVariant; +type ValueSerializeStructVariant = ValueSerializeVariant; struct ValueSerializeVariant { variant: &'static str, @@ -1486,6 +1487,20 @@ impl ValueSerializeVariant { } } +impl ValueSerializeVariant { + pub(crate) fn struct_(variant: &'static str, len: usize) -> Self { + Self { + variant, + inner: ValueSerializeMap { + ser: SerializeMap { + map: Table::with_capacity(len), + next_key: None, + }, + }, + } + } +} + impl serde::ser::SerializeTupleVariant for ValueSerializeVariant { type Ok = crate::Value; type Error = crate::ser::Error; @@ -1504,3 +1519,24 @@ impl serde::ser::SerializeTupleVariant for ValueSerializeVariant { + type Ok = crate::Value; + type Error = crate::ser::Error; + + #[inline] + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> + where + T: serde::ser::Serialize + ?Sized, + { + serde::ser::SerializeStruct::serialize_field(&mut self.inner, key, value) + } + + #[inline] + fn end(self) -> Result { + let inner = serde::ser::SerializeStruct::end(self.inner)?; + let mut table = Table::new(); + table.insert(self.variant.to_owned(), inner); + Ok(Value::Table(table)) + } +} diff --git a/crates/toml/tests/testsuite/serde.rs b/crates/toml/tests/testsuite/serde.rs index b7e00018..82ba00b1 100644 --- a/crates/toml/tests/testsuite/serde.rs +++ b/crates/toml/tests/testsuite/serde.rs @@ -452,10 +452,21 @@ fn parse_struct_variant() { }, ], }; - let err = toml::to_string(&input).unwrap_err(); - snapbox::assert_eq("unsupported Enum type", err.to_string()); + let expected = "[[inner]] + +[inner.Int] +first = 1 +second = 1 + +[[inner]] + +[inner.String] +first = \"2\" +second = \"2\" +"; + let raw = toml::to_string(&input).unwrap(); + snapbox::assert_eq(expected, raw); - /* equivalent! { Document { inner: vec![ @@ -469,7 +480,7 @@ fn parse_struct_variant() { map! { String: map! { first: "2".to_owned(), second: "2".to_owned() } }, ] }, - }*/ + } } #[test] diff --git a/crates/toml_edit/src/ser/map.rs b/crates/toml_edit/src/ser/map.rs index 6721e160..47e56ba4 100644 --- a/crates/toml_edit/src/ser/map.rs +++ b/crates/toml_edit/src/ser/map.rs @@ -425,7 +425,7 @@ impl serde::ser::Serializer for &mut MapValueSerializer { type SerializeTupleVariant = super::SerializeTupleVariant; type SerializeMap = super::SerializeMap; type SerializeStruct = super::SerializeMap; - type SerializeStructVariant = serde::ser::Impossible; + type SerializeStructVariant = super::SerializeStructVariant; fn serialize_bool(self, v: bool) -> Result { ValueSerializer::new().serialize_bool(v) @@ -586,6 +586,7 @@ impl serde::ser::Serializer for &mut MapValueSerializer { } pub type SerializeTupleVariant = SerializeVariant; +pub type SerializeStructVariant = SerializeVariant; pub struct SerializeVariant { variant: &'static str, @@ -601,6 +602,15 @@ impl SerializeVariant { } } +impl SerializeVariant { + pub(crate) fn struct_(variant: &'static str, len: usize) -> Self { + Self { + variant, + inner: SerializeMap::table_with_capacity(len), + } + } +} + impl serde::ser::SerializeTupleVariant for SerializeVariant { type Ok = crate::Value; type Error = Error; @@ -625,3 +635,30 @@ impl serde::ser::SerializeTupleVariant for SerializeVariant ))) } } + +impl serde::ser::SerializeStructVariant for SerializeVariant { + type Ok = crate::Value; + type Error = Error; + + #[inline] + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> + where + T: serde::ser::Serialize + ?Sized, + { + serde::ser::SerializeStruct::serialize_field(&mut self.inner, key, value) + } + + #[inline] + fn end(self) -> Result { + let inner = serde::ser::SerializeStruct::end(self.inner)?; + let mut items = crate::table::KeyValuePairs::new(); + let kv = crate::table::TableKeyValue::new( + crate::Key::new(self.variant), + crate::Item::Value(inner), + ); + items.insert(crate::InternalString::from(self.variant), kv); + Ok(crate::Value::InlineTable(crate::InlineTable::with_pairs( + items, + ))) + } +} diff --git a/crates/toml_edit/src/ser/value.rs b/crates/toml_edit/src/ser/value.rs index a094a85b..808bb900 100644 --- a/crates/toml_edit/src/ser/value.rs +++ b/crates/toml_edit/src/ser/value.rs @@ -63,7 +63,7 @@ impl serde::ser::Serializer for ValueSerializer { type SerializeTupleVariant = super::SerializeTupleVariant; type SerializeMap = super::SerializeMap; type SerializeStruct = super::SerializeMap; - type SerializeStructVariant = serde::ser::Impossible; + type SerializeStructVariant = super::SerializeStructVariant; fn serialize_bool(self, v: bool) -> Result { Ok(v.into()) @@ -233,11 +233,11 @@ impl serde::ser::Serializer for ValueSerializer { fn serialize_struct_variant( self, - name: &'static str, + _name: &'static str, _variant_index: u32, - _variant: &'static str, - _len: usize, + variant: &'static str, + len: usize, ) -> Result { - Err(Error::UnsupportedType(Some(name))) + Ok(super::SerializeStructVariant::struct_(variant, len)) } }