Add support for deserialization of custom type/shared value from into another Dynamic.
This commit is contained in:
parent
72cfd42ab7
commit
ae59a3321b
@ -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
|
||||
|
@ -57,7 +57,7 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||
// 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.
|
||||
|
@ -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
|
||||
}};
|
||||
}
|
||||
|
@ -124,6 +124,10 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
type Error = RhaiError;
|
||||
|
||||
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if type_name::<V::Value>() == type_name::<Dynamic>() {
|
||||
return Ok(reify! { self.0.clone() => !!! V::Value });
|
||||
}
|
||||
|
||||
match self.0 .0 {
|
||||
Union::Unit(..) => self.deserialize_unit(visitor),
|
||||
Union::Bool(..) => self.deserialize_bool(visitor),
|
||||
|
@ -1184,89 +1184,89 @@ impl Dynamic {
|
||||
self.flatten_in_place();
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
||||
return Some(reify! { self => T });
|
||||
return Some(reify! { self => !!! T });
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||
return match self.0 {
|
||||
Union::Unit(..) => Some(reify! { () => T }),
|
||||
Union::Unit(..) => Some(reify! { () => !!! T }),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||
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::<T>() == TypeId::of::<crate::FLOAT>() {
|
||||
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::<T>() == TypeId::of::<rust_decimal::Decimal>() {
|
||||
return match self.0 {
|
||||
Union::Decimal(v, ..) => Some(reify! { *v => T }),
|
||||
Union::Decimal(v, ..) => Some(reify! { *v => !!! T }),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||
return match self.0 {
|
||||
Union::Bool(b, ..) => Some(reify! { b => T }),
|
||||
Union::Bool(b, ..) => Some(reify! { b => !!! T }),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||
return match self.0 {
|
||||
Union::Str(s, ..) => Some(reify! { s => T }),
|
||||
Union::Str(s, ..) => Some(reify! { s => !!! T }),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||
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::<T>() == TypeId::of::<char>() {
|
||||
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::<T>() == TypeId::of::<crate::Array>() {
|
||||
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::<T>() == TypeId::of::<crate::Blob>() {
|
||||
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::<T>() == TypeId::of::<crate::Map>() {
|
||||
return match self.0 {
|
||||
Union::Map(m, ..) => Some(reify! { *m => T }),
|
||||
Union::Map(m, ..) => Some(reify! { *m => !!! T }),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||
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::<T>() == TypeId::of::<Instant>() {
|
||||
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<T: Any + Clone>(self) -> T {
|
||||
// Bail out early if the return type needs no cast
|
||||
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
||||
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::<T>() == TypeId::of::<u8>() => {
|
||||
Ok(reify! { *b => Vec<T> })
|
||||
Ok(reify! { *b => !!! Vec<T> })
|
||||
}
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => {
|
||||
@ -2061,7 +2061,7 @@ impl Dynamic {
|
||||
.collect()
|
||||
}
|
||||
Union::Blob(ref b, ..) if TypeId::of::<T>() == TypeId::of::<u8>() => {
|
||||
Ok(reify! { b.clone() => Vec<T> })
|
||||
Ok(reify! { b.clone() => !!! Vec<T> })
|
||||
}
|
||||
_ => Err(cell.type_name()),
|
||||
}
|
||||
|
@ -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<EvalAltResult>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[test]
|
||||
fn test_serde_de_variants() -> Result<(), Box<EvalAltResult>> {
|
||||
#[derive(Debug)]
|
||||
struct Foo;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Bar {
|
||||
#[serde(deserialize_with = "deserialize_foo")]
|
||||
value: Arc<Foo>,
|
||||
}
|
||||
|
||||
fn deserialize_foo<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Arc<Foo>, D::Error> {
|
||||
let value = <Dynamic as Deserialize>::deserialize(deserializer)?;
|
||||
|
||||
value
|
||||
.try_cast::<Arc<Foo>>()
|
||||
.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::<Bar>(&x)?;
|
||||
|
||||
assert!(Arc::ptr_eq(&bar.value, &value));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_de_integer_types() -> Result<(), Box<EvalAltResult>> {
|
||||
assert_eq!(42, from_dynamic::<i8>(&Dynamic::from(42 as INT))?);
|
||||
|
Loading…
Reference in New Issue
Block a user