From ae59a3321befb97b27b3d7730a7397ee38d676cc Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 28 Apr 2023 23:16:00 +0800 Subject: [PATCH] Add support for deserialization of custom type/shared value from into another Dynamic. --- CHANGELOG.md | 1 + src/func/register.rs | 2 +- src/reify.rs | 9 +++++++++ src/serde/de.rs | 4 ++++ src/types/dynamic.rs | 34 +++++++++++++++++----------------- tests/serde.rs | 34 +++++++++++++++++++++++++++++++++- 6 files changed, 65 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fad9dcf..36936da7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Enhancements ------------ * `Engine::is_symbol_disabled` is added to test whether a particular keyword/symbol is disabled. +* Support is added to deserialize a `Dynamic` value containing custom types or shared values back into another `Dynamic` (essentially a straight cloned copy). Version 1.13.0 diff --git a/src/func/register.rs b/src/func/register.rs index 5e410dd7..9f5901ec 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -57,7 +57,7 @@ pub fn by_value(data: &mut Dynamic) -> T { } if TypeId::of::() == TypeId::of::() { // If T is `String`, data must be `ImmutableString`, so map directly to it - return reify! { data.take().into_string().expect("`ImmutableString`") => T }; + return reify! { data.take().into_string().expect("`ImmutableString`") => !!! T }; } // We consume the argument and then replace it with () - the argument is not supposed to be used again. diff --git a/src/reify.rs b/src/reify.rs index cc8b6647..4c9506a8 100644 --- a/src/reify.rs +++ b/src/reify.rs @@ -8,6 +8,8 @@ /// * `reify! { `_variable_ or _expression_` => |`_temp-variable_`: `_type_`|` _code_ `)` /// * `reify! { `_variable_ or _expression_ `=>` `Option<`_type_`>` `)` /// * `reify! { `_variable_ or _expression_ `=>` _type_ `)` +/// +/// * `reify! { `_expression_ `=> !!!` _type_ `)` (unsafe, no type checks!) macro_rules! reify { ($old:ident => |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{ #[allow(clippy::redundant_else)] @@ -45,4 +47,11 @@ macro_rules! reify { ($old:expr => $t:ty) => { reify! { $old => |v: $t| v, || unreachable!() } }; + + ($old:expr => !!! $t:ty) => {{ + let old_value = $old; + let new_value: $t = + unsafe { std::mem::transmute_copy(&std::mem::ManuallyDrop::new(old_value)) }; + new_value + }}; } diff --git a/src/serde/de.rs b/src/serde/de.rs index 30a1de6f..681a6817 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -124,6 +124,10 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { type Error = RhaiError; fn deserialize_any>(self, visitor: V) -> RhaiResultOf { + if type_name::() == type_name::() { + return Ok(reify! { self.0.clone() => !!! V::Value }); + } + match self.0 .0 { Union::Unit(..) => self.deserialize_unit(visitor), Union::Bool(..) => self.deserialize_bool(visitor), diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index f1bf1d2b..c3887d86 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -1184,89 +1184,89 @@ impl Dynamic { self.flatten_in_place(); if TypeId::of::() == TypeId::of::() { - return Some(reify! { self => T }); + return Some(reify! { self => !!! T }); } if TypeId::of::() == TypeId::of::<()>() { return match self.0 { - Union::Unit(..) => Some(reify! { () => T }), + Union::Unit(..) => Some(reify! { () => !!! T }), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Int(n, ..) => Some(reify! { n => T }), + Union::Int(n, ..) => Some(reify! { n => !!! T }), _ => None, }; } #[cfg(not(feature = "no_float"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Float(v, ..) => Some(reify! { *v => T }), + Union::Float(v, ..) => Some(reify! { *v => !!! T }), _ => None, }; } #[cfg(feature = "decimal")] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Decimal(v, ..) => Some(reify! { *v => T }), + Union::Decimal(v, ..) => Some(reify! { *v => !!! T }), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Bool(b, ..) => Some(reify! { b => T }), + Union::Bool(b, ..) => Some(reify! { b => !!! T }), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Str(s, ..) => Some(reify! { s => T }), + Union::Str(s, ..) => Some(reify! { s => !!! T }), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Str(s, ..) => Some(reify! { s.to_string() => T }), + Union::Str(s, ..) => Some(reify! { s.to_string() => !!! T }), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Char(c, ..) => Some(reify! { c => T }), + Union::Char(c, ..) => Some(reify! { c => !!! T }), _ => None, }; } #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Array(a, ..) => Some(reify! { *a => T }), + Union::Array(a, ..) => Some(reify! { *a => !!! T }), _ => None, }; } #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Blob(b, ..) => Some(reify! { *b => T }), + Union::Blob(b, ..) => Some(reify! { *b => !!! T }), _ => None, }; } #[cfg(not(feature = "no_object"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::Map(m, ..) => Some(reify! { *m => T }), + Union::Map(m, ..) => Some(reify! { *m => !!! T }), _ => None, }; } if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::FnPtr(f, ..) => Some(reify! { *f => T }), + Union::FnPtr(f, ..) => Some(reify! { *f => !!! T }), _ => None, }; } #[cfg(not(feature = "no_time"))] if TypeId::of::() == TypeId::of::() { return match self.0 { - Union::TimeStamp(t, ..) => Some(reify! { *t => T }), + Union::TimeStamp(t, ..) => Some(reify! { *t => !!! T }), _ => None, }; } @@ -1306,7 +1306,7 @@ impl Dynamic { pub fn cast(self) -> T { // Bail out early if the return type needs no cast if TypeId::of::() == TypeId::of::() { - return reify! { self => T }; + return reify! { self => !!! T }; } #[cfg(not(feature = "no_closure"))] @@ -2038,7 +2038,7 @@ impl Dynamic { }) .collect(), Union::Blob(b, ..) if TypeId::of::() == TypeId::of::() => { - Ok(reify! { *b => Vec }) + Ok(reify! { *b => !!! Vec }) } #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, ..) => { @@ -2061,7 +2061,7 @@ impl Dynamic { .collect() } Union::Blob(ref b, ..) if TypeId::of::() == TypeId::of::() => { - Ok(reify! { b.clone() => Vec }) + Ok(reify! { b.clone() => !!! Vec }) } _ => Err(cell.type_name()), } diff --git a/tests/serde.rs b/tests/serde.rs index fb0ce51e..c8309cef 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -4,8 +4,9 @@ use rhai::{ serde::{from_dynamic, to_dynamic}, Dynamic, Engine, EvalAltResult, ImmutableString, Scope, INT, }; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use serde_json::json; +use std::sync::Arc; #[cfg(not(feature = "no_index"))] use rhai::Array; @@ -383,6 +384,37 @@ fn test_serde_de_primary_types() -> Result<(), Box> { Ok(()) } +#[cfg(not(feature = "no_object"))] +#[test] +fn test_serde_de_variants() -> Result<(), Box> { + #[derive(Debug)] + struct Foo; + + #[derive(Debug, Deserialize)] + struct Bar { + #[serde(deserialize_with = "deserialize_foo")] + value: Arc, + } + + fn deserialize_foo<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + let value = ::deserialize(deserializer)?; + + value + .try_cast::>() + .ok_or_else(|| serde::de::Error::custom("type error")) + } + + let value = Arc::new(Foo); + let mut map = Map::new(); + map.insert("value".into(), Dynamic::from(value.clone())); + let x = Dynamic::from(map); + let bar = from_dynamic::(&x)?; + + assert!(Arc::ptr_eq(&bar.value, &value)); + + Ok(()) +} + #[test] fn test_serde_de_integer_types() -> Result<(), Box> { assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?);