Add serde deserialization.
This commit is contained in:
parent
1b7ffdf408
commit
fa84e5c502
@ -21,7 +21,7 @@ num-traits = { version = "0.2.11", default-features = false }
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
#default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"]
|
#default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"]
|
||||||
default = []
|
default = ["serde"]
|
||||||
plugins = []
|
plugins = []
|
||||||
unchecked = [] # unchecked arithmetic
|
unchecked = [] # unchecked arithmetic
|
||||||
sync = [] # restrict to only types that implement Send + Sync
|
sync = [] # restrict to only types that implement Send + Sync
|
||||||
@ -65,5 +65,11 @@ default-features = false
|
|||||||
features = ["compile-time-rng"]
|
features = ["compile-time-rng"]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.serde]
|
||||||
|
package = "serde"
|
||||||
|
version = "1.0.111"
|
||||||
|
features = ["derive"]
|
||||||
|
optional = true
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
instant= { version = "0.1.4", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant
|
instant= { version = "0.1.4", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant
|
||||||
|
@ -38,6 +38,7 @@ Features
|
|||||||
* Dynamic dispatch via [function pointers](https://schungx.github.io/rhai/language/fn-ptr.html).
|
* Dynamic dispatch via [function pointers](https://schungx.github.io/rhai/language/fn-ptr.html).
|
||||||
* Some support for [object-oriented programming (OOP)](https://schungx.github.io/rhai/language/oop.html).
|
* Some support for [object-oriented programming (OOP)](https://schungx.github.io/rhai/language/oop.html).
|
||||||
* Organize code base with dynamically-loadable [modules](https://schungx.github.io/rhai/language/modules.html).
|
* Organize code base with dynamically-loadable [modules](https://schungx.github.io/rhai/language/modules.html).
|
||||||
|
* Serialization/deserialization support via [serde](https://crates.io/crates/serde)
|
||||||
* Scripts are [optimized](https://schungx.github.io/rhai/engine/optimize.html) (useful for template-based machine-generated scripts) for repeated evaluations.
|
* Scripts are [optimized](https://schungx.github.io/rhai/engine/optimize.html) (useful for template-based machine-generated scripts) for repeated evaluations.
|
||||||
* Support for [minimal builds](https://schungx.github.io/rhai/start/builds/minimal.html) by excluding unneeded language [features](https://schungx.github.io/rhai/start/features.html).
|
* Support for [minimal builds](https://schungx.github.io/rhai/start/builds/minimal.html) by excluding unneeded language [features](https://schungx.github.io/rhai/start/features.html).
|
||||||
|
|
||||||
|
@ -96,14 +96,15 @@ The Rhai Scripting Language
|
|||||||
9. [Maximum Statement Depth](safety/max-stmt-depth.md)
|
9. [Maximum Statement Depth](safety/max-stmt-depth.md)
|
||||||
8. [Advanced Topics](advanced.md)
|
8. [Advanced Topics](advanced.md)
|
||||||
1. [Object-Oriented Programming (OOP)](language/oop.md)
|
1. [Object-Oriented Programming (OOP)](language/oop.md)
|
||||||
2. [Script Optimization](engine/optimize/index.md)
|
2. [Serialization/Deserialization of `Dynamic` with `serde`](rust/serde.md)
|
||||||
|
3. [Script Optimization](engine/optimize/index.md)
|
||||||
1. [Optimization Levels](engine/optimize/optimize-levels.md)
|
1. [Optimization Levels](engine/optimize/optimize-levels.md)
|
||||||
2. [Re-Optimize an AST](engine/optimize/reoptimize.md)
|
2. [Re-Optimize an AST](engine/optimize/reoptimize.md)
|
||||||
3. [Eager Function Evaluation](engine/optimize/eager.md)
|
3. [Eager Function Evaluation](engine/optimize/eager.md)
|
||||||
4. [Side-Effect Considerations](engine/optimize/side-effects.md)
|
4. [Side-Effect Considerations](engine/optimize/side-effects.md)
|
||||||
5. [Volatility Considerations](engine/optimize/volatility.md)
|
5. [Volatility Considerations](engine/optimize/volatility.md)
|
||||||
6. [Subtle Semantic Changes](engine/optimize/semantics.md)
|
6. [Subtle Semantic Changes](engine/optimize/semantics.md)
|
||||||
3. [Eval Statement](language/eval.md)
|
4. [Eval Statement](language/eval.md)
|
||||||
9. [Appendix](appendix/index.md)
|
9. [Appendix](appendix/index.md)
|
||||||
1. [Keywords](appendix/keywords.md)
|
1. [Keywords](appendix/keywords.md)
|
||||||
2. [Operators](appendix/operators.md)
|
2. [Operators](appendix/operators.md)
|
||||||
|
@ -39,6 +39,8 @@ Dynamic
|
|||||||
|
|
||||||
* Some support for [object-oriented programming (OOP)][OOP].
|
* Some support for [object-oriented programming (OOP)][OOP].
|
||||||
|
|
||||||
|
* Serialization/deserialization support via [`serde`].
|
||||||
|
|
||||||
Safe
|
Safe
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
[package]: {{rootUrl}}/rust/packages/index.md
|
[package]: {{rootUrl}}/rust/packages/index.md
|
||||||
[packages]: {{rootUrl}}/rust/packages/index.md
|
[packages]: {{rootUrl}}/rust/packages/index.md
|
||||||
[`Scope`]: {{rootUrl}}/rust/scope.md
|
[`Scope`]: {{rootUrl}}/rust/scope.md
|
||||||
|
[`serde`]: {{rootUrl}}/rust/serde.md
|
||||||
|
|
||||||
[`type_of()`]: {{rootUrl}}/language/type-of.md
|
[`type_of()`]: {{rootUrl}}/language/type-of.md
|
||||||
[`to_string()`]: {{rootUrl}}/language/values-and-types.md
|
[`to_string()`]: {{rootUrl}}/language/values-and-types.md
|
||||||
|
63
doc/src/rust/serde.md
Normal file
63
doc/src/rust/serde.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
Serialization and Deserialization of `Dynamic` with `serde`
|
||||||
|
=========================================================
|
||||||
|
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
|
Rhai's [`Dynamic`] type supports serialization and deserialization by [`serde`](https://crates.io/crates/serde)
|
||||||
|
via the [`serde`][features] feature.
|
||||||
|
|
||||||
|
A [`Dynamic`] can be seamlessly converted to and from a type that implements `serde::Serialize` and/or
|
||||||
|
`serde::Deserialize`.
|
||||||
|
|
||||||
|
|
||||||
|
Serialization
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Serialization by [`serde`](https://crates.io/crates/serde) is not yet implemented.
|
||||||
|
|
||||||
|
It is simple to serialize a Rust type to `JSON` via `serde`, then use [`Engine::parse_json`]({{rootUrl}}/language/json.md) to convert
|
||||||
|
it into an [object map].
|
||||||
|
|
||||||
|
|
||||||
|
Deserialization
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The function `rhai::de::from_dynamic` automatically converts a [`Dynamic`] value into any Rust type
|
||||||
|
that implements `serde::Deserialize`.
|
||||||
|
|
||||||
|
In particular, [object maps] are converted into Rust `struct`'s (or any type that is marked as
|
||||||
|
a `serde` map) while [arrays] are converted into Rust `Vec`'s (or any type that is marked
|
||||||
|
as a `serde` sequence).
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Engine, Dynamic};
|
||||||
|
use rhai::de::from_dynamic;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
struct Point {
|
||||||
|
x: f64,
|
||||||
|
y: f64
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
struct MyStruct {
|
||||||
|
a: i64,
|
||||||
|
b: Vec<String>,
|
||||||
|
c: bool,
|
||||||
|
d: Point
|
||||||
|
}
|
||||||
|
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
let result: Dynamic = engine.eval(r#"
|
||||||
|
#{
|
||||||
|
a: 42,
|
||||||
|
b: [ "hello", "world" ],
|
||||||
|
c: true,
|
||||||
|
d: #{ x: 123.456, y: 999.0 }
|
||||||
|
}
|
||||||
|
"#)?;
|
||||||
|
|
||||||
|
// Convert the 'Dynamic' object map into 'MyStruct'
|
||||||
|
let x: MyStruct = from_dynamic(&result)?;
|
||||||
|
```
|
@ -24,6 +24,7 @@ more control over what a script can (or cannot) do.
|
|||||||
| `no_function` | Disable script-defined [functions]. |
|
| `no_function` | Disable script-defined [functions]. |
|
||||||
| `no_module` | Disable loading external [modules]. |
|
| `no_module` | Disable loading external [modules]. |
|
||||||
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
|
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
|
||||||
|
| `serde` | Enable serialization/deserialization via [`serde`]. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. |
|
||||||
| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |
|
| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ use crate::stdlib::{
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub type Array = Vec<Dynamic>;
|
pub type Array = Vec<Dynamic>;
|
||||||
|
|
||||||
/// Hash map of `Dynamic` values with `String` keys.
|
/// Hash map of `Dynamic` values with `ImmutableString` keys.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_object` feature.
|
/// Not available under the `no_object` feature.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
//! | `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
|
//! | `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
|
||||||
//! | `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
|
//! | `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
|
||||||
//! | `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, `Engine`, `Scope` and [`AST`] are all `Send + Sync`. |
|
//! | `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, `Engine`, `Scope` and [`AST`] are all `Send + Sync`. |
|
||||||
|
//! | `serde` | Enable serialization/deserialization via [`serde`]. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. |
|
||||||
//! | `internals` | Expose internal data structures (beware they may be volatile from version to version). |
|
//! | `internals` | Expose internal data structures (beware they may be volatile from version to version). |
|
||||||
//!
|
//!
|
||||||
//! See [The Rhai Book](https://schungx.github.io/rhai) for details on the Rhai script engine and language.
|
//! See [The Rhai Book](https://schungx.github.io/rhai) for details on the Rhai script engine and language.
|
||||||
@ -86,6 +87,7 @@ pub mod packages;
|
|||||||
mod parser;
|
mod parser;
|
||||||
mod result;
|
mod result;
|
||||||
mod scope;
|
mod scope;
|
||||||
|
mod serde;
|
||||||
mod stdlib;
|
mod stdlib;
|
||||||
mod token;
|
mod token;
|
||||||
mod r#unsafe;
|
mod r#unsafe;
|
||||||
@ -127,6 +129,11 @@ pub mod module_resolvers {
|
|||||||
pub use crate::module::resolvers::*;
|
pub use crate::module::resolvers::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub mod de {
|
||||||
|
pub use crate::serde::de::from_dynamic;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
pub use optimize::OptimizationLevel;
|
pub use optimize::OptimizationLevel;
|
||||||
|
|
||||||
|
375
src/serde/de.rs
Normal file
375
src/serde/de.rs
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
use super::str::ImmutableStringDeserializer;
|
||||||
|
use crate::any::{Dynamic, Union};
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::token::Position;
|
||||||
|
use crate::utils::ImmutableString;
|
||||||
|
|
||||||
|
use serde::de::{DeserializeSeed, Deserializer, Error, MapAccess, SeqAccess, Visitor};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
use crate::engine::Array;
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
use crate::engine::Map;
|
||||||
|
|
||||||
|
use crate::stdlib::{any::type_name, fmt};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use crate::stdlib::time::Instant;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use instant::Instant;
|
||||||
|
|
||||||
|
pub struct DynamicDeserializer<'a> {
|
||||||
|
value: &'a Dynamic,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DynamicDeserializer<'a> {
|
||||||
|
pub fn from_dynamic(value: &'a Dynamic) -> Self {
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
pub fn type_error<R, T>(&self) -> Result<T, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str(type_name::<R>())
|
||||||
|
}
|
||||||
|
pub fn type_error_str<T>(&self, name: &str) -> Result<T, Box<EvalAltResult>> {
|
||||||
|
Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
|
||||||
|
name.into(),
|
||||||
|
self.value.type_name().into(),
|
||||||
|
Position::none(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_dynamic<'de, T: Deserialize<'de>>(
|
||||||
|
value: &'de Dynamic,
|
||||||
|
) -> Result<T, Box<EvalAltResult>> {
|
||||||
|
T::deserialize(&mut DynamicDeserializer::from_dynamic(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for Box<EvalAltResult> {
|
||||||
|
fn custom<T: fmt::Display>(err: T) -> Self {
|
||||||
|
Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
err.to_string(),
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||||
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
|
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
match &self.value.0 {
|
||||||
|
Union::Unit(_) => self.deserialize_unit(visitor),
|
||||||
|
Union::Bool(_) => self.deserialize_bool(visitor),
|
||||||
|
Union::Str(_) => self.deserialize_str(visitor),
|
||||||
|
Union::Char(_) => self.deserialize_char(visitor),
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
Union::Int(_) => self.deserialize_i64(visitor),
|
||||||
|
#[cfg(feature = "only_i32")]
|
||||||
|
Union::Int(_) => self.deserialize_i32(visitor),
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
Union::Float(_) => self.deserialize_f64(visitor),
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Union::Array(_) => self.deserialize_seq(visitor),
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Union::Map(_) => self.deserialize_map(visitor),
|
||||||
|
Union::FnPtr(_) => unimplemented!(),
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
Union::Variant(value) if value.is::<Instant>() => unimplemented!(),
|
||||||
|
|
||||||
|
Union::Variant(value) if value.is::<i8>() => self.deserialize_i8(visitor),
|
||||||
|
Union::Variant(value) if value.is::<i16>() => self.deserialize_i16(visitor),
|
||||||
|
Union::Variant(value) if value.is::<i32>() => self.deserialize_i32(visitor),
|
||||||
|
Union::Variant(value) if value.is::<i64>() => self.deserialize_i64(visitor),
|
||||||
|
Union::Variant(value) if value.is::<u8>() => self.deserialize_u8(visitor),
|
||||||
|
Union::Variant(value) if value.is::<u16>() => self.deserialize_u16(visitor),
|
||||||
|
Union::Variant(value) if value.is::<u32>() => self.deserialize_u32(visitor),
|
||||||
|
Union::Variant(value) if value.is::<u64>() => self.deserialize_u64(visitor),
|
||||||
|
|
||||||
|
Union::Variant(_) => self.type_error_str("any"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
visitor.visit_bool(
|
||||||
|
self.value
|
||||||
|
.as_bool()
|
||||||
|
.or_else(|_| self.type_error::<bool, _>())?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<i8>()
|
||||||
|
.map_or_else(|| self.type_error::<i8, _>(), |&x| visitor.visit_i8(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<i16>()
|
||||||
|
.map_or_else(|| self.type_error::<i16, _>(), |&x| visitor.visit_i16(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<i32>()
|
||||||
|
.map_or_else(|| self.type_error::<i32, _>(), |&x| visitor.visit_i32(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<i64>()
|
||||||
|
.map_or_else(|| self.type_error::<i64, _>(), |&x| visitor.visit_i64(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<u8>()
|
||||||
|
.map_or_else(|| self.type_error::<u8, _>(), |&x| visitor.visit_u8(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<u16>()
|
||||||
|
.map_or_else(|| self.type_error::<u16, _>(), |&x| visitor.visit_u16(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<u32>()
|
||||||
|
.map_or_else(|| self.type_error::<u32, _>(), |&x| visitor.visit_u32(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<u64>()
|
||||||
|
.map_or_else(|| self.type_error::<u64, _>(), |&x| visitor.visit_u64(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<f32>()
|
||||||
|
.map_or_else(|| self.type_error::<f32, _>(), |&x| visitor.visit_f32(x))
|
||||||
|
}
|
||||||
|
#[cfg(feature = "no_float")]
|
||||||
|
self.type_error_str("f32")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_f64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<f64>()
|
||||||
|
.map_or_else(|| self.type_error::<f64, _>(), |&x| visitor.visit_f64(x))
|
||||||
|
}
|
||||||
|
#[cfg(feature = "no_float")]
|
||||||
|
self.type_error_str("f64")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<char>()
|
||||||
|
.map_or_else(|| self.type_error::<char, _>(), |&x| visitor.visit_char(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.value.downcast_ref::<ImmutableString>().map_or_else(
|
||||||
|
|| self.type_error::<ImmutableString, _>(),
|
||||||
|
|x| visitor.visit_borrowed_str(x.as_str()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_string<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_str(visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_bytes<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("bytes array")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_byte_buf<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("bytes array")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_option<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("bytes array")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.value
|
||||||
|
.downcast_ref::<()>()
|
||||||
|
.map_or_else(|| self.type_error::<(), _>(), |_| visitor.visit_unit())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_unit_struct<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_unit(visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_newtype_struct<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
visitor.visit_newtype_struct(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_seq<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
{
|
||||||
|
self.value.downcast_ref::<Array>().map_or_else(
|
||||||
|
|| self.type_error::<Array, _>(),
|
||||||
|
|arr| visitor.visit_seq(IterateArray::new(arr.iter())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
self.type_error_str("array")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_tuple<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_len: usize,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_seq(visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_tuple_struct<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
_len: usize,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_seq(visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
{
|
||||||
|
self.value.downcast_ref::<Map>().map_or_else(
|
||||||
|
|| self.type_error::<Map, _>(),
|
||||||
|
|map| visitor.visit_map(IterateMap::new(map.keys(), map.values())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[cfg(feature = "no_object")]
|
||||||
|
self.type_error_str("map")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_struct<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
_fields: &'static [&'static str],
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_map(visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_enum<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
_variants: &'static [&'static str],
|
||||||
|
_: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("num")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_identifier<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_str(visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_ignored_any<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_any(visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IterateArray<'a, ITER: Iterator<Item = &'a Dynamic>> {
|
||||||
|
iter: ITER,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, ITER: Iterator<Item = &'a Dynamic>> IterateArray<'a, ITER> {
|
||||||
|
pub fn new(iter: ITER) -> Self {
|
||||||
|
Self { iter }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a: 'de, 'de, ITER: Iterator<Item = &'a Dynamic>> SeqAccess<'de> for IterateArray<'a, ITER> {
|
||||||
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
|
fn next_element_seed<T: DeserializeSeed<'de>>(
|
||||||
|
&mut self,
|
||||||
|
seed: T,
|
||||||
|
) -> Result<Option<T::Value>, Box<EvalAltResult>> {
|
||||||
|
match self.iter.next() {
|
||||||
|
None => Ok(None),
|
||||||
|
Some(item) => seed
|
||||||
|
.deserialize(&mut DynamicDeserializer::from_dynamic(item))
|
||||||
|
.map(Some),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IterateMap<
|
||||||
|
'a,
|
||||||
|
KEYS: Iterator<Item = &'a ImmutableString>,
|
||||||
|
VALUES: Iterator<Item = &'a Dynamic>,
|
||||||
|
> {
|
||||||
|
keys: KEYS,
|
||||||
|
values: VALUES,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, KEYS: Iterator<Item = &'a ImmutableString>, VALUES: Iterator<Item = &'a Dynamic>>
|
||||||
|
IterateMap<'a, KEYS, VALUES>
|
||||||
|
{
|
||||||
|
pub fn new(keys: KEYS, values: VALUES) -> Self {
|
||||||
|
Self { keys, values }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
'a: 'de,
|
||||||
|
'de,
|
||||||
|
KEYS: Iterator<Item = &'a ImmutableString>,
|
||||||
|
VALUES: Iterator<Item = &'a Dynamic>,
|
||||||
|
> MapAccess<'de> for IterateMap<'a, KEYS, VALUES>
|
||||||
|
{
|
||||||
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
|
fn next_key_seed<K: DeserializeSeed<'de>>(
|
||||||
|
&mut self,
|
||||||
|
seed: K,
|
||||||
|
) -> Result<Option<K::Value>, Box<EvalAltResult>> {
|
||||||
|
match self.keys.next() {
|
||||||
|
None => Ok(None),
|
||||||
|
Some(item) => seed
|
||||||
|
.deserialize(&mut ImmutableStringDeserializer::from_str(item))
|
||||||
|
.map(Some),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_value_seed<V: DeserializeSeed<'de>>(
|
||||||
|
&mut self,
|
||||||
|
seed: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
seed.deserialize(&mut DynamicDeserializer::from_dynamic(
|
||||||
|
self.values.next().unwrap(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
2
src/serde/mod.rs
Normal file
2
src/serde/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod de;
|
||||||
|
mod str;
|
152
src/serde/str.rs
Normal file
152
src/serde/str.rs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::token::Position;
|
||||||
|
use crate::utils::ImmutableString;
|
||||||
|
|
||||||
|
use serde::de::{Deserializer, Visitor};
|
||||||
|
|
||||||
|
use crate::stdlib::any::type_name;
|
||||||
|
|
||||||
|
pub struct ImmutableStringDeserializer<'a> {
|
||||||
|
value: &'a ImmutableString,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ImmutableStringDeserializer<'a> {
|
||||||
|
pub fn from_str(value: &'a ImmutableString) -> Self {
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
pub fn type_error<R, T>(&self) -> Result<T, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str(type_name::<R>())
|
||||||
|
}
|
||||||
|
pub fn type_error_str<T>(&self, name: &str) -> Result<T, Box<EvalAltResult>> {
|
||||||
|
Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
|
||||||
|
name.into(),
|
||||||
|
"string".into(),
|
||||||
|
Position::none(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserializer<'de> for &mut ImmutableStringDeserializer<'de> {
|
||||||
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
|
fn deserialize_any<V: Visitor<'de>>(self, v: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_str(v)
|
||||||
|
}
|
||||||
|
fn deserialize_bool<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error::<bool, _>()
|
||||||
|
}
|
||||||
|
fn deserialize_i8<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error::<i8, _>()
|
||||||
|
}
|
||||||
|
fn deserialize_i16<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error::<i16, _>()
|
||||||
|
}
|
||||||
|
fn deserialize_i32<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error::<i32, _>()
|
||||||
|
}
|
||||||
|
fn deserialize_i64<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error::<i64, _>()
|
||||||
|
}
|
||||||
|
fn deserialize_u8<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error::<u8, _>()
|
||||||
|
}
|
||||||
|
fn deserialize_u16<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error::<u16, _>()
|
||||||
|
}
|
||||||
|
fn deserialize_u32<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error::<u32, _>()
|
||||||
|
}
|
||||||
|
fn deserialize_u64<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error::<u64, _>()
|
||||||
|
}
|
||||||
|
fn deserialize_f32<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("f32")
|
||||||
|
}
|
||||||
|
fn deserialize_f64<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("f64")
|
||||||
|
}
|
||||||
|
fn deserialize_char<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error::<char, _>()
|
||||||
|
}
|
||||||
|
fn deserialize_str<V: Visitor<'de>>(self, v: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
v.visit_borrowed_str(self.value.as_str())
|
||||||
|
}
|
||||||
|
fn deserialize_string<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_str(visitor)
|
||||||
|
}
|
||||||
|
fn deserialize_bytes<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("bytes array")
|
||||||
|
}
|
||||||
|
fn deserialize_byte_buf<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("bytes array")
|
||||||
|
}
|
||||||
|
fn deserialize_option<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("option")
|
||||||
|
}
|
||||||
|
fn deserialize_unit<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error::<(), _>()
|
||||||
|
}
|
||||||
|
fn deserialize_unit_struct<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
v: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_unit(v)
|
||||||
|
}
|
||||||
|
fn deserialize_newtype_struct<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
v: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
v.visit_newtype_struct(self)
|
||||||
|
}
|
||||||
|
fn deserialize_seq<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("array")
|
||||||
|
}
|
||||||
|
fn deserialize_tuple<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_len: usize,
|
||||||
|
v: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_seq(v)
|
||||||
|
}
|
||||||
|
fn deserialize_tuple_struct<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
_len: usize,
|
||||||
|
v: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_seq(v)
|
||||||
|
}
|
||||||
|
fn deserialize_map<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("map")
|
||||||
|
}
|
||||||
|
fn deserialize_struct<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
_fields: &'static [&'static str],
|
||||||
|
v: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_map(v)
|
||||||
|
}
|
||||||
|
fn deserialize_enum<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
_variants: &'static [&'static str],
|
||||||
|
_: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.type_error_str("enum")
|
||||||
|
}
|
||||||
|
fn deserialize_identifier<V: Visitor<'de>>(self, v: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_str(v)
|
||||||
|
}
|
||||||
|
fn deserialize_ignored_any<V: Visitor<'de>>(
|
||||||
|
self,
|
||||||
|
v: V,
|
||||||
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
|
self.deserialize_any(v)
|
||||||
|
}
|
||||||
|
}
|
108
tests/serde.rs
Normal file
108
tests/serde.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#![cfg(feature = "serde")]
|
||||||
|
|
||||||
|
use rhai::{de::from_dynamic, Dynamic, Engine, EvalAltResult, INT};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
use rhai::Array;
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
use rhai::Map;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serde_de_primary_types() {
|
||||||
|
assert_eq!(42_u16, from_dynamic(&Dynamic::from(42_u16)).unwrap());
|
||||||
|
assert_eq!(42 as INT, from_dynamic(&(42 as INT).into()).unwrap());
|
||||||
|
assert_eq!(true, from_dynamic(&true.into()).unwrap());
|
||||||
|
assert_eq!((), from_dynamic(&().into()).unwrap());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
assert_eq!(123.456_f64, from_dynamic(&123.456_f64.into()).unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
123.456_f32,
|
||||||
|
from_dynamic(&Dynamic::from(123.456_f32)).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"hello",
|
||||||
|
from_dynamic::<String>(&"hello".to_string().into()).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
fn test_serde_de_array() {
|
||||||
|
let arr: Vec<INT> = vec![123, 456, 42, 999];
|
||||||
|
assert_eq!(arr, from_dynamic::<Vec<INT>>(&arr.clone().into()).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serde_de_struct() {
|
||||||
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
|
struct Hello {
|
||||||
|
a: INT,
|
||||||
|
b: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
|
struct Test {
|
||||||
|
int: u32,
|
||||||
|
seq: Vec<String>,
|
||||||
|
obj: Hello,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut map = Map::new();
|
||||||
|
map.insert("int".into(), Dynamic::from(42_u32));
|
||||||
|
|
||||||
|
let mut map2 = Map::new();
|
||||||
|
map2.insert("a".into(), (123 as INT).into());
|
||||||
|
map2.insert("b".into(), true.into());
|
||||||
|
|
||||||
|
map.insert("obj".into(), map2.into());
|
||||||
|
|
||||||
|
let arr: Array = vec!["hello".into(), "kitty".into(), "world".into()];
|
||||||
|
map.insert("seq".into(), arr.into());
|
||||||
|
|
||||||
|
let expected = Test {
|
||||||
|
int: 42,
|
||||||
|
seq: vec!["hello".into(), "kitty".into(), "world".into()],
|
||||||
|
obj: Hello { a: 123, b: true },
|
||||||
|
};
|
||||||
|
assert_eq!(expected, from_dynamic(&map.into()).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serde_de_script() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Point {
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct MyStruct {
|
||||||
|
a: i64,
|
||||||
|
b: Vec<String>,
|
||||||
|
c: bool,
|
||||||
|
d: Point,
|
||||||
|
}
|
||||||
|
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
let result: Dynamic = engine.eval(
|
||||||
|
r#"
|
||||||
|
#{
|
||||||
|
a: 42,
|
||||||
|
b: [ "hello", "world" ],
|
||||||
|
c: true,
|
||||||
|
d: #{ x: 123.456, y: 999.0 }
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Convert the 'Dynamic' object map into 'MyStruct'
|
||||||
|
let x: MyStruct = from_dynamic(&result)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user