diff --git a/doc/src/rust/serde.md b/doc/src/rust/serde.md index bf3743b2..5b12ddc6 100644 --- a/doc/src/rust/serde.md +++ b/doc/src/rust/serde.md @@ -13,10 +13,46 @@ A [`Dynamic`] can be seamlessly converted to and from a type that implements `se Serialization ------------- -Serialization by [`serde`](https://crates.io/crates/serde) is not yet implemented. +While 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], +Rhai supports serializing a [`Dynamic`] directly via `serde` without going through the `JSON` step. -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]. +The function `rhai::see::to_dynamic` automatically converts any Rust type that implements `serde::Serialize` +into a [`Dynamic`]. + +In particular, Rust `struct`'s (or any type that is marked as a `serde` map) are converted into [object maps] +while Rust `Vec`'s (or any type that is marked as a `serde` sequence) are converted into [arrays]. + +```rust +use rhai::{Dynamic, Map}; +use rhai::ser::to_dynamic; + +#[derive(Debug, serde::Serialize)] +struct Point { + x: f64, + y: f64 +} + +#[derive(Debug, serde::Serialize)] +struct MyStruct { + a: i64, + b: Vec, + c: bool, + d: Point +} + +let x = MyStruct { + a: 42, + b: vec![ "hello".into(), "world".into() ], + c: true, + d: Point { x: 123.456, y: 999.0 } +}; + +// Convert the 'MyStruct' into a 'Dynamic' +let map: Dynamic = to_dynamic(x); + +map.is::() == true; +``` Deserialization diff --git a/src/any.rs b/src/any.rs index 0a89e57a..2a2ae5a9 100644 --- a/src/any.rs +++ b/src/any.rs @@ -210,15 +210,20 @@ pub(crate) fn map_std_type_name(name: &str) -> &str { "string" } else if name == type_name::<&str>() { "string" - } else if name == type_name::() { - "map" - } else if name == type_name::() { - "array" } else if name == type_name::() { "Fn" } else if name == type_name::() { "timestamp" } else { + #[cfg(not(feature = "no_index"))] + if name == type_name::() { + return "array"; + } + #[cfg(not(feature = "no_object"))] + if name == type_name::() { + return "map"; + } + name } } diff --git a/src/lib.rs b/src/lib.rs index 9d60ecea..77c39460 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,6 +129,10 @@ pub mod module_resolvers { pub use crate::module::resolvers::*; } +#[cfg(feature = "serde")] +pub mod ser { + pub use crate::serde::ser::to_dynamic; +} #[cfg(feature = "serde")] pub mod de { pub use crate::serde::de::from_dynamic; diff --git a/src/parser.rs b/src/parser.rs index 3ff3798f..8dc1af84 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1701,7 +1701,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result Expr::Dot(Box::new((lhs, func, op_pos))), // lhs.rhs - _ => unreachable!(), + (lhs, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())), }) } diff --git a/src/serde/de.rs b/src/serde/de.rs index 63924dd1..2b4b7bd9 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -26,8 +26,8 @@ pub struct DynamicDeserializer<'a> { value: &'a Dynamic, } -impl<'a> DynamicDeserializer<'a> { - pub fn from_dynamic(value: &'a Dynamic) -> Self { +impl<'de> DynamicDeserializer<'de> { + pub fn from_dynamic(value: &'de Dynamic) -> Self { Self { value } } pub fn type_error(&self) -> Result> { diff --git a/src/serde/mod.rs b/src/serde/mod.rs index a4cf45de..9d2e0281 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -1,2 +1,3 @@ pub mod de; +pub mod ser; mod str; diff --git a/src/serde/ser.rs b/src/serde/ser.rs new file mode 100644 index 00000000..95471e33 --- /dev/null +++ b/src/serde/ser.rs @@ -0,0 +1,517 @@ +use crate::any::Dynamic; +use crate::result::EvalAltResult; +use crate::token::Position; + +#[cfg(not(feature = "no_index"))] +use crate::engine::Array; +#[cfg(not(feature = "no_object"))] +use crate::engine::Map; + +use serde::ser::{ + Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, + SerializeTupleStruct, SerializeTupleVariant, Serializer, +}; +use serde::Serialize; + +use crate::stdlib::{any::type_name, fmt, mem}; + +pub struct DynamicSerializer { + key: Dynamic, + value: Dynamic, +} + +impl DynamicSerializer { + pub fn new(value: Dynamic) -> Self { + Self { + key: Default::default(), + value, + } + } + pub fn type_error(&self) -> Result> { + self.type_error_str(type_name::()) + } + pub fn type_error_str(&self, name: &str) -> Result> { + Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + name.into(), + self.value.type_name().into(), + Position::none(), + ))) + } +} + +pub fn to_dynamic(value: T) -> Result> { + let mut s = DynamicSerializer::new(Default::default()); + value.serialize(&mut s) +} + +impl Error for Box { + fn custom(err: T) -> Self { + Box::new(EvalAltResult::ErrorRuntime( + err.to_string(), + Position::none(), + )) + } +} + +impl Serializer for &mut DynamicSerializer { + type Ok = Dynamic; + type Error = Box; + type SerializeSeq = DynamicSerializer; + type SerializeTuple = DynamicSerializer; + type SerializeTupleStruct = DynamicSerializer; + type SerializeTupleVariant = DynamicSerializer; + type SerializeMap = DynamicSerializer; + type SerializeStruct = DynamicSerializer; + type SerializeStructVariant = DynamicSerializer; + + fn serialize_bool(self, v: bool) -> Result> { + Ok(v.into()) + } + + fn serialize_i8(self, v: i8) -> Result> { + #[cfg(not(feature = "only_i32"))] + return self.serialize_i64(i64::from(v)); + #[cfg(feature = "only_i32")] + return self.serialize_i32(i32::from(v)); + } + + fn serialize_i16(self, v: i16) -> Result> { + #[cfg(not(feature = "only_i32"))] + return self.serialize_i64(i64::from(v)); + #[cfg(feature = "only_i32")] + return self.serialize_i32(i32::from(v)); + } + + fn serialize_i32(self, v: i32) -> Result> { + #[cfg(not(feature = "only_i32"))] + return self.serialize_i64(i64::from(v)); + #[cfg(feature = "only_i32")] + return Ok(v.into()); + } + + fn serialize_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.serialize_i32(v as i32); + } + } + + fn serialize_u8(self, v: u8) -> Result> { + #[cfg(not(feature = "only_i32"))] + return self.serialize_i64(i64::from(v)); + #[cfg(feature = "only_i32")] + return self.serialize_i32(i32::from(v)); + } + + fn serialize_u16(self, v: u16) -> Result> { + #[cfg(not(feature = "only_i32"))] + return self.serialize_i64(i64::from(v)); + #[cfg(feature = "only_i32")] + return self.serialize_i32(i32::from(v)); + } + + fn serialize_u32(self, v: u32) -> Result> { + #[cfg(not(feature = "only_i32"))] + return self.serialize_i64(i64::from(v)); + #[cfg(feature = "only_i32")] + if v > i32::MAX as u64 { + return Ok(Dynamic::from(v)); + } else { + return self.serialize_i32(v as i32); + } + } + + fn serialize_u64(self, v: u64) -> Result> { + #[cfg(not(feature = "only_i32"))] + if v > i64::MAX as u64 { + return Ok(Dynamic::from(v)); + } else { + return self.serialize_i64(v as i64); + } + #[cfg(feature = "only_i32")] + if v > i32::MAX as u64 { + return Ok(Dynamic::from(v)); + } else { + return self.serialize_i32(v as i32); + } + } + + fn serialize_f32(self, v: f32) -> Result> { + #[cfg(not(feature = "no_float"))] + return Ok(Dynamic::from(v)); + #[cfg(feature = "no_float")] + return self.type_error_str("f32"); + } + + fn serialize_f64(self, v: f64) -> Result> { + #[cfg(not(feature = "no_float"))] + return Ok(v.into()); + #[cfg(feature = "no_float")] + return self.type_error_str("f64"); + } + + fn serialize_char(self, v: char) -> Result> { + Ok(v.into()) + } + + fn serialize_str(self, v: &str) -> Result> { + Ok(v.to_string().into()) + } + + fn serialize_bytes(self, _: &[u8]) -> Result> { + self.type_error_str("bytes array") + } + + fn serialize_none(self) -> Result> { + Ok(().into()) + } + + fn serialize_some( + self, + value: &T, + ) -> Result> { + value.serialize(&mut *self) + } + + fn serialize_unit(self) -> Result> { + Ok(().into()) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result> { + self.serialize_unit() + } + + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result> { + self.serialize_str(variant) + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result> { + value.serialize(&mut *self) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + value: &T, + ) -> Result> { + value.serialize(&mut *self) + } + + fn serialize_seq(self, _len: Option) -> Result> { + #[cfg(not(feature = "no_index"))] + return Ok(DynamicSerializer::new(Array::new().into())); + #[cfg(feature = "no_index")] + return self.type_error_str("array"); + } + + fn serialize_tuple(self, len: usize) -> Result> { + self.serialize_seq(Some(len)) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + len: usize, + ) -> Result> { + self.serialize_seq(Some(len)) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + len: usize, + ) -> Result> { + self.serialize_seq(Some(len)) + } + + fn serialize_map(self, _len: Option) -> Result> { + #[cfg(not(feature = "no_object"))] + return Ok(DynamicSerializer::new(Map::new().into())); + #[cfg(feature = "no_object")] + return self.type_error_str("map"); + } + + fn serialize_struct( + self, + _name: &'static str, + len: usize, + ) -> Result> { + self.serialize_map(Some(len)) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + len: usize, + ) -> Result> { + self.serialize_map(Some(len)) + } +} + +impl SerializeSeq for DynamicSerializer { + type Ok = Dynamic; + type Error = Box; + + fn serialize_element( + &mut self, + value: &T, + ) -> Result<(), Box> { + #[cfg(not(feature = "no_index"))] + { + let value = value.serialize(&mut *self)?; + if let Some(arr) = self.value.downcast_mut::() { + arr.push(value); + Ok(()) + } else { + self.type_error::() + } + } + #[cfg(feature = "no_index")] + self.type_error_str("array") + } + + // Close the sequence. + fn end(self) -> Result> { + #[cfg(not(feature = "no_index"))] + return Ok(self.value); + #[cfg(feature = "no_index")] + return self.type_error_str("array"); + } +} + +impl SerializeTuple for DynamicSerializer { + type Ok = Dynamic; + type Error = Box; + + fn serialize_element( + &mut self, + value: &T, + ) -> Result<(), Box> { + #[cfg(not(feature = "no_index"))] + { + let value = value.serialize(&mut *self)?; + if let Some(arr) = self.value.downcast_mut::() { + arr.push(value); + Ok(()) + } else { + self.type_error::() + } + } + #[cfg(feature = "no_index")] + self.type_error_str("array") + } + + fn end(self) -> Result> { + #[cfg(not(feature = "no_index"))] + return Ok(self.value); + #[cfg(feature = "no_index")] + return self.type_error_str("array"); + } +} + +impl SerializeTupleStruct for DynamicSerializer { + type Ok = Dynamic; + type Error = Box; + + fn serialize_field( + &mut self, + value: &T, + ) -> Result<(), Box> { + #[cfg(not(feature = "no_index"))] + { + let value = value.serialize(&mut *self)?; + if let Some(arr) = self.value.downcast_mut::() { + arr.push(value); + Ok(()) + } else { + self.type_error::() + } + } + #[cfg(feature = "no_index")] + self.type_error_str("array") + } + + fn end(self) -> Result> { + #[cfg(not(feature = "no_index"))] + return Ok(self.value); + #[cfg(feature = "no_index")] + return self.type_error_str("array"); + } +} + +impl SerializeTupleVariant for DynamicSerializer { + type Ok = Dynamic; + type Error = Box; + + fn serialize_field( + &mut self, + value: &T, + ) -> Result<(), Box> { + #[cfg(not(feature = "no_index"))] + { + let value = value.serialize(&mut *self)?; + if let Some(arr) = self.value.downcast_mut::() { + arr.push(value); + Ok(()) + } else { + self.type_error::() + } + } + #[cfg(feature = "no_index")] + self.type_error_str("array") + } + + fn end(self) -> Result> { + #[cfg(not(feature = "no_index"))] + return Ok(self.value); + #[cfg(feature = "no_index")] + return self.type_error_str("array"); + } +} + +impl SerializeMap for DynamicSerializer { + type Ok = Dynamic; + type Error = Box; + + fn serialize_key(&mut self, key: &T) -> Result<(), Box> { + #[cfg(not(feature = "no_object"))] + { + self.key = key.serialize(&mut *self)?; + Ok(()) + } + #[cfg(feature = "no_object")] + self.type_error_str("map") + } + + fn serialize_value( + &mut self, + value: &T, + ) -> Result<(), Box> { + #[cfg(not(feature = "no_object"))] + { + let key = mem::take(&mut self.key) + .take_immutable_string() + .or_else(|_| self.type_error::())?; + let value = value.serialize(&mut *self)?; + if let Some(map) = self.value.downcast_mut::() { + map.insert(key, value); + Ok(()) + } else { + self.type_error::() + } + } + #[cfg(feature = "no_object")] + self.type_error_str("map") + } + + fn serialize_entry( + &mut self, + key: &K, + value: &T, + ) -> Result<(), Box> { + #[cfg(not(feature = "no_object"))] + { + let key: Dynamic = key.serialize(&mut *self)?; + let key = key + .take_immutable_string() + .or_else(|_| self.type_error::())?; + let value = value.serialize(&mut *self)?; + if let Some(map) = self.value.downcast_mut::() { + map.insert(key, value); + Ok(()) + } else { + self.type_error::() + } + } + #[cfg(feature = "no_object")] + self.type_error_str("map") + } + + fn end(self) -> Result> { + #[cfg(not(feature = "no_object"))] + return Ok(self.value); + #[cfg(feature = "no_object")] + return self.type_error_str("map"); + } +} + +impl SerializeStruct for DynamicSerializer { + type Ok = Dynamic; + type Error = Box; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<(), Box> { + #[cfg(not(feature = "no_object"))] + { + let value = value.serialize(&mut *self)?; + if let Some(map) = self.value.downcast_mut::() { + map.insert(key.into(), value); + Ok(()) + } else { + self.type_error::() + } + } + #[cfg(feature = "no_object")] + self.type_error_str("map") + } + + fn end(self) -> Result> { + #[cfg(not(feature = "no_object"))] + return Ok(self.value); + #[cfg(feature = "no_object")] + return self.type_error_str("map"); + } +} + +impl SerializeStructVariant for DynamicSerializer { + type Ok = Dynamic; + type Error = Box; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<(), Box> { + #[cfg(not(feature = "no_object"))] + { + let value = value.serialize(&mut *self)?; + if let Some(map) = self.value.downcast_mut::() { + map.insert(key.into(), value); + Ok(()) + } else { + self.type_error::() + } + } + #[cfg(feature = "no_object")] + self.type_error_str("map") + } + + fn end(self) -> Result> { + #[cfg(not(feature = "no_object"))] + return Ok(self.value); + #[cfg(feature = "no_object")] + return self.type_error_str("map"); + } +} diff --git a/tests/serde.rs b/tests/serde.rs index daf712f0..82ec330b 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -1,7 +1,7 @@ #![cfg(feature = "serde")] -use rhai::{de::from_dynamic, Dynamic, Engine, EvalAltResult, INT}; -use serde::Deserialize; +use rhai::{de::from_dynamic, ser::to_dynamic, Dynamic, Engine, EvalAltResult, INT}; +use serde::{Deserialize, Serialize}; #[cfg(not(feature = "no_index"))] use rhai::Array; @@ -9,36 +9,115 @@ use rhai::Array; 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()); +fn test_serde_ser_primary_types() -> Result<(), Box> { + assert_eq!( + to_dynamic(42_u64)?.type_name(), + std::any::type_name::() + ); + assert_eq!(to_dynamic(u64::MAX)?.type_name(), "u64"); + assert_eq!( + to_dynamic(42 as INT)?.type_name(), + std::any::type_name::() + ); + assert_eq!(to_dynamic(true)?.type_name(), "bool"); + assert_eq!(to_dynamic(())?.type_name(), "()"); #[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!(to_dynamic(123.456_f64)?.type_name(), "f64"); + assert_eq!(to_dynamic(123.456_f32)?.type_name(), "f32"); } - assert_eq!( - "hello", - from_dynamic::(&"hello".to_string().into()).unwrap() - ); + assert_eq!(to_dynamic("hello".to_string())?.type_name(), "string"); + + Ok(()) } #[test] #[cfg(not(feature = "no_index"))] -fn test_serde_de_array() { +fn test_serde_ser_array() -> Result<(), Box> { let arr: Vec = vec![123, 456, 42, 999]; - assert_eq!(arr, from_dynamic::>(&arr.clone().into()).unwrap()); + + let d = to_dynamic(arr)?; + assert!(d.is::()); + assert_eq!(d.cast::().len(), 4); + + Ok(()) } #[test] -fn test_serde_de_struct() { +#[cfg(not(feature = "no_index"))] +#[cfg(not(feature = "no_object"))] +fn test_serde_ser_struct() -> Result<(), Box> { + #[derive(Debug, Serialize, PartialEq)] + struct Hello { + a: INT, + b: bool, + } + + #[derive(Debug, Serialize, PartialEq)] + struct Test { + int: u32, + seq: Vec, + obj: Hello, + } + + let x = Test { + int: 42, + seq: vec!["hello".into(), "kitty".into(), "world".into()], + obj: Hello { a: 123, b: true }, + }; + + let d = to_dynamic(x)?; + + assert!(d.is::()); + + let mut map = d.cast::(); + let mut obj = map.remove("obj").unwrap().cast::(); + let mut seq = map.remove("seq").unwrap().cast::(); + + assert_eq!(obj.remove("a").unwrap().cast::(), 123); + assert!(obj.remove("b").unwrap().cast::()); + assert_eq!(map.remove("int").unwrap().cast::(), 42); + assert_eq!(seq.len(), 3); + assert_eq!(seq.remove(1).cast::(), "kitty"); + + Ok(()) +} + +#[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())?); + + #[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!( + "hello", + from_dynamic::(&"hello".to_string().into())? + ); + + Ok(()) +} + +#[test] +#[cfg(not(feature = "no_index"))] +fn test_serde_de_array() -> Result<(), Box> { + let arr: Vec = vec![123, 456, 42, 999]; + assert_eq!(arr, from_dynamic::>(&arr.clone().into())?); + Ok(()) +} + +#[test] +#[cfg(not(feature = "no_index"))] +#[cfg(not(feature = "no_object"))] +fn test_serde_de_struct() -> Result<(), Box> { #[derive(Debug, Deserialize, PartialEq)] struct Hello { a: INT, @@ -69,10 +148,15 @@ fn test_serde_de_struct() { seq: vec!["hello".into(), "kitty".into(), "world".into()], obj: Hello { a: 123, b: true }, }; - assert_eq!(expected, from_dynamic(&map.into()).unwrap()); + assert_eq!(expected, from_dynamic(&map.into())?); + + Ok(()) } #[test] +#[cfg(not(feature = "no_index"))] +#[cfg(not(feature = "no_object"))] +#[cfg(not(feature = "no_float"))] fn test_serde_de_script() -> Result<(), Box> { #[derive(Debug, Deserialize)] struct Point { @@ -102,7 +186,7 @@ fn test_serde_de_script() -> Result<(), Box> { )?; // Convert the 'Dynamic' object map into 'MyStruct' - let x: MyStruct = from_dynamic(&result)?; + let _: MyStruct = from_dynamic(&result)?; Ok(()) }