diff --git a/src/serde/de.rs b/src/serde/de.rs index f91c051d..88533718 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -7,7 +7,10 @@ use crate::result::EvalAltResult; use crate::token::Position; use crate::utils::ImmutableString; -use serde::de::{DeserializeSeed, Deserializer, Error, MapAccess, SeqAccess, Visitor}; +use serde::de::{ + DeserializeSeed, Deserializer, EnumAccess, Error, IntoDeserializer, MapAccess, SeqAccess, + VariantAccess, Visitor, +}; use serde::Deserialize; #[cfg(not(feature = "no_index"))] @@ -384,9 +387,30 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self, _name: &'static str, _variants: &'static [&'static str], - _: V, + visitor: V, ) -> Result> { - self.type_error() + if let Ok(s) = self.value.as_str() { + visitor.visit_enum(s.into_deserializer()) + } else { + #[cfg(not(feature = "no_object"))] + if let Some(map) = self.value.downcast_ref::() { + let mut iter = map.iter(); + let first = iter.next(); + let second = iter.next(); + if let (Some((key, value)), None) = (first, second) { + visitor.visit_enum(EnumDeserializer { + tag: &key, + content: DynamicDeserializer::from_dynamic(value), + }) + } else { + self.type_error() + } + } else { + self.type_error() + } + #[cfg(feature = "no_object")] + return self.type_error(); + } } fn deserialize_identifier>( @@ -494,3 +518,57 @@ where )) } } + +#[cfg(not(feature = "no_object"))] +struct EnumDeserializer<'t, 'de: 't> { + tag: &'t str, + content: DynamicDeserializer<'de>, +} + +#[cfg(not(feature = "no_object"))] +impl<'t, 'de> EnumAccess<'de> for EnumDeserializer<'t, 'de> { + type Error = Box; + type Variant = Self; + + fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> + where + V: DeserializeSeed<'de>, + { + seed.deserialize(self.tag.into_deserializer()) + .map(|v| (v, self)) + } +} + +#[cfg(not(feature = "no_object"))] +impl<'t, 'de> VariantAccess<'de> for EnumDeserializer<'t, 'de> { + type Error = Box; + + fn unit_variant(mut self) -> Result<(), Self::Error> { + Deserialize::deserialize(&mut self.content) + } + + fn newtype_variant_seed(mut self, seed: T) -> Result + where + T: DeserializeSeed<'de>, + { + seed.deserialize(&mut self.content) + } + + fn tuple_variant(mut self, len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.content.deserialize_tuple(len, visitor) + } + + fn struct_variant( + mut self, + fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.content.deserialize_struct("", fields, visitor) + } +} diff --git a/tests/serde.rs b/tests/serde.rs index 95a3c24e..1d509eea 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -236,3 +236,261 @@ fn test_serde_de_script() -> Result<(), Box> { Ok(()) } + +#[test] +fn test_serde_de_unit_enum() -> Result<(), Box> { + #[derive(Debug, PartialEq, Deserialize)] + enum MyEnum { + VariantFoo, + VariantBar, + } + + { + let d = Dynamic::from("VariantFoo".to_string()); + assert_eq!(MyEnum::VariantFoo, from_dynamic(&d)?); + } + + { + let d = Dynamic::from("VariantBar".to_string()); + assert_eq!(MyEnum::VariantBar, from_dynamic(&d)?); + } + + Ok(()) +} + +#[test] +#[cfg(not(feature = "no_object"))] +fn test_serde_de_externally_tagged_enum() -> Result<(), Box> { + #[derive(Debug, PartialEq, Deserialize)] + #[serde(deny_unknown_fields)] + enum MyEnum { + VariantUnit, + #[cfg(not(feature = "no_index"))] + VariantUnitTuple(), + VariantNewtype(i32), + #[cfg(not(feature = "no_index"))] + VariantTuple(i32, i32), + VariantEmptyStruct {}, + VariantStruct { + a: i32, + }, + } + + { + let d = Dynamic::from("VariantUnit".to_string()); + assert_eq!(MyEnum::VariantUnit, from_dynamic(&d).unwrap()); + } + + #[cfg(not(feature = "no_index"))] + { + let array: Array = vec![]; + let mut map_outer = Map::new(); + map_outer.insert("VariantUnitTuple".into(), array.into()); + assert_eq!( + MyEnum::VariantUnitTuple(), + from_dynamic(&map_outer.into()).unwrap() + ); + } + + { + let mut map_outer = Map::new(); + map_outer.insert("VariantNewtype".into(), (123 as INT).into()); + assert_eq!( + MyEnum::VariantNewtype(123), + from_dynamic(&map_outer.into()).unwrap() + ); + } + + #[cfg(not(feature = "no_index"))] + { + let array: Array = vec![(123 as INT).into(), (456 as INT).into()]; + let mut map_outer = Map::new(); + map_outer.insert("VariantTuple".into(), array.into()); + assert_eq!( + MyEnum::VariantTuple(123, 456), + from_dynamic(&map_outer.into()).unwrap() + ); + } + + { + let map_inner = Map::new(); + let mut map_outer = Map::new(); + map_outer.insert("VariantEmptyStruct".into(), map_inner.into()); + assert_eq!( + MyEnum::VariantEmptyStruct {}, + from_dynamic(&map_outer.into()).unwrap() + ); + } + + { + let mut map_inner = Map::new(); + map_inner.insert("a".into(), (123 as INT).into()); + let mut map_outer = Map::new(); + map_outer.insert("VariantStruct".into(), map_inner.into()); + assert_eq!( + MyEnum::VariantStruct { a: 123 }, + from_dynamic(&map_outer.into()).unwrap() + ); + } + + Ok(()) +} + +#[test] +#[cfg(not(feature = "no_object"))] +fn test_serde_de_internally_tagged_enum() -> Result<(), Box> { + #[derive(Debug, PartialEq, Deserialize)] + #[serde(tag = "tag", deny_unknown_fields)] + enum MyEnum { + VariantEmptyStruct {}, + VariantStruct { a: i32 }, + } + + { + let mut map = Map::new(); + map.insert("tag".into(), "VariantStruct".into()); + map.insert("a".into(), (123 as INT).into()); + assert_eq!( + MyEnum::VariantStruct { a: 123 }, + from_dynamic(&map.into()).unwrap() + ); + } + + { + let mut map = Map::new(); + map.insert("tag".into(), "VariantEmptyStruct".into()); + assert_eq!( + MyEnum::VariantEmptyStruct {}, + from_dynamic(&map.into()).unwrap() + ); + } + + Ok(()) +} + +#[test] +#[cfg(not(feature = "no_object"))] +fn test_serde_de_adjacently_tagged_enum() -> Result<(), Box> { + #[derive(Debug, PartialEq, Deserialize)] + #[serde(tag = "tag", content = "content", deny_unknown_fields)] + enum MyEnum { + VariantUnit, + #[cfg(not(feature = "no_index"))] + VariantUnitTuple(), + VariantNewtype(i32), + #[cfg(not(feature = "no_index"))] + VariantTuple(i32, i32), + VariantEmptyStruct {}, + VariantStruct { + a: i32, + }, + } + + { + let mut map_outer = Map::new(); + map_outer.insert("tag".into(), "VariantUnit".into()); + assert_eq!( + MyEnum::VariantUnit, + from_dynamic(&map_outer.into()).unwrap() + ); + } + + #[cfg(not(feature = "no_index"))] + { + let array: Array = vec![]; + let mut map_outer = Map::new(); + map_outer.insert("tag".into(), "VariantUnitTuple".into()); + map_outer.insert("content".into(), array.into()); + assert_eq!( + MyEnum::VariantUnitTuple(), + from_dynamic(&map_outer.into()).unwrap() + ); + } + + { + let mut map_outer = Map::new(); + map_outer.insert("tag".into(), "VariantNewtype".into()); + map_outer.insert("content".into(), (123 as INT).into()); + assert_eq!( + MyEnum::VariantNewtype(123), + from_dynamic(&map_outer.into()).unwrap() + ); + } + + #[cfg(not(feature = "no_index"))] + { + let array: Array = vec![(123 as INT).into(), (456 as INT).into()]; + let mut map_outer = Map::new(); + map_outer.insert("tag".into(), "VariantTuple".into()); + map_outer.insert("content".into(), array.into()); + assert_eq!( + MyEnum::VariantTuple(123, 456), + from_dynamic(&map_outer.into()).unwrap() + ); + } + + { + let map_inner = Map::new(); + let mut map_outer = Map::new(); + map_outer.insert("tag".into(), "VariantEmptyStruct".into()); + map_outer.insert("content".into(), map_inner.into()); + assert_eq!( + MyEnum::VariantEmptyStruct {}, + from_dynamic(&map_outer.into()).unwrap() + ); + } + + { + let mut map_inner = Map::new(); + map_inner.insert("a".into(), (123 as INT).into()); + let mut map_outer = Map::new(); + map_outer.insert("tag".into(), "VariantStruct".into()); + map_outer.insert("content".into(), map_inner.into()); + assert_eq!( + MyEnum::VariantStruct { a: 123 }, + from_dynamic(&map_outer.into()).unwrap() + ); + } + + Ok(()) +} + +#[test] +#[cfg(not(feature = "no_object"))] +fn test_serde_de_untagged_enum() -> Result<(), Box> { + #[derive(Debug, PartialEq, Deserialize)] + #[serde(untagged, deny_unknown_fields)] + enum MyEnum { + VariantEmptyStruct {}, + VariantStruct1 { a: i32 }, + VariantStruct2 { b: i32 }, + } + + { + let map = Map::new(); + assert_eq!( + MyEnum::VariantEmptyStruct {}, + from_dynamic(&map.into()).unwrap() + ); + } + + { + let mut map = Map::new(); + map.insert("a".into(), (123 as INT).into()); + assert_eq!( + MyEnum::VariantStruct1 { a: 123 }, + from_dynamic(&map.into()).unwrap() + ); + } + + { + let mut map = Map::new(); + map.insert("b".into(), (123 as INT).into()); + assert_eq!( + MyEnum::VariantStruct2 { b: 123 }, + from_dynamic(&map.into()).unwrap() + ); + } + + Ok(()) +}