From d61a5714d15179f6bce0249552dfe6771260e123 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 13 Aug 2022 14:24:31 -0700 Subject: [PATCH 1/2] Add TaggedValue to_value/from_value test Currently fails with: ---- test_tagged stdout ---- thread 'test_tagged' panicked at 'assertion failed: `(left == right)` left: `TaggedValue { tag: !Variant, value: Number(0) }`, right: `Mapping {"!Variant": Number(0)}`', tests/test_value.rs:148:5 --- tests/test_value.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/test_value.rs b/tests/test_value.rs index 391cdd10..261922b1 100644 --- a/tests/test_value.rs +++ b/tests/test_value.rs @@ -3,7 +3,7 @@ use indoc::indoc; use serde::de::IntoDeserializer; use serde::Deserialize; -use serde_derive::Deserialize; +use serde_derive::{Deserialize, Serialize}; use serde_yaml::{Number, Value}; #[test] @@ -131,3 +131,19 @@ fn test_debug() { assert_eq!(debug, expected); } + +#[test] +fn test_tagged() { + #[derive(Serialize)] + enum Enum { + Variant(usize), + } + + let value = serde_yaml::to_value(&Enum::Variant(0)).unwrap(); + + let deserialized: serde_yaml::Value = serde_yaml::from_value(value.clone()).unwrap(); + assert_eq!(value, deserialized); + + let serialized = serde_yaml::to_value(&value).unwrap(); + assert_eq!(value, serialized); +} From e66524192b2c3b0d4d8302acd9bb23207b3a1c3e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 13 Aug 2022 13:22:05 -0700 Subject: [PATCH 2/2] Fix serialization of TaggedValue in to_value --- src/value/ser.rs | 443 ++++++++++++++++++++++++++++++++++++++++++-- src/value/tagged.rs | 6 +- 2 files changed, 433 insertions(+), 16 deletions(-) diff --git a/src/value/ser.rs b/src/value/ser.rs index a00dbc5f..861ff338 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -1,6 +1,9 @@ use crate::error::Error; +use crate::value::tagged::{self, MaybeTag}; use crate::value::{to_value, Mapping, Number, Sequence, Tag, TaggedValue, Value}; use serde::ser::{self, Serialize}; +use std::fmt::Display; +use std::mem; type Result = std::result::Result; @@ -220,11 +223,15 @@ impl ser::Serializer for Serializer { }) } - fn serialize_map(self, _len: Option) -> Result { - Ok(SerializeMap { - mapping: Mapping::new(), - next_key: None, - }) + fn serialize_map(self, len: Option) -> Result { + if len == Some(1) { + Ok(SerializeMap::CheckForTag) + } else { + Ok(SerializeMap::Untagged { + mapping: Mapping::new(), + next_key: None, + }) + } } fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { @@ -325,9 +332,13 @@ impl ser::SerializeTupleVariant for SerializeTupleVariant { } } -pub struct SerializeMap { - mapping: Mapping, - next_key: Option, +pub enum SerializeMap { + CheckForTag, + Tagged(TaggedValue), + Untagged { + mapping: Mapping, + next_key: Option, + }, } impl ser::SerializeMap for SerializeMap { @@ -338,7 +349,27 @@ impl ser::SerializeMap for SerializeMap { where T: ?Sized + ser::Serialize, { - self.next_key = Some(to_value(key)?); + let key = Some(to_value(key)?); + match self { + SerializeMap::CheckForTag => { + *self = SerializeMap::Untagged { + mapping: Mapping::new(), + next_key: key, + }; + } + SerializeMap::Tagged(tagged) => { + let mut mapping = Mapping::new(); + mapping.insert( + Value::String(tagged.tag.to_string()), + mem::take(&mut tagged.value), + ); + *self = SerializeMap::Untagged { + mapping, + next_key: key, + }; + } + SerializeMap::Untagged { next_key, .. } => *next_key = key, + } Ok(()) } @@ -346,8 +377,12 @@ impl ser::SerializeMap for SerializeMap { where T: ?Sized + ser::Serialize, { - match self.next_key.take() { - Some(key) => self.mapping.insert(key, to_value(value)?), + let (mapping, key) = match self { + SerializeMap::CheckForTag | SerializeMap::Tagged(_) => unreachable!(), + SerializeMap::Untagged { mapping, next_key } => (mapping, next_key), + }; + match key.take() { + Some(key) => mapping.insert(key, to_value(value)?), None => panic!("serialize_value called before serialize_key"), }; Ok(()) @@ -358,12 +393,394 @@ impl ser::SerializeMap for SerializeMap { K: ?Sized + ser::Serialize, V: ?Sized + ser::Serialize, { - self.mapping.insert(to_value(key)?, to_value(value)?); + struct CheckForTag; + struct NotTag { + delegate: T, + } + + impl ser::Serializer for CheckForTag { + type Ok = MaybeTag; + type Error = Error; + + type SerializeSeq = NotTag; + type SerializeTuple = NotTag; + type SerializeTupleStruct = NotTag; + type SerializeTupleVariant = NotTag; + type SerializeMap = NotTag; + type SerializeStruct = NotTag; + type SerializeStructVariant = NotTag; + + fn serialize_bool(self, v: bool) -> Result { + Serializer.serialize_bool(v).map(MaybeTag::NotTag) + } + + fn serialize_i8(self, v: i8) -> Result { + Serializer.serialize_i8(v).map(MaybeTag::NotTag) + } + + fn serialize_i16(self, v: i16) -> Result { + Serializer.serialize_i16(v).map(MaybeTag::NotTag) + } + + fn serialize_i32(self, v: i32) -> Result { + Serializer.serialize_i32(v).map(MaybeTag::NotTag) + } + + fn serialize_i64(self, v: i64) -> Result { + Serializer.serialize_i64(v).map(MaybeTag::NotTag) + } + + fn serialize_i128(self, v: i128) -> Result { + Serializer.serialize_i128(v).map(MaybeTag::NotTag) + } + + fn serialize_u8(self, v: u8) -> Result { + Serializer.serialize_u8(v).map(MaybeTag::NotTag) + } + + fn serialize_u16(self, v: u16) -> Result { + Serializer.serialize_u16(v).map(MaybeTag::NotTag) + } + + fn serialize_u32(self, v: u32) -> Result { + Serializer.serialize_u32(v).map(MaybeTag::NotTag) + } + + fn serialize_u64(self, v: u64) -> Result { + Serializer.serialize_u64(v).map(MaybeTag::NotTag) + } + + fn serialize_u128(self, v: u128) -> Result { + Serializer.serialize_u128(v).map(MaybeTag::NotTag) + } + + fn serialize_f32(self, v: f32) -> Result { + Serializer.serialize_f32(v).map(MaybeTag::NotTag) + } + + fn serialize_f64(self, v: f64) -> Result { + Serializer.serialize_f64(v).map(MaybeTag::NotTag) + } + + fn serialize_char(self, value: char) -> Result { + Serializer.serialize_char(value).map(MaybeTag::NotTag) + } + + fn serialize_str(self, value: &str) -> Result { + Serializer.serialize_str(value).map(MaybeTag::NotTag) + } + + fn serialize_bytes(self, value: &[u8]) -> Result { + Serializer.serialize_bytes(value).map(MaybeTag::NotTag) + } + + fn serialize_unit(self) -> Result { + Serializer.serialize_unit().map(MaybeTag::NotTag) + } + + fn serialize_unit_struct(self, name: &'static str) -> Result { + Serializer.serialize_unit_struct(name).map(MaybeTag::NotTag) + } + + fn serialize_unit_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + ) -> Result { + Serializer + .serialize_unit_variant(name, variant_index, variant) + .map(MaybeTag::NotTag) + } + + fn serialize_newtype_struct(self, name: &'static str, value: &T) -> Result + where + T: ?Sized + ser::Serialize, + { + Serializer + .serialize_newtype_struct(name, value) + .map(MaybeTag::NotTag) + } + + fn serialize_newtype_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + ser::Serialize, + { + Serializer + .serialize_newtype_variant(name, variant_index, variant, value) + .map(MaybeTag::NotTag) + } + + fn serialize_none(self) -> Result { + Serializer.serialize_none().map(MaybeTag::NotTag) + } + + fn serialize_some(self, value: &V) -> Result + where + V: ?Sized + ser::Serialize, + { + Serializer.serialize_some(value).map(MaybeTag::NotTag) + } + + fn serialize_seq(self, len: Option) -> Result { + Ok(NotTag { + delegate: Serializer.serialize_seq(len)?, + }) + } + + fn serialize_tuple(self, len: usize) -> Result { + Ok(NotTag { + delegate: Serializer.serialize_tuple(len)?, + }) + } + + fn serialize_tuple_struct( + self, + name: &'static str, + len: usize, + ) -> Result { + Ok(NotTag { + delegate: Serializer.serialize_tuple_struct(name, len)?, + }) + } + + fn serialize_tuple_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + Ok(NotTag { + delegate: Serializer.serialize_tuple_variant( + name, + variant_index, + variant, + len, + )?, + }) + } + + fn serialize_map(self, len: Option) -> Result { + Ok(NotTag { + delegate: Serializer.serialize_map(len)?, + }) + } + + fn serialize_struct( + self, + name: &'static str, + len: usize, + ) -> Result { + Ok(NotTag { + delegate: Serializer.serialize_struct(name, len)?, + }) + } + + fn serialize_struct_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + Ok(NotTag { + delegate: Serializer.serialize_struct_variant( + name, + variant_index, + variant, + len, + )?, + }) + } + + fn collect_str(self, value: &T) -> Result + where + T: ?Sized + Display, + { + Ok(match tagged::check_for_tag(value) { + MaybeTag::Tag(tag) => MaybeTag::Tag(tag), + MaybeTag::NotTag(string) => MaybeTag::NotTag(Value::String(string)), + }) + } + } + + impl ser::SerializeSeq for NotTag { + type Ok = MaybeTag; + type Error = Error; + + fn serialize_element(&mut self, elem: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + self.delegate.serialize_element(elem) + } + + fn end(self) -> Result { + self.delegate.end().map(MaybeTag::NotTag) + } + } + + impl ser::SerializeTuple for NotTag { + type Ok = MaybeTag; + type Error = Error; + + fn serialize_element(&mut self, elem: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + self.delegate.serialize_element(elem) + } + + fn end(self) -> Result { + self.delegate.end().map(MaybeTag::NotTag) + } + } + + impl ser::SerializeTupleStruct for NotTag { + type Ok = MaybeTag; + type Error = Error; + + fn serialize_field(&mut self, value: &V) -> Result<()> + where + V: ?Sized + ser::Serialize, + { + self.delegate.serialize_field(value) + } + + fn end(self) -> Result { + self.delegate.end().map(MaybeTag::NotTag) + } + } + + impl ser::SerializeTupleVariant for NotTag { + type Ok = MaybeTag; + type Error = Error; + + fn serialize_field(&mut self, v: &V) -> Result<()> + where + V: ?Sized + ser::Serialize, + { + self.delegate.serialize_field(v) + } + + fn end(self) -> Result { + self.delegate.end().map(MaybeTag::NotTag) + } + } + + impl ser::SerializeMap for NotTag { + type Ok = MaybeTag; + type Error = Error; + + fn serialize_key(&mut self, key: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + self.delegate.serialize_key(key) + } + + fn serialize_value(&mut self, value: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + self.delegate.serialize_value(value) + } + + fn serialize_entry(&mut self, key: &K, value: &V) -> Result<()> + where + K: ?Sized + ser::Serialize, + V: ?Sized + ser::Serialize, + { + self.delegate.serialize_entry(key, value) + } + + fn end(self) -> Result { + self.delegate.end().map(MaybeTag::NotTag) + } + } + + impl ser::SerializeStruct for NotTag { + type Ok = MaybeTag; + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &V) -> Result<()> + where + V: ?Sized + ser::Serialize, + { + self.delegate.serialize_field(key, value) + } + + fn end(self) -> Result { + self.delegate.end().map(MaybeTag::NotTag) + } + } + + impl ser::SerializeStructVariant for NotTag { + type Ok = MaybeTag; + type Error = Error; + + fn serialize_field(&mut self, field: &'static str, v: &V) -> Result<()> + where + V: ?Sized + ser::Serialize, + { + self.delegate.serialize_field(field, v) + } + + fn end(self) -> Result { + self.delegate.end().map(MaybeTag::NotTag) + } + } + + match self { + SerializeMap::CheckForTag => { + let key = key.serialize(CheckForTag)?; + let mut mapping = Mapping::new(); + *self = match key { + MaybeTag::Tag(string) => SerializeMap::Tagged(TaggedValue { + tag: Tag::new(string), + value: to_value(value)?, + }), + MaybeTag::NotTag(key) => { + mapping.insert(key, to_value(value)?); + SerializeMap::Untagged { + mapping, + next_key: None, + } + } + }; + } + SerializeMap::Tagged(tagged) => { + let mut mapping = Mapping::new(); + mapping.insert( + Value::String(tagged.tag.to_string()), + mem::take(&mut tagged.value), + ); + mapping.insert(to_value(key)?, to_value(value)?); + *self = SerializeMap::Untagged { + mapping, + next_key: None, + }; + } + SerializeMap::Untagged { mapping, .. } => { + mapping.insert(to_value(key)?, to_value(value)?); + } + } Ok(()) } fn end(self) -> Result { - Ok(Value::Mapping(self.mapping)) + Ok(match self { + SerializeMap::CheckForTag => Value::Mapping(Mapping::new()), + SerializeMap::Tagged(tagged) => Value::Tagged(Box::new(tagged)), + SerializeMap::Untagged { mapping, .. } => Value::Mapping(mapping), + }) } } diff --git a/src/value/tagged.rs b/src/value/tagged.rs index 3bebf0eb..74593630 100644 --- a/src/value/tagged.rs +++ b/src/value/tagged.rs @@ -323,12 +323,12 @@ impl<'de> VariantAccess<'de> for &'de Value { } } -pub(crate) enum MaybeTag { +pub(crate) enum MaybeTag { Tag(String), - NotTag(String), + NotTag(T), } -pub(crate) fn check_for_tag(value: &T) -> MaybeTag +pub(crate) fn check_for_tag(value: &T) -> MaybeTag where T: ?Sized + Display, {