Add Serialize/Deserialize for Dynamic and ImmutableString.
This commit is contained in:
parent
6b5a14ee88
commit
ec272cf9b9
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
|||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
flags:
|
flags:
|
||||||
- ""
|
- ""
|
||||||
- "--features serde,internals"
|
- "--features metadata,internals"
|
||||||
- "--features unchecked"
|
- "--features unchecked"
|
||||||
- "--features sync"
|
- "--features sync"
|
||||||
- "--features no_optimize"
|
- "--features no_optimize"
|
||||||
@ -32,7 +32,7 @@ jobs:
|
|||||||
- "--features no_module"
|
- "--features no_module"
|
||||||
- "--features no_closure"
|
- "--features no_closure"
|
||||||
- "--features unicode-xid-ident"
|
- "--features unicode-xid-ident"
|
||||||
- "--features sync,no_function,no_float,no_optimize,no_module,no_closure,serde,unchecked"
|
- "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,unchecked"
|
||||||
toolchain: [stable]
|
toolchain: [stable]
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
include:
|
include:
|
||||||
|
@ -97,4 +97,4 @@ optional = true
|
|||||||
instant= { version = "0.1", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant
|
instant= { version = "0.1", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = [ "serde", "internals" ]
|
features = [ "metadata", "internals" ]
|
||||||
|
@ -22,7 +22,7 @@ Supported targets and builds
|
|||||||
* All common CPU targets for Windows, Linux and MacOS.
|
* All common CPU targets for Windows, Linux and MacOS.
|
||||||
* WebAssembly (WASM)
|
* WebAssembly (WASM)
|
||||||
* `no-std`
|
* `no-std`
|
||||||
* Minimum Rust version 1.45
|
* Minimum Rust version 1.49
|
||||||
|
|
||||||
|
|
||||||
Standard features
|
Standard features
|
||||||
|
14
RELEASES.md
14
RELEASES.md
@ -4,6 +4,20 @@ Rhai Release Notes
|
|||||||
Version 0.19.11
|
Version 0.19.11
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
Breaking changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Rust compiler requirement raised to 1.49.
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Fixes compilation errors in `metadata` feature build.
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* `ahash` is used to hash function call parameters. This should yield speed improvements.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.10
|
Version 0.19.10
|
||||||
|
@ -4,10 +4,8 @@ use super::str::ImmutableStringDeserializer;
|
|||||||
use crate::dynamic::Union;
|
use crate::dynamic::Union;
|
||||||
use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString};
|
use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString};
|
||||||
use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, Position};
|
use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, Position};
|
||||||
use serde::de::{
|
use serde::de::{DeserializeSeed, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor};
|
||||||
DeserializeSeed, Deserializer, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor,
|
use serde::{Deserialize, Deserializer};
|
||||||
};
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::Array;
|
use crate::Array;
|
||||||
@ -50,13 +48,9 @@ impl<'de> DynamicDeserializer<'de> {
|
|||||||
visitor: V,
|
visitor: V,
|
||||||
) -> Result<V::Value, Box<EvalAltResult>> {
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
{
|
return visitor.visit_i64(v);
|
||||||
visitor.visit_i64(v)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "only_i32")]
|
#[cfg(feature = "only_i32")]
|
||||||
{
|
return visitor.visit_i32(v);
|
||||||
visitor.visit_i32(v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
144
src/serde_impl/deserialize.rs
Normal file
144
src/serde_impl/deserialize.rs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
//! Implementations of [`serde::Deserialize`].
|
||||||
|
|
||||||
|
use crate::stdlib::{fmt, string::ToString};
|
||||||
|
use crate::{Dynamic, ImmutableString, INT};
|
||||||
|
use serde::de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
use crate::Array;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
use crate::Map;
|
||||||
|
|
||||||
|
struct DynamicVisitor;
|
||||||
|
|
||||||
|
impl<'d> Visitor<'d> for DynamicVisitor {
|
||||||
|
type Value = Dynamic;
|
||||||
|
|
||||||
|
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.write_str("any type that can be converted into a Dynamic")
|
||||||
|
}
|
||||||
|
fn visit_bool<E: Error>(self, v: bool) -> Result<Self::Value, E> {
|
||||||
|
Ok(v.into())
|
||||||
|
}
|
||||||
|
fn visit_i8<E: Error>(self, v: i8) -> Result<Self::Value, E> {
|
||||||
|
Ok(INT::from(v).into())
|
||||||
|
}
|
||||||
|
fn visit_i16<E: Error>(self, v: i16) -> Result<Self::Value, E> {
|
||||||
|
Ok(INT::from(v).into())
|
||||||
|
}
|
||||||
|
fn visit_i32<E: Error>(self, v: i32) -> Result<Self::Value, E> {
|
||||||
|
Ok(INT::from(v).into())
|
||||||
|
}
|
||||||
|
fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
return Ok(v.into());
|
||||||
|
#[cfg(feature = "only_i32")]
|
||||||
|
if v > i32::MAX as i64 {
|
||||||
|
return Ok(Dynamic::from(v));
|
||||||
|
} else {
|
||||||
|
return self.visit_i32(v as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn visit_u8<E: Error>(self, v: u8) -> Result<Self::Value, E> {
|
||||||
|
Ok(INT::from(v).into())
|
||||||
|
}
|
||||||
|
fn visit_u16<E: Error>(self, v: u16) -> Result<Self::Value, E> {
|
||||||
|
Ok(INT::from(v).into())
|
||||||
|
}
|
||||||
|
fn visit_u32<E: Error>(self, v: u32) -> Result<Self::Value, E> {
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
return Ok(INT::from(v).into());
|
||||||
|
#[cfg(feature = "only_i32")]
|
||||||
|
if v > i32::MAX as u32 {
|
||||||
|
return Ok(Dynamic::from(v));
|
||||||
|
} else {
|
||||||
|
return self.visit_i32(v as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
if v > i64::MAX as u64 {
|
||||||
|
return Ok(Dynamic::from(v));
|
||||||
|
} else {
|
||||||
|
return self.visit_i64(v as i64);
|
||||||
|
}
|
||||||
|
#[cfg(feature = "only_i32")]
|
||||||
|
if v > i32::MAX as u64 {
|
||||||
|
return Ok(Dynamic::from(v));
|
||||||
|
} else {
|
||||||
|
return self.visit_i32(v as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> {
|
||||||
|
#[cfg(not(feature = "f32_float"))]
|
||||||
|
return self.visit_f64(v as f64);
|
||||||
|
#[cfg(feature = "f32_float")]
|
||||||
|
return Ok(v.into());
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
|
||||||
|
#[cfg(not(feature = "f32_float"))]
|
||||||
|
return Ok(v.into());
|
||||||
|
#[cfg(feature = "f32_float")]
|
||||||
|
return self.visit_f32(v as f32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_char<E: Error>(self, v: char) -> Result<Self::Value, E> {
|
||||||
|
self.visit_string(v.to_string())
|
||||||
|
}
|
||||||
|
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
||||||
|
Ok(v.into())
|
||||||
|
}
|
||||||
|
fn visit_borrowed_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
||||||
|
self.visit_str(v)
|
||||||
|
}
|
||||||
|
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
|
||||||
|
Ok(v.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
|
||||||
|
Ok(Dynamic::UNIT)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_newtype_struct<D: Deserializer<'d>>(self, de: D) -> Result<Self::Value, D::Error> {
|
||||||
|
Deserialize::deserialize(de)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
fn visit_seq<A: SeqAccess<'d>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
|
||||||
|
let mut arr: Array = Default::default();
|
||||||
|
|
||||||
|
while let Some(v) = seq.next_element()? {
|
||||||
|
arr.push(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(arr.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
fn visit_map<M: MapAccess<'d>>(self, mut map: M) -> Result<Self::Value, M::Error> {
|
||||||
|
let mut m: Map = Default::default();
|
||||||
|
|
||||||
|
while let Some((k, v)) = map.next_entry()? {
|
||||||
|
m.insert(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(m.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> Deserialize<'d> for Dynamic {
|
||||||
|
fn deserialize<D: Deserializer<'d>>(de: D) -> Result<Self, D::Error> {
|
||||||
|
de.deserialize_any(DynamicVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> Deserialize<'d> for ImmutableString {
|
||||||
|
fn deserialize<D: Deserializer<'d>>(de: D) -> Result<Self, D::Error> {
|
||||||
|
let s: String = Deserialize::deserialize(de)?;
|
||||||
|
Ok(s.into())
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
//! Helper module defining serialization/deserialization support for [`serde`].
|
//! Helper module defining serialization/deserialization support for [`serde`].
|
||||||
|
|
||||||
pub mod de;
|
pub mod de;
|
||||||
|
mod deserialize;
|
||||||
pub mod ser;
|
pub mod ser;
|
||||||
|
mod serialize;
|
||||||
mod str;
|
mod str;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
|
@ -4,9 +4,8 @@ use crate::stdlib::{boxed::Box, fmt, string::ToString};
|
|||||||
use crate::{Dynamic, EvalAltResult, Position};
|
use crate::{Dynamic, EvalAltResult, Position};
|
||||||
use serde::ser::{
|
use serde::ser::{
|
||||||
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
|
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
|
||||||
Serializer,
|
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::{Serialize, Serializer};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::Array;
|
use crate::Array;
|
||||||
|
55
src/serde_impl/serialize.rs
Normal file
55
src/serde_impl/serialize.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
//! Implementations of [`serde::Serialize`].
|
||||||
|
|
||||||
|
use crate::dynamic::Union;
|
||||||
|
use crate::stdlib::string::ToString;
|
||||||
|
use crate::{Dynamic, ImmutableString};
|
||||||
|
use serde::ser::{Serialize, SerializeMap, Serializer};
|
||||||
|
|
||||||
|
impl Serialize for Dynamic {
|
||||||
|
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
|
||||||
|
match &self.0 {
|
||||||
|
Union::Unit(_, _) => ser.serialize_unit(),
|
||||||
|
Union::Bool(x, _) => ser.serialize_bool(*x),
|
||||||
|
Union::Str(s, _) => ser.serialize_str(s.as_str()),
|
||||||
|
Union::Char(c, _) => ser.serialize_str(&c.to_string()),
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
Union::Int(x, _) => ser.serialize_i64(*x),
|
||||||
|
#[cfg(feature = "only_i32")]
|
||||||
|
Union::Int(x, _) => ser.serialize_i32(*x),
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(not(feature = "f32_float"))]
|
||||||
|
Union::Float(x, _) => ser.serialize_f64(**x),
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(feature = "f32_float")]
|
||||||
|
Union::Float(x, _) => ser.serialize_f32(*x),
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Union::Array(a, _) => (**a).serialize(ser),
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Union::Map(m, _) => {
|
||||||
|
let mut map = ser.serialize_map(Some(m.len()))?;
|
||||||
|
for (k, v) in m.iter() {
|
||||||
|
map.serialize_entry(k, v)?;
|
||||||
|
}
|
||||||
|
map.end()
|
||||||
|
}
|
||||||
|
Union::FnPtr(f, _) => ser.serialize_str(f.fn_name()),
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
Union::TimeStamp(_, _) => unimplemented!("serialization of timestamp is not supported"),
|
||||||
|
|
||||||
|
Union::Variant(v, _) => ser.serialize_str((***v).type_name()),
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
Union::Shared(cell, _) => cell.borrow().serialize(ser),
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
Union::Shared(cell, _) => cell.read().unwrap().serialize(ser),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for ImmutableString {
|
||||||
|
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
|
||||||
|
ser.serialize_str(self.as_str())
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use rhai::{
|
use rhai::{
|
||||||
serde::{from_dynamic, to_dynamic},
|
serde::{from_dynamic, to_dynamic},
|
||||||
Dynamic, Engine, EvalAltResult, INT,
|
Dynamic, Engine, EvalAltResult, ImmutableString, INT,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -290,15 +290,15 @@ fn test_serde_ser_untagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serde_de_primary_types() -> Result<(), Box<EvalAltResult>> {
|
fn test_serde_de_primary_types() -> Result<(), Box<EvalAltResult>> {
|
||||||
assert_eq!(42_u16, from_dynamic(&Dynamic::from(42_u16))?);
|
assert_eq!(42, from_dynamic::<u16>(&Dynamic::from(42_u16))?);
|
||||||
assert_eq!(42 as INT, from_dynamic(&(42 as INT).into())?);
|
assert_eq!(42, from_dynamic::<INT>(&(42 as INT).into())?);
|
||||||
assert_eq!(true, from_dynamic(&true.into())?);
|
assert_eq!(true, from_dynamic::<bool>(&true.into())?);
|
||||||
assert_eq!((), from_dynamic(&().into())?);
|
assert_eq!((), from_dynamic::<()>(&().into())?);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
assert_eq!(123.456_f64, from_dynamic(&123.456_f64.into())?);
|
assert_eq!(123.456, from_dynamic::<f64>(&123.456_f64.into())?);
|
||||||
assert_eq!(123.456_f32, from_dynamic(&Dynamic::from(123.456_f32))?);
|
assert_eq!(123.456, from_dynamic::<f32>(&Dynamic::from(123.456_f32))?);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -311,14 +311,14 @@ fn test_serde_de_primary_types() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serde_de_integer_types() -> Result<(), Box<EvalAltResult>> {
|
fn test_serde_de_integer_types() -> Result<(), Box<EvalAltResult>> {
|
||||||
assert_eq!(42_i8, from_dynamic(&Dynamic::from(42 as INT))?);
|
assert_eq!(42, from_dynamic::<i8>(&Dynamic::from(42 as INT))?);
|
||||||
assert_eq!(42_i16, from_dynamic(&Dynamic::from(42 as INT))?);
|
assert_eq!(42, from_dynamic::<i16>(&Dynamic::from(42 as INT))?);
|
||||||
assert_eq!(42_i32, from_dynamic(&Dynamic::from(42 as INT))?);
|
assert_eq!(42, from_dynamic::<i32>(&Dynamic::from(42 as INT))?);
|
||||||
assert_eq!(42_i64, from_dynamic(&Dynamic::from(42 as INT))?);
|
assert_eq!(42, from_dynamic::<i64>(&Dynamic::from(42 as INT))?);
|
||||||
assert_eq!(42_u8, from_dynamic(&Dynamic::from(42 as INT))?);
|
assert_eq!(42, from_dynamic::<u8>(&Dynamic::from(42 as INT))?);
|
||||||
assert_eq!(42_u16, from_dynamic(&Dynamic::from(42 as INT))?);
|
assert_eq!(42, from_dynamic::<u16>(&Dynamic::from(42 as INT))?);
|
||||||
assert_eq!(42_u32, from_dynamic(&Dynamic::from(42 as INT))?);
|
assert_eq!(42, from_dynamic::<u32>(&Dynamic::from(42 as INT))?);
|
||||||
assert_eq!(42_u64, from_dynamic(&Dynamic::from(42 as INT))?);
|
assert_eq!(42, from_dynamic::<u64>(&Dynamic::from(42 as INT))?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -635,3 +635,42 @@ fn test_serde_de_untagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
fn test_serde_json() -> serde_json::Result<()> {
|
||||||
|
let s: ImmutableString = "hello".into();
|
||||||
|
assert_eq!(serde_json::to_string(&s)?, r#""hello""#);
|
||||||
|
|
||||||
|
let mut map = Map::new();
|
||||||
|
map.insert("a".into(), (123 as INT).into());
|
||||||
|
|
||||||
|
let arr: Array = vec![(1 as INT).into(), (2 as INT).into(), (3 as INT).into()];
|
||||||
|
map.insert("b".into(), arr.into());
|
||||||
|
map.insert("c".into(), true.into());
|
||||||
|
let d: Dynamic = map.into();
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&d)?;
|
||||||
|
|
||||||
|
assert!(json.contains("\"a\":123"));
|
||||||
|
assert!(json.contains("\"b\":[1,2,3]"));
|
||||||
|
assert!(json.contains("\"c\":true"));
|
||||||
|
|
||||||
|
let d2: Dynamic = serde_json::from_str(&json)?;
|
||||||
|
|
||||||
|
assert!(d2.is::<Map>());
|
||||||
|
|
||||||
|
let mut m = d2.cast::<Map>();
|
||||||
|
|
||||||
|
assert_eq!(m["a"].as_int().unwrap(), 123);
|
||||||
|
assert!(m["c"].as_bool().unwrap());
|
||||||
|
|
||||||
|
let a = m.remove("b").unwrap().cast::<Array>();
|
||||||
|
|
||||||
|
assert_eq!(a.len(), 3);
|
||||||
|
assert_eq!(format!("{:?}", a), "[1, 2, 3]");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user