From b3b3a083b8c4318df9ab5e68deca24f06f84f935 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 4 Jul 2020 15:39:40 +0800 Subject: [PATCH] Add docs on serde feature. --- Cargo.toml | 8 +- RELEASES.md | 9 ++ doc/src/rust/serde.md | 15 ++- src/lib.rs | 4 +- src/serde/de.rs | 173 ++++++++++++++++++++++++---------- src/serde/mod.rs | 2 + src/serde/ser.rs | 212 ++++++++++++++++++++++++------------------ src/serde/str.rs | 51 +++++----- 8 files changed, 303 insertions(+), 171 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fa8f940c..6380d11b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ num-traits = { version = "0.2.11", default-features = false } [features] #default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"] -default = [] +default = ["serde"] plugins = [] unchecked = [] # unchecked arithmetic sync = [] # restrict to only types that implement Send + Sync @@ -73,3 +73,9 @@ optional = true [target.'cfg(target_arch = "wasm32")'.dependencies] instant= { version = "0.1.4", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant + +[package.metadata.docs.rs] +features = ["serde"] + +[package.metadata.playground] +features = ["serde"] diff --git a/RELEASES.md b/RELEASES.md index 57645f5f..0a1ead2b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,11 +4,20 @@ Rhai Release Notes Version 0.17.0 ============== +This version adds [`serde`](https://crates.io/crates/serde) support for working with `Dynamic` values (particularly _object maps_). + Breaking changes ---------------- * `EvalAltResult::ErrorMismatchOutputType` has an extra argument containing the name of the requested type. +New features +------------ + +* New `serde` feature to allow serializating/deserializating to/from `Dynamic` values using [`serde`](https://crates.io/crates/serde). + This is particularly useful when converting a Rust `struct` to a `Dynamic` _object map_ and back. + + Version 0.16.1 ============== diff --git a/doc/src/rust/serde.md b/doc/src/rust/serde.md index 5b12ddc6..de59dc34 100644 --- a/doc/src/rust/serde.md +++ b/doc/src/rust/serde.md @@ -13,16 +13,21 @@ A [`Dynamic`] can be seamlessly converted to and from a type that implements `se Serialization ------------- -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. - -The function `rhai::see::to_dynamic` automatically converts any Rust type that implements `serde::Serialize` +The function `rhai::ser::to_dynamic` automatically converts any Rust type that implements `serde::Serialize` into a [`Dynamic`]. +This is usually not necessary because using [`Dynamic::from`][`Dynamic`] is much easier and is essentially +the same thing. The only difference is treatment for integer values. `Dynamic::from` will keep the different +integer types intact, while `rhai::ser::to_dynamic` will convert them all into [`INT`][standard types] +(i.e. the system integer type which is `i64` or `i32` depending on the [`only_i32`] feature). + 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]. +While it is also 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::ser::to_dynamic` serializes it to [`Dynamic`] directly via `serde` without going through the `JSON` step. + ```rust use rhai::{Dynamic, Map}; use rhai::ser::to_dynamic; diff --git a/src/lib.rs b/src/lib.rs index c6626b98..50c574f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ //! | `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. | //! | `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. | +//! | `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). | //! //! See [The Rhai Book](https://schungx.github.io/rhai) for details on the Rhai script engine and language. @@ -130,10 +130,12 @@ pub mod module_resolvers { pub use crate::module::resolvers::*; } +/// Serialization support for [`serde`](https://crates.io/crates/serde). #[cfg(feature = "serde")] pub mod ser { pub use crate::serde::ser::to_dynamic; } +/// Deserialization support for [`serde`](https://crates.io/crates/serde). #[cfg(feature = "serde")] pub mod de { pub use crate::serde::de::from_dynamic; diff --git a/src/serde/de.rs b/src/serde/de.rs index 2b4b7bd9..be466508 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -1,5 +1,8 @@ +//! Implement deserialization support of `Dynamic` for [`serde`](https://crates.io/crates/serde). + use super::str::ImmutableStringDeserializer; use crate::any::{Dynamic, Union}; +use crate::error::ParseErrorType; use crate::result::EvalAltResult; use crate::token::Position; use crate::utils::ImmutableString; @@ -22,26 +25,83 @@ use crate::stdlib::time::Instant; #[cfg(target_arch = "wasm32")] use instant::Instant; +/// Deserializer for `Dynamic` which is kept as a reference. +/// +/// The reference is necessary because the deserialized type may hold references +/// (especially `&str`) to the source `Dynamic`. pub struct DynamicDeserializer<'a> { value: &'a Dynamic, } impl<'de> DynamicDeserializer<'de> { + /// Create a `DynamicDeserializer` from a reference to a `Dynamic` value. + /// + /// The reference is necessary because the deserialized type may hold references + /// (especially `&str`) to the source `Dynamic`. pub fn from_dynamic(value: &'de Dynamic) -> Self { Self { value } } - pub fn type_error(&self) -> Result> { - self.type_error_str(type_name::()) - } - pub fn type_error_str(&self, name: &str) -> Result> { + /// Shortcut for a type conversion error. + fn type_error(&self) -> Result> { Err(Box::new(EvalAltResult::ErrorMismatchOutputType( - name.into(), + type_name::().into(), self.value.type_name().into(), Position::none(), ))) } } +/// Deserialize a `Dynamic` value into a Rust type that implements `serde::Deserialize`. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> Result<(), Box> { +/// # #[cfg(not(feature = "no_index"))] +/// # #[cfg(not(feature = "no_object"))] +/// # { +/// use rhai::{Dynamic, Array, Map, INT}; +/// use rhai::de::from_dynamic; +/// use serde::Deserialize; +/// +/// #[derive(Debug, Deserialize, PartialEq)] +/// struct Hello { +/// a: INT, +/// b: bool, +/// } +/// +/// #[derive(Debug, Deserialize, PartialEq)] +/// struct Test { +/// int: u32, +/// seq: Vec, +/// 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!["foo".into(), "bar".into(), "baz".into()]; +/// map.insert("seq".into(), arr.into()); +/// +/// let value: Test = from_dynamic(&map.into())?; +/// +/// let expected = Test { +/// int: 42, +/// seq: vec!["foo".into(), "bar".into(), "baz".into()], +/// obj: Hello { a: 123, b: true }, +/// }; +/// +/// assert_eq!(value, expected); +/// # } +/// # Ok(()) +/// # } +/// ``` pub fn from_dynamic<'de, T: Deserialize<'de>>( value: &'de Dynamic, ) -> Result> { @@ -50,8 +110,8 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>( impl Error for Box { fn custom(err: T) -> Self { - Box::new(EvalAltResult::ErrorRuntime( - err.to_string(), + Box::new(EvalAltResult::ErrorParsing( + ParseErrorType::BadInput(err.to_string()), Position::none(), )) } @@ -76,10 +136,10 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { Union::Array(_) => self.deserialize_seq(visitor), #[cfg(not(feature = "no_object"))] Union::Map(_) => self.deserialize_map(visitor), - Union::FnPtr(_) => unimplemented!(), + Union::FnPtr(_) => self.type_error(), #[cfg(not(feature = "no_std"))] - Union::Variant(value) if value.is::() => unimplemented!(), + Union::Variant(value) if value.is::() => self.type_error(), Union::Variant(value) if value.is::() => self.deserialize_i8(visitor), Union::Variant(value) if value.is::() => self.deserialize_i16(visitor), @@ -90,64 +150,60 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { Union::Variant(value) if value.is::() => self.deserialize_u32(visitor), Union::Variant(value) if value.is::() => self.deserialize_u64(visitor), - Union::Variant(_) => self.type_error_str("any"), + Union::Variant(_) => self.type_error(), } } fn deserialize_bool>(self, visitor: V) -> Result> { - visitor.visit_bool( - self.value - .as_bool() - .or_else(|_| self.type_error::())?, - ) + visitor.visit_bool(self.value.as_bool().or_else(|_| self.type_error())?) } fn deserialize_i8>(self, visitor: V) -> Result> { self.value .downcast_ref::() - .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>(self, visitor: V) -> Result> { self.value .downcast_ref::() - .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>(self, visitor: V) -> Result> { self.value .downcast_ref::() - .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>(self, visitor: V) -> Result> { self.value .downcast_ref::() - .map_or_else(|| self.type_error::(), |&x| visitor.visit_i64(x)) + .map_or_else(|| self.type_error(), |&x| visitor.visit_i64(x)) } fn deserialize_u8>(self, visitor: V) -> Result> { self.value .downcast_ref::() - .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>(self, visitor: V) -> Result> { self.value .downcast_ref::() - .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>(self, visitor: V) -> Result> { self.value .downcast_ref::() - .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>(self, visitor: V) -> Result> { self.value .downcast_ref::() - .map_or_else(|| self.type_error::(), |&x| visitor.visit_u64(x)) + .map_or_else(|| self.type_error(), |&x| visitor.visit_u64(x)) } fn deserialize_f32>(self, visitor: V) -> Result> { @@ -155,7 +211,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { { self.value .downcast_ref::() - .map_or_else(|| self.type_error::(), |&x| visitor.visit_f32(x)) + .map_or_else(|| self.type_error(), |&x| visitor.visit_f32(x)) } #[cfg(feature = "no_float")] self.type_error_str("f32") @@ -166,7 +222,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { { self.value .downcast_ref::() - .map_or_else(|| self.type_error::(), |&x| visitor.visit_f64(x)) + .map_or_else(|| self.type_error(), |&x| visitor.visit_f64(x)) } #[cfg(feature = "no_float")] self.type_error_str("f64") @@ -175,12 +231,12 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { fn deserialize_char>(self, visitor: V) -> Result> { self.value .downcast_ref::() - .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>(self, visitor: V) -> Result> { self.value.downcast_ref::().map_or_else( - || self.type_error::(), + || self.type_error(), |x| visitor.visit_borrowed_str(x.as_str()), ) } @@ -193,21 +249,21 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } fn deserialize_bytes>(self, _: V) -> Result> { - self.type_error_str("bytes array") + self.type_error() } fn deserialize_byte_buf>(self, _: V) -> Result> { - self.type_error_str("bytes array") + self.type_error() } fn deserialize_option>(self, _: V) -> Result> { - self.type_error_str("bytes array") + self.type_error() } fn deserialize_unit>(self, visitor: V) -> Result> { self.value .downcast_ref::<()>() - .map_or_else(|| self.type_error::<(), _>(), |_| visitor.visit_unit()) + .map_or_else(|| self.type_error(), |_| visitor.visit_unit()) } fn deserialize_unit_struct>( @@ -230,12 +286,12 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { #[cfg(not(feature = "no_index"))] { self.value.downcast_ref::().map_or_else( - || self.type_error::(), + || self.type_error(), |arr| visitor.visit_seq(IterateArray::new(arr.iter())), ) } #[cfg(feature = "no_index")] - self.type_error_str("array") + self.type_error() } fn deserialize_tuple>( @@ -259,12 +315,12 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { #[cfg(not(feature = "no_object"))] { self.value.downcast_ref::().map_or_else( - || self.type_error::(), + || self.type_error(), |map| visitor.visit_map(IterateMap::new(map.keys(), map.values())), ) } #[cfg(feature = "no_object")] - self.type_error_str("map") + self.type_error() } fn deserialize_struct>( @@ -282,7 +338,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { _variants: &'static [&'static str], _: V, ) -> Result> { - self.type_error_str("num") + self.type_error() } fn deserialize_identifier>( @@ -300,23 +356,35 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { } } -struct IterateArray<'a, ITER: Iterator> { +/// `SeqAccess` implementation for arrays. +struct IterateArray<'a, ITER> +where + ITER: Iterator, +{ + /// Iterator for a stream of `Dynamic` values. iter: ITER, } -impl<'a, ITER: Iterator> IterateArray<'a, ITER> { +impl<'a, ITER> IterateArray<'a, ITER> +where + ITER: Iterator, +{ pub fn new(iter: ITER) -> Self { Self { iter } } } -impl<'a: 'de, 'de, ITER: Iterator> SeqAccess<'de> for IterateArray<'a, ITER> { +impl<'a: 'de, 'de, ITER> SeqAccess<'de> for IterateArray<'a, ITER> +where + ITER: Iterator, +{ type Error = Box; fn next_element_seed>( &mut self, seed: T, ) -> Result, Box> { + // Deserialize each item coming out of the iterator. match self.iter.next() { None => Ok(None), Some(item) => seed @@ -326,29 +394,32 @@ impl<'a: 'de, 'de, ITER: Iterator> SeqAccess<'de> for Iterat } } -struct IterateMap< - 'a, +/// `MapAccess` implementation for maps. +struct IterateMap<'a, KEYS, VALUES> +where KEYS: Iterator, VALUES: Iterator, -> { +{ + // Iterator for a stream of `Dynamic` keys. keys: KEYS, + // Iterator for a stream of `Dynamic` values. values: VALUES, } -impl<'a, KEYS: Iterator, VALUES: Iterator> - IterateMap<'a, KEYS, VALUES> +impl<'a, KEYS, VALUES> IterateMap<'a, KEYS, VALUES> +where + KEYS: Iterator, + VALUES: Iterator, { pub fn new(keys: KEYS, values: VALUES) -> Self { Self { keys, values } } } -impl< - 'a: 'de, - 'de, - KEYS: Iterator, - VALUES: Iterator, - > MapAccess<'de> for IterateMap<'a, KEYS, VALUES> +impl<'a: 'de, 'de, KEYS, VALUES> MapAccess<'de> for IterateMap<'a, KEYS, VALUES> +where + KEYS: Iterator, + VALUES: Iterator, { type Error = Box; @@ -356,6 +427,7 @@ impl< &mut self, seed: K, ) -> Result, Box> { + // Deserialize each `ImmutableString` key coming out of the keys iterator. match self.keys.next() { None => Ok(None), Some(item) => seed @@ -368,6 +440,7 @@ impl< &mut self, seed: V, ) -> Result> { + // Deserialize each value item coming out of the iterator. seed.deserialize(&mut DynamicDeserializer::from_dynamic( self.values.next().unwrap(), )) diff --git a/src/serde/mod.rs b/src/serde/mod.rs index 9d2e0281..2ed95bb4 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -1,3 +1,5 @@ +//! Helper module defining serialization/deserialization support for [`serde`](https://crates.io/crates/serde). + pub mod de; pub mod ser; mod str; diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 95471e33..1477da2d 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -1,3 +1,5 @@ +//! Implement serialization support of `Dynamic` for [`serde`](https://crates.io/crates/serde). + use crate::any::Dynamic; use crate::result::EvalAltResult; use crate::token::Position; @@ -15,30 +17,72 @@ use serde::Serialize; use crate::stdlib::{any::type_name, fmt, mem}; +/// Serializer for `Dynamic` which is kept as a reference. pub struct DynamicSerializer { + /// Buffer to hold a temporary key. key: Dynamic, + /// Buffer to hold a temporary value. value: Dynamic, } impl DynamicSerializer { + /// Create a `DynamicSerializer` from a `Dynamic` value. 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(), - ))) - } } +/// Serialize a Rust type that implements `serde::Serialize` into a `Dynamic`. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> Result<(), Box> { +/// # #[cfg(not(feature = "no_index"))] +/// # #[cfg(not(feature = "no_object"))] +/// # #[cfg(not(feature = "no_float"))] +/// # { +/// use rhai::{Dynamic, Array, Map, INT}; +/// use rhai::ser::to_dynamic; +/// use serde::Serialize; +/// +/// #[derive(Debug, serde::Serialize, PartialEq)] +/// struct Point { +/// x: f64, +/// y: f64 +/// } +/// +/// #[derive(Debug, serde::Serialize, PartialEq)] +/// 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 value = to_dynamic(x)?; +/// +/// assert!(value.is::()); +/// +/// let map = value.cast::(); +/// let point = map.get("d").unwrap().downcast_ref::().unwrap(); +/// assert_eq!(*point.get("x").unwrap().downcast_ref::().unwrap(), 123.456); +/// assert_eq!(*point.get("y").unwrap().downcast_ref::().unwrap(), 999.0); +/// # } +/// # Ok(()) +/// # } +/// ``` pub fn to_dynamic(value: T) -> Result> { let mut s = DynamicSerializer::new(Default::default()); value.serialize(&mut s) @@ -141,17 +185,11 @@ impl Serializer for &mut DynamicSerializer { } 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"); + Ok(Dynamic::from(v)) } 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"); + Ok(Dynamic::from(v)) } fn serialize_char(self, v: char) -> Result> { @@ -162,8 +200,8 @@ impl Serializer for &mut DynamicSerializer { Ok(v.to_string().into()) } - fn serialize_bytes(self, _: &[u8]) -> Result> { - self.type_error_str("bytes array") + fn serialize_bytes(self, v: &[u8]) -> Result> { + Ok(Dynamic::from(v.to_vec())) } fn serialize_none(self) -> Result> { @@ -216,7 +254,11 @@ impl Serializer for &mut DynamicSerializer { #[cfg(not(feature = "no_index"))] return Ok(DynamicSerializer::new(Array::new().into())); #[cfg(feature = "no_index")] - return self.type_error_str("array"); + return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + "Dynamic".into(), + "array".into(), + Position::none(), + ))); } fn serialize_tuple(self, len: usize) -> Result> { @@ -245,7 +287,11 @@ impl Serializer for &mut DynamicSerializer { #[cfg(not(feature = "no_object"))] return Ok(DynamicSerializer::new(Map::new().into())); #[cfg(feature = "no_object")] - return self.type_error_str("map"); + return Err(Box::new(EvalAltResult::ErrorMismatchOutputType( + "Dynamic".into(), + "map".into(), + Position::none(), + ))); } fn serialize_struct( @@ -278,15 +324,12 @@ impl SerializeSeq for DynamicSerializer { #[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::() - } + let arr = self.value.downcast_mut::().unwrap(); + arr.push(value); + Ok(()) } #[cfg(feature = "no_index")] - self.type_error_str("array") + unreachable!() } // Close the sequence. @@ -294,7 +337,7 @@ impl SerializeSeq for DynamicSerializer { #[cfg(not(feature = "no_index"))] return Ok(self.value); #[cfg(feature = "no_index")] - return self.type_error_str("array"); + unreachable!() } } @@ -309,22 +352,19 @@ impl SerializeTuple for DynamicSerializer { #[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::() - } + let arr = self.value.downcast_mut::().unwrap(); + arr.push(value); + Ok(()) } #[cfg(feature = "no_index")] - self.type_error_str("array") + unreachable!() } fn end(self) -> Result> { #[cfg(not(feature = "no_index"))] return Ok(self.value); #[cfg(feature = "no_index")] - return self.type_error_str("array"); + unreachable!() } } @@ -339,22 +379,19 @@ impl SerializeTupleStruct for DynamicSerializer { #[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::() - } + let arr = self.value.downcast_mut::().unwrap(); + arr.push(value); + Ok(()) } #[cfg(feature = "no_index")] - self.type_error_str("array") + unreachable!() } fn end(self) -> Result> { #[cfg(not(feature = "no_index"))] return Ok(self.value); #[cfg(feature = "no_index")] - return self.type_error_str("array"); + unreachable!() } } @@ -369,22 +406,19 @@ impl SerializeTupleVariant for DynamicSerializer { #[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::() - } + let arr = self.value.downcast_mut::().unwrap(); + arr.push(value); + Ok(()) } #[cfg(feature = "no_index")] - self.type_error_str("array") + unreachable!() } fn end(self) -> Result> { #[cfg(not(feature = "no_index"))] return Ok(self.value); #[cfg(feature = "no_index")] - return self.type_error_str("array"); + unreachable!() } } @@ -399,7 +433,7 @@ impl SerializeMap for DynamicSerializer { Ok(()) } #[cfg(feature = "no_object")] - self.type_error_str("map") + unreachable!() } fn serialize_value( @@ -410,17 +444,20 @@ impl SerializeMap for DynamicSerializer { { let key = mem::take(&mut self.key) .take_immutable_string() - .or_else(|_| self.type_error::())?; + .map_err(|typ| { + Box::new(EvalAltResult::ErrorMismatchOutputType( + "string".into(), + typ.into(), + Position::none(), + )) + })?; let value = value.serialize(&mut *self)?; - if let Some(map) = self.value.downcast_mut::() { - map.insert(key, value); - Ok(()) - } else { - self.type_error::() - } + let map = self.value.downcast_mut::().unwrap(); + map.insert(key, value); + Ok(()) } #[cfg(feature = "no_object")] - self.type_error_str("map") + unreachable!() } fn serialize_entry( @@ -431,26 +468,27 @@ impl SerializeMap for DynamicSerializer { #[cfg(not(feature = "no_object"))] { let key: Dynamic = key.serialize(&mut *self)?; - let key = key - .take_immutable_string() - .or_else(|_| self.type_error::())?; + let key = key.take_immutable_string().map_err(|typ| { + Box::new(EvalAltResult::ErrorMismatchOutputType( + "string".into(), + typ.into(), + Position::none(), + )) + })?; let value = value.serialize(&mut *self)?; - if let Some(map) = self.value.downcast_mut::() { - map.insert(key, value); - Ok(()) - } else { - self.type_error::() - } + let map = self.value.downcast_mut::().unwrap(); + map.insert(key, value); + Ok(()) } #[cfg(feature = "no_object")] - self.type_error_str("map") + unreachable!() } fn end(self) -> Result> { #[cfg(not(feature = "no_object"))] return Ok(self.value); #[cfg(feature = "no_object")] - return self.type_error_str("map"); + unreachable!() } } @@ -466,22 +504,19 @@ impl SerializeStruct for DynamicSerializer { #[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::() - } + let map = self.value.downcast_mut::().unwrap(); + map.insert(key.into(), value); + Ok(()) } #[cfg(feature = "no_object")] - self.type_error_str("map") + unreachable!() } fn end(self) -> Result> { #[cfg(not(feature = "no_object"))] return Ok(self.value); #[cfg(feature = "no_object")] - return self.type_error_str("map"); + unreachable!() } } @@ -497,21 +532,18 @@ impl SerializeStructVariant for DynamicSerializer { #[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::() - } + let map = self.value.downcast_mut::().unwrap(); + map.insert(key.into(), value); + Ok(()) } #[cfg(feature = "no_object")] - self.type_error_str("map") + unreachable!() } fn end(self) -> Result> { #[cfg(not(feature = "no_object"))] return Ok(self.value); #[cfg(feature = "no_object")] - return self.type_error_str("map"); + unreachable!() } } diff --git a/src/serde/str.rs b/src/serde/str.rs index 131cc0fe..23e2c3d2 100644 --- a/src/serde/str.rs +++ b/src/serde/str.rs @@ -1,3 +1,5 @@ +//! Implement deserialization support of `ImmutableString` for [`serde`](https://crates.io/crates/serde). + use crate::result::EvalAltResult; use crate::token::Position; use crate::utils::ImmutableString; @@ -6,20 +8,20 @@ use serde::de::{Deserializer, Visitor}; use crate::stdlib::any::type_name; +/// Deserializer for `ImmutableString`. pub struct ImmutableStringDeserializer<'a> { value: &'a ImmutableString, } impl<'a> ImmutableStringDeserializer<'a> { + /// Create an `ImmutableStringDeserializer` from an `ImmutableString` reference. pub fn from_str(value: &'a ImmutableString) -> Self { Self { value } } - pub fn type_error(&self) -> Result> { - self.type_error_str(type_name::()) - } - pub fn type_error_str(&self, name: &str) -> Result> { + /// Shortcut for a type conversion error. + fn type_error(&self) -> Result> { Err(Box::new(EvalAltResult::ErrorMismatchOutputType( - name.into(), + type_name::().into(), "string".into(), Position::none(), ))) @@ -33,42 +35,43 @@ impl<'de> Deserializer<'de> for &mut ImmutableStringDeserializer<'de> { self.deserialize_str(v) } fn deserialize_bool>(self, _: V) -> Result> { - self.type_error::() + self.type_error() } fn deserialize_i8>(self, _: V) -> Result> { - self.type_error::() + self.type_error() } fn deserialize_i16>(self, _: V) -> Result> { - self.type_error::() + self.type_error() } fn deserialize_i32>(self, _: V) -> Result> { - self.type_error::() + self.type_error() } fn deserialize_i64>(self, _: V) -> Result> { - self.type_error::() + self.type_error() } fn deserialize_u8>(self, _: V) -> Result> { - self.type_error::() + self.type_error() } fn deserialize_u16>(self, _: V) -> Result> { - self.type_error::() + self.type_error() } fn deserialize_u32>(self, _: V) -> Result> { - self.type_error::() + self.type_error() } fn deserialize_u64>(self, _: V) -> Result> { - self.type_error::() + self.type_error() } fn deserialize_f32>(self, _: V) -> Result> { - self.type_error_str("f32") + self.type_error() } fn deserialize_f64>(self, _: V) -> Result> { - self.type_error_str("f64") + self.type_error() } fn deserialize_char>(self, _: V) -> Result> { - self.type_error::() + self.type_error() } fn deserialize_str>(self, v: V) -> Result> { + // Only allow deserialization into a string. v.visit_borrowed_str(self.value.as_str()) } fn deserialize_string>( @@ -78,16 +81,16 @@ impl<'de> Deserializer<'de> for &mut ImmutableStringDeserializer<'de> { self.deserialize_str(visitor) } fn deserialize_bytes>(self, _: V) -> Result> { - self.type_error_str("bytes array") + self.type_error() } fn deserialize_byte_buf>(self, _: V) -> Result> { - self.type_error_str("bytes array") + self.type_error() } fn deserialize_option>(self, _: V) -> Result> { - self.type_error_str("option") + self.type_error() } fn deserialize_unit>(self, _: V) -> Result> { - self.type_error::<(), _>() + self.type_error() } fn deserialize_unit_struct>( self, @@ -104,7 +107,7 @@ impl<'de> Deserializer<'de> for &mut ImmutableStringDeserializer<'de> { v.visit_newtype_struct(self) } fn deserialize_seq>(self, _: V) -> Result> { - self.type_error_str("array") + self.type_error() } fn deserialize_tuple>( self, @@ -122,7 +125,7 @@ impl<'de> Deserializer<'de> for &mut ImmutableStringDeserializer<'de> { self.deserialize_seq(v) } fn deserialize_map>(self, _: V) -> Result> { - self.type_error_str("map") + self.type_error() } fn deserialize_struct>( self, @@ -138,7 +141,7 @@ impl<'de> Deserializer<'de> for &mut ImmutableStringDeserializer<'de> { _variants: &'static [&'static str], _: V, ) -> Result> { - self.type_error_str("enum") + self.type_error() } fn deserialize_identifier>(self, v: V) -> Result> { self.deserialize_str(v)