diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 493911b5..df6f3b20 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: os: [ubuntu-latest] flags: - "" - - "--features serde,internals" + - "--features metadata,internals" - "--features unchecked" - "--features sync" - "--features no_optimize" @@ -32,7 +32,7 @@ jobs: - "--features no_module" - "--features no_closure" - "--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] experimental: [false] include: diff --git a/Cargo.toml b/Cargo.toml index cc9e2051..ef8767ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,4 +97,4 @@ optional = true instant= { version = "0.1", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant [package.metadata.docs.rs] -features = [ "serde", "internals" ] +features = [ "metadata", "internals" ] diff --git a/README.md b/README.md index 8f5f82e9..83428cf0 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Supported targets and builds * All common CPU targets for Windows, Linux and MacOS. * WebAssembly (WASM) * `no-std` -* Minimum Rust version 1.45 +* Minimum Rust version 1.49 Standard features diff --git a/RELEASES.md b/RELEASES.md index e1445d1e..4fcc5001 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,6 +4,20 @@ Rhai Release Notes 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 diff --git a/src/serde_impl/de.rs b/src/serde_impl/de.rs index e17d177c..eb35ae63 100644 --- a/src/serde_impl/de.rs +++ b/src/serde_impl/de.rs @@ -4,10 +4,8 @@ use super::str::ImmutableStringDeserializer; use crate::dynamic::Union; use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString}; use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, Position}; -use serde::de::{ - DeserializeSeed, Deserializer, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor, -}; -use serde::Deserialize; +use serde::de::{DeserializeSeed, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor}; +use serde::{Deserialize, Deserializer}; #[cfg(not(feature = "no_index"))] use crate::Array; @@ -50,13 +48,9 @@ impl<'de> DynamicDeserializer<'de> { visitor: V, ) -> Result> { #[cfg(not(feature = "only_i32"))] - { - visitor.visit_i64(v) - } + return visitor.visit_i64(v); #[cfg(feature = "only_i32")] - { - visitor.visit_i32(v) - } + return visitor.visit_i32(v); } } diff --git a/src/serde_impl/deserialize.rs b/src/serde_impl/deserialize.rs new file mode 100644 index 00000000..8d998ad8 --- /dev/null +++ b/src/serde_impl/deserialize.rs @@ -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(self, v: bool) -> Result { + Ok(v.into()) + } + fn visit_i8(self, v: i8) -> Result { + Ok(INT::from(v).into()) + } + fn visit_i16(self, v: i16) -> Result { + Ok(INT::from(v).into()) + } + fn visit_i32(self, v: i32) -> Result { + Ok(INT::from(v).into()) + } + fn visit_i64(self, v: i64) -> Result { + #[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(self, v: u8) -> Result { + Ok(INT::from(v).into()) + } + fn visit_u16(self, v: u16) -> Result { + Ok(INT::from(v).into()) + } + fn visit_u32(self, v: u32) -> Result { + #[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(self, v: u64) -> Result { + #[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(self, v: f32) -> Result { + #[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(self, v: f64) -> Result { + #[cfg(not(feature = "f32_float"))] + return Ok(v.into()); + #[cfg(feature = "f32_float")] + return self.visit_f32(v as f32); + } + + fn visit_char(self, v: char) -> Result { + self.visit_string(v.to_string()) + } + fn visit_str(self, v: &str) -> Result { + Ok(v.into()) + } + fn visit_borrowed_str(self, v: &str) -> Result { + self.visit_str(v) + } + fn visit_string(self, v: String) -> Result { + Ok(v.into()) + } + + fn visit_unit(self) -> Result { + Ok(Dynamic::UNIT) + } + + fn visit_newtype_struct>(self, de: D) -> Result { + Deserialize::deserialize(de) + } + + #[cfg(not(feature = "no_index"))] + fn visit_seq>(self, mut seq: A) -> Result { + 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>(self, mut map: M) -> Result { + 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>(de: D) -> Result { + de.deserialize_any(DynamicVisitor) + } +} + +impl<'d> Deserialize<'d> for ImmutableString { + fn deserialize>(de: D) -> Result { + let s: String = Deserialize::deserialize(de)?; + Ok(s.into()) + } +} diff --git a/src/serde_impl/mod.rs b/src/serde_impl/mod.rs index d38105a1..1c90abbf 100644 --- a/src/serde_impl/mod.rs +++ b/src/serde_impl/mod.rs @@ -1,7 +1,9 @@ //! Helper module defining serialization/deserialization support for [`serde`]. pub mod de; +mod deserialize; pub mod ser; +mod serialize; mod str; #[cfg(feature = "metadata")] diff --git a/src/serde_impl/ser.rs b/src/serde_impl/ser.rs index dc62d3c7..edc272cb 100644 --- a/src/serde_impl/ser.rs +++ b/src/serde_impl/ser.rs @@ -4,9 +4,8 @@ use crate::stdlib::{boxed::Box, fmt, string::ToString}; use crate::{Dynamic, EvalAltResult, Position}; use serde::ser::{ Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, - Serializer, }; -use serde::Serialize; +use serde::{Serialize, Serializer}; #[cfg(not(feature = "no_index"))] use crate::Array; diff --git a/src/serde_impl/serialize.rs b/src/serde_impl/serialize.rs new file mode 100644 index 00000000..bdab788f --- /dev/null +++ b/src/serde_impl/serialize.rs @@ -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(&self, ser: S) -> Result { + 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(&self, ser: S) -> Result { + ser.serialize_str(self.as_str()) + } +} diff --git a/tests/serde.rs b/tests/serde.rs index 75291413..1e1a0ad6 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -2,7 +2,7 @@ use rhai::{ serde::{from_dynamic, to_dynamic}, - Dynamic, Engine, EvalAltResult, INT, + Dynamic, Engine, EvalAltResult, ImmutableString, INT, }; use serde::{Deserialize, Serialize}; @@ -290,15 +290,15 @@ fn test_serde_ser_untagged_enum() -> Result<(), Box> { #[test] fn test_serde_de_primary_types() -> Result<(), Box> { - assert_eq!(42_u16, from_dynamic(&Dynamic::from(42_u16))?); - assert_eq!(42 as INT, from_dynamic(&(42 as INT).into())?); - assert_eq!(true, from_dynamic(&true.into())?); - assert_eq!((), from_dynamic(&().into())?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42_u16))?); + assert_eq!(42, from_dynamic::(&(42 as INT).into())?); + assert_eq!(true, from_dynamic::(&true.into())?); + assert_eq!((), from_dynamic::<()>(&().into())?); #[cfg(not(feature = "no_float"))] { - assert_eq!(123.456_f64, from_dynamic(&123.456_f64.into())?); - assert_eq!(123.456_f32, from_dynamic(&Dynamic::from(123.456_f32))?); + assert_eq!(123.456, from_dynamic::(&123.456_f64.into())?); + assert_eq!(123.456, from_dynamic::(&Dynamic::from(123.456_f32))?); } assert_eq!( @@ -311,14 +311,14 @@ fn test_serde_de_primary_types() -> Result<(), Box> { #[test] fn test_serde_de_integer_types() -> Result<(), Box> { - assert_eq!(42_i8, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_i16, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_i32, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_i64, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_u8, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_u16, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_u32, from_dynamic(&Dynamic::from(42 as INT))?); - assert_eq!(42_u64, from_dynamic(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); + assert_eq!(42, from_dynamic::(&Dynamic::from(42 as INT))?); Ok(()) } @@ -635,3 +635,42 @@ fn test_serde_de_untagged_enum() -> Result<(), Box> { 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::()); + + let mut m = d2.cast::(); + + assert_eq!(m["a"].as_int().unwrap(), 123); + assert!(m["c"].as_bool().unwrap()); + + let a = m.remove("b").unwrap().cast::(); + + assert_eq!(a.len(), 3); + assert_eq!(format!("{:?}", a), "[1, 2, 3]"); + + Ok(()) +}