Make Scope serializable.

This commit is contained in:
Stephen Chung 2022-09-26 18:14:45 +08:00
parent 335d12e182
commit 8d1310c0f3
6 changed files with 208 additions and 62 deletions

View File

@ -5,8 +5,12 @@ Version 1.11.0
============== ==============
Enhancements Enhancements
------------
* The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term `string`). This facilitates more accurate parsing by separating strings and identifiers. * The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term `string`). This facilitates more accurate parsing by separating strings and identifiers.
* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
* `Scope` is now serializable and deserializable via `serde`.
* `Scope` now contains a const generic parameter that allows specifying how many entries to be kept inline.
Version 1.10.1 Version 1.10.1

View File

@ -9,9 +9,7 @@ use std::prelude::v1::*;
use std::{any::type_name, fmt}; use std::{any::type_name, fmt};
/// Deserializer for [`Dynamic`][crate::Dynamic]. /// Deserializer for [`Dynamic`][crate::Dynamic].
pub struct DynamicDeserializer<'de> { pub struct DynamicDeserializer<'de>(&'de Dynamic);
value: &'de Dynamic,
}
impl<'de> IntoDeserializer<'de, RhaiError> for &'de Dynamic { impl<'de> IntoDeserializer<'de, RhaiError> for &'de Dynamic {
type Deserializer = DynamicDeserializer<'de>; type Deserializer = DynamicDeserializer<'de>;
@ -19,7 +17,7 @@ impl<'de> IntoDeserializer<'de, RhaiError> for &'de Dynamic {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
fn into_deserializer(self) -> Self::Deserializer { fn into_deserializer(self) -> Self::Deserializer {
DynamicDeserializer { value: self } DynamicDeserializer(self)
} }
} }
@ -31,7 +29,7 @@ impl<'de> DynamicDeserializer<'de> {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn new(value: &'de Dynamic) -> Self { pub const fn new(value: &'de Dynamic) -> Self {
Self { value } Self(value)
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
#[cold] #[cold]
@ -43,12 +41,10 @@ impl<'de> DynamicDeserializer<'de> {
#[cold] #[cold]
#[inline(never)] #[inline(never)]
fn type_error_str<T>(&self, error: &str) -> RhaiResultOf<T> { fn type_error_str<T>(&self, error: &str) -> RhaiResultOf<T> {
Err(ERR::ErrorMismatchOutputType( Err(
error.into(), ERR::ErrorMismatchOutputType(error.into(), self.0.type_name().into(), Position::NONE)
self.value.type_name().into(), .into(),
Position::NONE,
) )
.into())
} }
#[inline(always)] #[inline(always)]
fn deserialize_int<V: Visitor<'de>>(self, v: crate::INT, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_int<V: Visitor<'de>>(self, v: crate::INT, visitor: V) -> RhaiResultOf<V::Value> {
@ -128,7 +124,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
type Error = RhaiError; type Error = RhaiError;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
match self.value.0 { match self.0 .0 {
Union::Unit(..) => self.deserialize_unit(visitor), Union::Unit(..) => self.deserialize_unit(visitor),
Union::Bool(..) => self.deserialize_bool(visitor), Union::Bool(..) => self.deserialize_bool(visitor),
Union::Str(..) => self.deserialize_str(visitor), Union::Str(..) => self.deserialize_str(visitor),
@ -182,110 +178,110 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
} }
fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
visitor.visit_bool(self.value.as_bool().or_else(|_| self.type_error())?) visitor.visit_bool(self.0.as_bool().or_else(|_| self.type_error())?)
} }
fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<i8>() .downcast_ref::<i8>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_i8(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_i8(x))
} }
} }
fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<i16>() .downcast_ref::<i16>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_i16(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_i16(x))
} }
} }
fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else if cfg!(feature = "only_i32") { } else if cfg!(feature = "only_i32") {
self.type_error() self.type_error()
} else { } else {
self.value self.0
.downcast_ref::<i32>() .downcast_ref::<i32>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_i32(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_i32(x))
} }
} }
fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else if cfg!(not(feature = "only_i32")) { } else if cfg!(not(feature = "only_i32")) {
self.type_error() self.type_error()
} else { } else {
self.value self.0
.downcast_ref::<i64>() .downcast_ref::<i64>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_i64(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_i64(x))
} }
} }
fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else if cfg!(not(feature = "only_i32")) { } else if cfg!(not(feature = "only_i32")) {
self.type_error() self.type_error()
} else { } else {
self.value self.0
.downcast_ref::<i128>() .downcast_ref::<i128>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_i128(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_i128(x))
} }
} }
fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<u8>() .downcast_ref::<u8>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_u8(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_u8(x))
} }
} }
fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<u16>() .downcast_ref::<u16>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_u16(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_u16(x))
} }
} }
fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<u32>() .downcast_ref::<u32>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_u32(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_u32(x))
} }
} }
fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<u64>() .downcast_ref::<u64>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_u64(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_u64(x))
} }
} }
fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<u128>() .downcast_ref::<u128>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_u128(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_u128(x))
} }
@ -294,7 +290,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_f32<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_f32<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
return self return self
.value .0
.downcast_ref::<f32>() .downcast_ref::<f32>()
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x)); .map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x));
@ -304,7 +300,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
use rust_decimal::prelude::ToPrimitive; use rust_decimal::prelude::ToPrimitive;
return self return self
.value .0
.downcast_ref::<rust_decimal::Decimal>() .downcast_ref::<rust_decimal::Decimal>()
.and_then(|&x| x.to_f32()) .and_then(|&x| x.to_f32())
.map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v)); .map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v));
@ -318,7 +314,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
return self return self
.value .0
.downcast_ref::<f64>() .downcast_ref::<f64>()
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x)); .map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x));
@ -328,7 +324,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
use rust_decimal::prelude::ToPrimitive; use rust_decimal::prelude::ToPrimitive;
return self return self
.value .0
.downcast_ref::<rust_decimal::Decimal>() .downcast_ref::<rust_decimal::Decimal>()
.and_then(|&x| x.to_f64()) .and_then(|&x| x.to_f64())
.map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v)); .map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v));
@ -340,13 +336,13 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
} }
fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.value self.0
.downcast_ref::<char>() .downcast_ref::<char>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_char(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_char(x))
} }
fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.value.downcast_ref::<ImmutableString>().map_or_else( self.0.downcast_ref::<ImmutableString>().map_or_else(
|| self.type_error(), || self.type_error(),
|x| visitor.visit_borrowed_str(x.as_str()), |x| visitor.visit_borrowed_str(x.as_str()),
) )
@ -359,7 +355,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_bytes<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_bytes<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return self return self
.value .0
.downcast_ref::<crate::Blob>() .downcast_ref::<crate::Blob>()
.map_or_else(|| self.type_error(), |x| _visitor.visit_bytes(x)); .map_or_else(|| self.type_error(), |x| _visitor.visit_bytes(x));
@ -372,7 +368,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
} }
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if self.value.is::<()>() { if self.0.is::<()>() {
visitor.visit_none() visitor.visit_none()
} else { } else {
visitor.visit_some(self) visitor.visit_some(self)
@ -380,7 +376,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
} }
fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.value self.0
.downcast_ref::<()>() .downcast_ref::<()>()
.map_or_else(|| self.type_error(), |_| visitor.visit_unit()) .map_or_else(|| self.type_error(), |_| visitor.visit_unit())
} }
@ -403,7 +399,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_seq<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_seq<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return self.value.downcast_ref::<crate::Array>().map_or_else( return self.0.downcast_ref::<crate::Array>().map_or_else(
|| self.type_error(), || self.type_error(),
|arr| _visitor.visit_seq(IterateDynamicArray::new(arr.iter())), |arr| _visitor.visit_seq(IterateDynamicArray::new(arr.iter())),
); );
@ -427,7 +423,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_map<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_map<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return self.value.downcast_ref::<crate::Map>().map_or_else( return self.0.downcast_ref::<crate::Map>().map_or_else(
|| self.type_error(), || self.type_error(),
|map| { |map| {
_visitor.visit_map(IterateMap::new( _visitor.visit_map(IterateMap::new(
@ -456,11 +452,11 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
_variants: &'static [&'static str], _variants: &'static [&'static str],
visitor: V, visitor: V,
) -> RhaiResultOf<V::Value> { ) -> RhaiResultOf<V::Value> {
if let Some(s) = self.value.read_lock::<ImmutableString>() { if let Some(s) = self.0.read_lock::<ImmutableString>() {
visitor.visit_enum(s.as_str().into_deserializer()) visitor.visit_enum(s.as_str().into_deserializer())
} else { } else {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return self.value.downcast_ref::<crate::Map>().map_or_else( return self.0.downcast_ref::<crate::Map>().map_or_else(
|| self.type_error(), || self.type_error(),
|map| { |map| {
let mut iter = map.iter(); let mut iter = map.iter();

View File

@ -1,14 +1,17 @@
//! Implementations of [`serde::Deserialize`]. //! Implementations of [`serde::Deserialize`].
use crate::{Dynamic, ImmutableString, INT}; use crate::{Dynamic, Identifier, ImmutableString, Scope, INT};
use serde::de::{Deserialize, Deserializer, Error, Visitor}; use serde::{
de::{Error, SeqAccess, Visitor},
Deserialize, Deserializer,
};
use std::fmt; use std::fmt;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
struct DynamicVisitor; struct DynamicVisitor;
impl<'d> Visitor<'d> for DynamicVisitor { impl<'de> Visitor<'de> for DynamicVisitor {
type Value = Dynamic; type Value = Dynamic;
#[cold] #[cold]
@ -145,12 +148,12 @@ impl<'d> Visitor<'d> for DynamicVisitor {
} }
#[inline(always)] #[inline(always)]
fn visit_newtype_struct<D: Deserializer<'d>>(self, de: D) -> Result<Self::Value, D::Error> { fn visit_newtype_struct<D: Deserializer<'de>>(self, de: D) -> Result<Self::Value, D::Error> {
Deserialize::deserialize(de) Deserialize::deserialize(de)
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
fn visit_seq<A: serde::de::SeqAccess<'d>>(self, mut seq: A) -> Result<Self::Value, A::Error> { fn visit_seq<A: serde::de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut arr = crate::Array::new(); let mut arr = crate::Array::new();
while let Some(v) = seq.next_element()? { while let Some(v) = seq.next_element()? {
@ -161,7 +164,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
fn visit_map<M: serde::de::MapAccess<'d>>(self, mut map: M) -> Result<Self::Value, M::Error> { fn visit_map<M: serde::de::MapAccess<'de>>(self, mut map: M) -> Result<Self::Value, M::Error> {
let mut m = crate::Map::new(); let mut m = crate::Map::new();
while let Some((k, v)) = map.next_entry()? { while let Some((k, v)) = map.next_entry()? {
@ -172,17 +175,69 @@ impl<'d> Visitor<'d> for DynamicVisitor {
} }
} }
impl<'d> Deserialize<'d> for Dynamic { impl<'de> Deserialize<'de> for Dynamic {
#[inline(always)] #[inline(always)]
fn deserialize<D: Deserializer<'d>>(de: D) -> Result<Self, D::Error> { fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
de.deserialize_any(DynamicVisitor) deserializer.deserialize_any(DynamicVisitor)
} }
} }
impl<'d> Deserialize<'d> for ImmutableString { impl<'de> Deserialize<'de> for ImmutableString {
#[inline] #[inline(always)]
fn deserialize<D: Deserializer<'d>>(de: D) -> Result<Self, D::Error> { fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s: String = Deserialize::deserialize(de)?; let s: String = Deserialize::deserialize(deserializer)?;
Ok(s.into()) Ok(s.into())
} }
} }
impl<'de> Deserialize<'de> for Scope<'de> {
#[inline(always)]
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Debug, Clone, Hash, Deserialize)]
struct ScopeEntry {
pub name: Identifier,
pub value: Dynamic,
#[serde(default)]
pub is_constant: bool,
}
struct VecVisitor;
impl<'de> Visitor<'de> for VecVisitor {
type Value = Scope<'static>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a sequence")
}
#[inline]
fn visit_seq<A>(self, mut access: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut scope = if let Some(size) = access.size_hint() {
Scope::with_capacity(size)
} else {
Scope::new()
};
while let Some(ScopeEntry {
name,
value,
is_constant,
}) = access.next_element()?
{
if is_constant {
scope.push_constant_dynamic(name, value);
} else {
scope.push_dynamic(name, value);
}
}
Ok(scope)
}
}
deserializer.deserialize_seq(VecVisitor)
}
}

View File

@ -1,8 +1,8 @@
//! Implementations of [`serde::Serialize`]. //! Implementations of [`serde::Serialize`].
use crate::types::dynamic::Union; use crate::types::dynamic::Union;
use crate::{Dynamic, ImmutableString}; use crate::{Dynamic, ImmutableString, Scope};
use serde::ser::{Serialize, Serializer}; use serde::{ser::SerializeSeq, Serialize, Serializer};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -88,3 +88,33 @@ impl Serialize for ImmutableString {
ser.serialize_str(self.as_str()) ser.serialize_str(self.as_str())
} }
} }
impl Serialize for Scope<'_> {
#[inline(always)]
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
#[derive(Debug, Clone, Hash, Serialize)]
struct ScopeEntry<'a> {
pub name: &'a str,
pub value: &'a Dynamic,
#[serde(default, skip_serializing_if = "is_false")]
pub is_constant: bool,
}
fn is_false(value: &bool) -> bool {
!value
}
let mut ser = ser.serialize_seq(Some(self.len()))?;
for (name, is_constant, value) in self.iter_raw() {
let entry = ScopeEntry {
name,
value,
is_constant,
};
ser.serialize_element(&entry)?;
}
ser.end()
}
}

View File

@ -22,6 +22,14 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
/// Currently the lifetime parameter is not used, but it is not guaranteed to remain unused for /// Currently the lifetime parameter is not used, but it is not guaranteed to remain unused for
/// future versions. Until then, `'static` can be used. /// future versions. Until then, `'static` can be used.
/// ///
/// # Constant Generic Parameter
///
/// There is a constant generic parameter that indicates how many entries to keep inline.
/// As long as the number of entries does not exceed this limit, no allocations occur.
/// The default is 8.
///
/// A larger value makes [`Scope`] larger, but reduces the chance of allocations.
///
/// # Thread Safety /// # Thread Safety
/// ///
/// Currently, [`Scope`] is neither [`Send`] nor [`Sync`]. Turn on the `sync` feature to make it /// Currently, [`Scope`] is neither [`Send`] nor [`Sync`]. Turn on the `sync` feature to make it
@ -61,7 +69,7 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
// //
// [`Dynamic`] is reasonably small so packing it tightly improves cache performance. // [`Dynamic`] is reasonably small so packing it tightly improves cache performance.
#[derive(Debug, Hash, Default)] #[derive(Debug, Hash, Default)]
pub struct Scope<'a> { pub struct Scope<'a, const N: usize = SCOPE_ENTRIES_INLINED> {
/// Current value of the entry. /// Current value of the entry.
values: SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>, values: SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>,
/// Name of the entry. /// Name of the entry.
@ -170,6 +178,28 @@ impl Scope<'_> {
dummy: PhantomData, dummy: PhantomData,
} }
} }
/// Create a new [`Scope`] with a particular capacity.
///
/// # Example
///
/// ```
/// use rhai::Scope;
///
/// let mut my_scope = Scope::with_capacity(10);
///
/// my_scope.push("x", 42_i64);
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
/// ```
#[inline(always)]
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
values: SmallVec::with_capacity(capacity),
names: SmallVec::with_capacity(capacity),
aliases: SmallVec::with_capacity(capacity),
dummy: PhantomData,
}
}
/// Empty the [`Scope`]. /// Empty the [`Scope`].
/// ///
/// # Example /// # Example

View File

@ -2,7 +2,7 @@
use rhai::{ use rhai::{
serde::{from_dynamic, to_dynamic}, serde::{from_dynamic, to_dynamic},
Dynamic, Engine, EvalAltResult, ImmutableString, INT, Dynamic, Engine, EvalAltResult, ImmutableString, Scope, INT,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
@ -823,3 +823,34 @@ fn test_serde_json_borrowed_string() {
println!("value: {value:?}"); println!("value: {value:?}");
let _: Dynamic = serde_json::from_value(value).unwrap(); let _: Dynamic = serde_json::from_value(value).unwrap();
} }
#[test]
#[cfg(not(feature = "no_object"))]
fn test_serde_scope() {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct TestStruct {
foo: Option<char>,
}
let mut scope = Scope::new();
scope.push("x", 42 as INT);
scope.push_constant("y", true);
scope.push("z", TestStruct { foo: None });
let json = serde_json::to_string(&scope).unwrap();
assert_eq!(
json,
r#"[{"name":"x","value":42},{"name":"y","value":true,"is_constant":true},{"name":"z","value":"serde::test_serde_scope::TestStruct"}]"#
);
scope = serde_json::from_str(&json).unwrap();
assert_eq!(scope.len(), 3);
assert_eq!(scope.get_value::<INT>("x").unwrap(), 42);
assert_eq!(scope.get_value::<bool>("y").unwrap(), true);
assert_eq!(
scope.get_value::<String>("z").unwrap(),
"serde::test_serde_scope::TestStruct"
);
}