diff --git a/rmpv-tests/tests/de_ref.rs b/rmpv-tests/tests/de_ref.rs index 7bb22ee9..e687b2bc 100644 --- a/rmpv-tests/tests/de_ref.rs +++ b/rmpv-tests/tests/de_ref.rs @@ -280,6 +280,106 @@ fn pass_enum_from_value() { deserialize_from(ValueRef::Array(vec![ValueRef::from(3), ValueRef::Array(vec![ValueRef::from("John"), ValueRef::from(42)])])).unwrap()); } +#[test] +fn pass_map_encoded_enum_variants_from_value_ref() { + #[derive(Debug, PartialEq, Deserialize)] + enum Enum<'a> { + Unit, + EmptyTuple(), + EmptyStruct {}, + Newtype(Vec), + Tuple(&'a str, u32), + Struct { name: &'a str, age: u32 }, + } + + let unit = ValueRef::Map(vec![(ValueRef::from("Unit"), ValueRef::Nil)]); + assert_eq!(Enum::Unit, deserialize_from(unit).unwrap()); + for payload in [ValueRef::Array(vec![]), ValueRef::Map(vec![])] { + let value = ValueRef::Map(vec![(ValueRef::from("EmptyStruct"), payload)]); + assert_eq!(Enum::EmptyStruct {}, deserialize_from(value).unwrap()); + } + let value = ValueRef::Map(vec![(ValueRef::from("EmptyTuple"), ValueRef::Array(vec![]))]); + assert_eq!(Enum::EmptyTuple(), deserialize_from(value).unwrap()); + for values in [vec![], vec![1], vec![1, 2]] { + let expected = values.clone(); + let value = ValueRef::Map(vec![ + (ValueRef::from("Newtype"), ValueRef::Array(values.into_iter().map(ValueRef::from).collect())), + ]); + let decoded: Enum<'_> = deserialize_from(value).unwrap(); + match decoded { + Enum::Newtype(actual) => assert_eq!(expected, actual), + _ => panic!("unexpected enum variant"), + } + } + let value = ValueRef::Map(vec![ + (ValueRef::from("Tuple"), ValueRef::Array(vec![ValueRef::from("John"), ValueRef::from(42)])), + ]); + assert_eq!(Enum::Tuple("John", 42), deserialize_from(value).unwrap()); + + let value = ValueRef::Map(vec![ + (ValueRef::from("Struct"), ValueRef::Map(vec![ + (ValueRef::from("name"), ValueRef::from("John")), + (ValueRef::from("age"), ValueRef::from(42)), + ])), + ]); + let decoded: Enum<'_> = deserialize_from(value).unwrap(); + assert_eq!(Enum::Struct { name: "John", age: 42 }, decoded); + + for (index, value) in [ + ValueRef::Map(vec![]), + ValueRef::Map(vec![(ValueRef::from("Unit"), ValueRef::Nil), (ValueRef::from("Unit"), ValueRef::Nil)]), + ValueRef::Map(vec![(ValueRef::Array(vec![]), ValueRef::Nil)]), + ValueRef::Map(vec![(ValueRef::from("Unknown"), ValueRef::Nil)]), + ValueRef::Map(vec![(ValueRef::from("Tuple"), ValueRef::from(1))]), + ].into_iter().enumerate() { + assert!(deserialize_from::, _>(value).is_err(), "case {index}"); + } +} + +#[test] +fn pass_borrowed_map_enum_from_reference() { + #[derive(Debug, PartialEq, Deserialize)] + enum Enum<'a> { + Unit, + EmptyTuple(), + EmptyStruct {}, + Newtype(&'a str), + Tuple(&'a str, u32), + Struct { name: &'a str, age: u32 }, + } + + let value = ValueRef::Map(vec![(ValueRef::from("Unit"), ValueRef::Nil)]); + assert_eq!(Enum::Unit, serde::Deserialize::deserialize(&value).unwrap()); + for payload in [ValueRef::Array(vec![]), ValueRef::Map(vec![])] { + let value = ValueRef::Map(vec![(ValueRef::from("EmptyStruct"), payload)]); + let decoded: Enum<'_> = serde::Deserialize::deserialize(&value).unwrap(); + assert_eq!(Enum::EmptyStruct {}, decoded); + } + let value = ValueRef::Map(vec![(ValueRef::from("EmptyTuple"), ValueRef::Array(vec![]))]); + assert_eq!(Enum::EmptyTuple(), serde::Deserialize::deserialize(&value).unwrap()); + let value = ValueRef::Map(vec![(ValueRef::from("Newtype"), ValueRef::from("borrowed"))]); + let decoded: Enum<'_> = serde::Deserialize::deserialize(&value).unwrap(); + assert_eq!(Enum::Newtype("borrowed"), decoded); + let value = ValueRef::Map(vec![(ValueRef::from("Tuple"), ValueRef::Array(vec![ValueRef::from("John"), ValueRef::from(42)]))]); + assert_eq!(Enum::Tuple("John", 42), serde::Deserialize::deserialize(&value).unwrap()); + let value = ValueRef::Map(vec![(ValueRef::from("Struct"), ValueRef::Map(vec![ + (ValueRef::from("name"), ValueRef::from("John")), + (ValueRef::from("age"), ValueRef::from(42)), + ]))]); + assert_eq!(Enum::Struct { name: "John", age: 42 }, serde::Deserialize::deserialize(&value).unwrap()); + + for (index, value) in [ + ValueRef::Map(vec![]), + ValueRef::Map(vec![(ValueRef::from("Unit"), ValueRef::Nil), (ValueRef::from("Unit"), ValueRef::Nil)]), + ValueRef::Map(vec![(ValueRef::Array(vec![]), ValueRef::Nil)]), + ValueRef::Map(vec![(ValueRef::from("Unknown"), ValueRef::Nil)]), + ValueRef::Map(vec![(ValueRef::from("Tuple"), ValueRef::from(1))]), + ].into_iter().enumerate() { + let decoded: Result, _> = serde::Deserialize::deserialize(&value); + assert!(decoded.is_err(), "case {index}"); + } +} + #[test] fn pass_from_slice() { let buf = [0x93, 0xa4, 0x4a, 0x6f, 0x68, 0x6e, 0xa5, 0x53, 0x6d, 0x69, 0x74, 0x68, 0x2a]; diff --git a/rmpv-tests/tests/decode.rs b/rmpv-tests/tests/decode.rs index 53e193f4..075cdba9 100644 --- a/rmpv-tests/tests/decode.rs +++ b/rmpv-tests/tests/decode.rs @@ -1,4 +1,4 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use serde_bytes::ByteBuf; use std::collections::BTreeMap; @@ -263,6 +263,97 @@ fn pass_enum_from_value() { from_value(Value::Array(vec![Value::from(3), Value::Array(vec![Value::from("John"), Value::from(42)])])).unwrap()); } +#[test] +fn pass_externally_tagged_enum_map_from_value() { + for result in [Ok(1), Err(2)] { + let encoded = rmp_serde::to_vec_named(&result).unwrap(); + assert_eq!( + encoded, + if result.is_ok() { + vec![0x81, 0xa2, b'O', b'k', 0x01] + } else { + vec![0x81, 0xa3, b'E', b'r', b'r', 0x02] + } + ); + let value: Value = rmp_serde::from_slice(&encoded).unwrap(); + let decoded: Result = from_value(value).unwrap(); + assert_eq!(result, decoded); + } +} + +#[test] +fn pass_map_encoded_enum_variants_from_value() { + #[derive(Debug, PartialEq, Deserialize)] + enum Enum { + Unit, + EmptyTuple(), + EmptyStruct {}, + Newtype(Vec), + Tuple(String, u32), + Struct { name: String, age: u32 }, + } + + assert_eq!(Enum::Unit, from_value(Value::Map(vec![(Value::from("Unit"), Value::Nil)])).unwrap()); + assert_eq!(Enum::EmptyTuple(), from_value(Value::Map(vec![(Value::from("EmptyTuple"), Value::Array(vec![]))])).unwrap()); + assert_eq!(Enum::EmptyStruct {}, from_value(Value::Map(vec![(Value::from("EmptyStruct"), Value::Array(vec![]))])).unwrap()); + assert_eq!(Enum::EmptyStruct {}, from_value(Value::Map(vec![( + Value::from("EmptyStruct"), + Value::Map(vec![]), + )])).unwrap()); + for values in [vec![], vec![1], vec![1, 2]] { + assert_eq!(Enum::Newtype(values.clone()), from_value(Value::Map(vec![ + (Value::from("Newtype"), Value::Array(values.into_iter().map(Value::from).collect())), + ])).unwrap()); + } + assert_eq!(Enum::Tuple("John".into(), 42), from_value(Value::Map(vec![ + (Value::from("Tuple"), Value::Array(vec![Value::from("John"), Value::from(42)])), + ])).unwrap()); + assert_eq!(Enum::Struct { name: "John".into(), age: 42 }, from_value(Value::Map(vec![ + (Value::from("Struct"), Value::Map(vec![ + (Value::from("name"), Value::from("John")), + (Value::from("age"), Value::from(42)), + ])), + ])).unwrap()); +} + +#[test] +fn reject_malformed_enum_maps_from_value() { + #[derive(Debug, PartialEq, Deserialize)] + enum Enum { + Unit, + } + + for value in [ + Value::Map(vec![]), + Value::Map(vec![(Value::from("Unit"), Value::Nil), (Value::from("Unit"), Value::Nil)]), + Value::Map(vec![(Value::from("Unknown"), Value::Nil)]), + Value::Map(vec![(Value::from("Unit"), Value::from(1))]), + ] { + assert!(from_value::(value).is_err()); + } +} + +#[test] +fn pass_serializer_generated_empty_enum_variants_from_value() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + enum Enum { + Tuple(), + Struct {}, + } + + for expected in [Enum::Tuple(), Enum::Struct {}] { + let encoded = rmp_serde::to_vec_named(&expected).unwrap(); + let fixture = match &expected { + Enum::Tuple() => vec![0x81, 0xa5, b'T', b'u', b'p', b'l', b'e', 0x90], + Enum::Struct {} => vec![0x81, 0xa6, b'S', b't', b'r', b'u', b'c', b't', 0x80], + }; + assert_eq!(encoded, fixture); + let value: Value = rmp_serde::from_slice(&encoded).unwrap(); + let decoded: Enum = from_value(value).unwrap(); + assert_eq!(expected, decoded); + } +} + #[test] fn pass_tuple_struct_from_ext() { #[derive(Debug, PartialEq)] diff --git a/rmpv/src/ext/de.rs b/rmpv/src/ext/de.rs index 63eb7d27..17387a42 100644 --- a/rmpv/src/ext/de.rs +++ b/rmpv/src/ext/de.rs @@ -363,7 +363,16 @@ impl<'de> Deserializer<'de> for Value { fn deserialize_enum(self, _name: &str, _variants: &'static [&'static str], visitor: V) -> Result where V: Visitor<'de> { - ValueBase::deserialize_enum(self, visitor) + match self { + Self::Map(mut values) if values.len() == 1 => { + match values.pop() { + Some((key, value)) => visitor.visit_enum(MapEnumDeserializer::new(key, value)), + None => Err(de::Error::invalid_length(0, &"map with one element")), + } + }, + Self::Map(values) => Err(de::Error::invalid_length(values.len(), &"map with one element")), + value => ValueBase::deserialize_enum(value, visitor), + } } #[inline] @@ -456,7 +465,16 @@ impl<'de> Deserializer<'de> for ValueRef<'de> { fn deserialize_enum(self, _name: &str, _variants: &'static [&'static str], visitor: V) -> Result where V: Visitor<'de> { - ValueBase::deserialize_enum(self, visitor) + match self { + ValueRef::Map(mut values) if values.len() == 1 => { + match values.pop() { + Some((key, value)) => visitor.visit_enum(MapEnumDeserializer::new(key, value)), + None => Err(de::Error::invalid_length(0, &"map with one element")), + } + }, + ValueRef::Map(values) => Err(de::Error::invalid_length(values.len(), &"map with one element")), + value => ValueBase::deserialize_enum(value, visitor), + } } #[inline] @@ -570,6 +588,11 @@ impl<'de> Deserializer<'de> for &'de ValueRef<'de> { visitor.visit_enum(EnumRefDeserializer::new(id, iter.next())) }, + ValueRef::Map(v) if v.len() == 1 => { + let (key, value) = &v[0]; + visitor.visit_enum(MapEnumDeserializer::new(key, value)) + }, + ValueRef::Map(v) => Err(de::Error::invalid_length(v.len(), &"map with one element")), other => Err(de::Error::invalid_type(other.unexpected(), &"array, map or int")), } } @@ -838,6 +861,84 @@ impl<'de, U: ValueBase<'de> + ValueExt> de::EnumAccess<'de> for EnumDeserializer } } +struct MapEnumDeserializer { + key: K, + value: V, +} + +impl MapEnumDeserializer { + const fn new(key: K, value: V) -> Self { + Self { key, value } + } +} + +impl<'de, K, V> de::EnumAccess<'de> for MapEnumDeserializer +where + K: Deserializer<'de, Error = Error>, + V: Deserializer<'de, Error = Error>, +{ + type Error = Error; + type Variant = MapVariantDeserializer; + + fn variant_seed(self, seed: S) -> Result<(S::Value, Self::Variant), Self::Error> + where + S: de::DeserializeSeed<'de>, + { + seed.deserialize(self.key) + .map(|variant| (variant, MapVariantDeserializer { value: self.value })) + } +} + +struct MapVariantDeserializer { + value: V, +} + +impl<'de, V> de::VariantAccess<'de> for MapVariantDeserializer +where + V: Deserializer<'de, Error = Error>, +{ + type Error = Error; + + fn unit_variant(self) -> Result<(), Error> { + struct UnitVisitor; + + impl<'de> Visitor<'de> for UnitVisitor { + type Value = (); + + fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { + formatter.write_str("unit variant") + } + + fn visit_unit(self) -> Result<(), E> { + Ok(()) + } + } + + self.value.deserialize_unit(UnitVisitor) + } + + fn newtype_variant_seed(self, seed: T) -> Result + where + T: de::DeserializeSeed<'de>, + { + seed.deserialize(self.value) + } + + fn tuple_variant(self, _len: usize, visitor: A) -> Result + where + A: Visitor<'de>, + { + self.value.deserialize_seq(visitor) + } + + fn struct_variant(self, _fields: &'static [&'static str], visitor: A) -> Result + where + A: Visitor<'de>, + { + self.value.deserialize_any(visitor) + } +} + struct VariantDeserializer { value: Option, }