From 6d6776c76190a3e79c47ea5894689b8096a21d2e Mon Sep 17 00:00:00 2001 From: Bob Halley <104104227+bhalleycf@users.noreply.github.com> Date: Mon, 6 Mar 2023 04:19:27 -0800 Subject: [PATCH] Let deserialize_bytes() handle an Array as well. (#44) --- README.md | 9 +++++---- src/de.rs | 14 ++++++++++++-- src/ser.rs | 19 ++++++++++++++++--- tests/common/mod.rs | 14 ++++++++++++++ 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c11530c..3b83751 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ By default, Rust ⬄ JavaScript conversions in `serde-wasm-bindgen` follow this | `HashMap`, `BTreeMap`, etc. | `Map` | any iterable over `[K, V]` | | `Struct { key1: value1, … }` | `{ key1: value1, … }` object | | | tuple, `Vec`, `HashSet`, etc. | `T[]` array | any iterable over `T` | -| [`serde_bytes`] byte buffer | `Uint8Array` | `ArrayBuffer` | +| [`serde_bytes`] byte buffer | `Uint8Array` | `ArrayBuffer`, `Array` | [as configured in Serde]: https://serde.rs/enum-representations.html [safe integer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger @@ -121,13 +121,14 @@ are additionally supported when deserializing from JavaScript to the Rust type. ### Serializer configuration options -You can customize serialization from Rust to JavaScript by setting the following options on the [`Serializer::new()`](https://docs.rs/serde-wasm-bindgen/latest/serde_wasm_bindgen/struct.Serializer.html) instance: +You can customize serialization from Rust to JavaScript by setting the following options on the [`Serializer::new()`](https://docs.rs/serde-wasm-bindgen/latest/serde_wasm_bindgen/struct.Serializer.html) instance (all default to false): - `.serialize_missing_as_null(true)`: Serialize `()`, unit structs and `Option::None` to `null` instead of `undefined`. -- `.serialize_maps_as_objects(true)`: Serialize maps into plain JavaScript objects instead of ES2015 Maps. false by default. +- `.serialize_maps_as_objects(true)`: Serialize maps into plain JavaScript objects instead of ES2015 Maps. - `.serialize_large_number_types_as_bigints(true)`: Serialize `u64`, `i64`, `usize` and `isize` to `bigint`s instead of attempting to fit them into the [safe integer] `number` or failing. +- `.serialize_bytes_as_arrays(true)`: Serialize bytes into plain JavaScript arrays instead of ES2015 Uint8Arrays. -You can also use the `Serializer::json_compatible()` preset to create a JSON compatible serializer. It enables `serialize_missing_as_null` and `serialize_maps_as_objects` under the hood. +You can also use the `Serializer::json_compatible()` preset to create a JSON compatible serializer. It enables `serialize_missing_as_null`, `serialize_maps_as_objects`, and `serialize_bytes_as_arrays` under the hood. ## License diff --git a/src/de.rs b/src/de.rs index 6322045..0f39ffb 100644 --- a/src/de.rs +++ b/src/de.rs @@ -239,6 +239,14 @@ impl Deserializer { _ => self.invalid_type(visitor), } } + + fn deserialize_from_array<'de, V: de::Visitor<'de>>( + &self, + visitor: V, + array: &Array, + ) -> Result { + visitor.visit_seq(SeqDeserializer::new(array.iter().map(Deserializer::from))) + } } impl<'de> de::Deserializer<'de> for Deserializer { @@ -460,7 +468,7 @@ impl<'de> de::Deserializer<'de> for Deserializer { /// - Any Rust sequence from Serde point of view ([`Vec`], [`HashSet`](std::collections::HashSet), etc.) fn deserialize_seq>(self, visitor: V) -> Result { if let Some(arr) = self.value.dyn_ref::() { - visitor.visit_seq(SeqDeserializer::new(arr.iter().map(Deserializer::from))) + self.deserialize_from_array(visitor, arr) } else if let Some(iter) = js_sys::try_iter(&self.value)? { visitor.visit_seq(SeqAccess { iter }) } else { @@ -559,10 +567,12 @@ impl<'de> de::Deserializer<'de> for Deserializer { /// /// Supported inputs: /// - `ArrayBuffer` - converted to an `Uint8Array` view first. - /// - `Uint8Array` - copied to a newly created `Vec` on the Rust side. + /// - `Uint8Array`, `Array` - copied to a newly created `Vec` on the Rust side. fn deserialize_byte_buf>(self, visitor: V) -> Result { if let Some(bytes) = self.as_bytes() { visitor.visit_byte_buf(bytes) + } else if let Some(arr) = self.value.dyn_ref::() { + self.deserialize_from_array(visitor, arr) } else { self.invalid_type(visitor) } diff --git a/src/ser.rs b/src/ser.rs index 73557cf..8c42300 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -219,6 +219,7 @@ pub struct Serializer { serialize_missing_as_null: bool, serialize_maps_as_objects: bool, serialize_large_number_types_as_bigints: bool, + serialize_bytes_as_arrays: bool, } impl Serializer { @@ -228,6 +229,7 @@ impl Serializer { serialize_missing_as_null: false, serialize_maps_as_objects: false, serialize_large_number_types_as_bigints: false, + serialize_bytes_as_arrays: false, } } @@ -240,6 +242,7 @@ impl Serializer { serialize_missing_as_null: true, serialize_maps_as_objects: true, serialize_large_number_types_as_bigints: false, + serialize_bytes_as_arrays: true, } } @@ -263,6 +266,13 @@ impl Serializer { self.serialize_large_number_types_as_bigints = value; self } + + /// Set to `true` to serialize bytes into plain JavaScript arrays instead of + /// ES2015 `Uint8Array`s. `false` by default. + pub const fn serialize_bytes_as_arrays(mut self, value: bool) -> Self { + self.serialize_bytes_as_arrays = value; + self + } } macro_rules! forward_to_into { @@ -354,9 +364,12 @@ impl<'s> ser::Serializer for &'s Serializer { // // This is necessary because any allocation in WebAssembly can require reallocation of the // backing memory, which will invalidate existing views (including `Uint8Array`). - Ok(JsValue::from(Uint8Array::new( - unsafe { Uint8Array::view(v) }.as_ref(), - ))) + let view = unsafe { Uint8Array::view(v) }; + if self.serialize_bytes_as_arrays { + Ok(JsValue::from(Array::from(view.as_ref()))) + } else { + Ok(JsValue::from(Uint8Array::new(view.as_ref()))) + } } fn serialize_none(self) -> Result { diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 1b595c8..c648853 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -479,6 +479,20 @@ fn bytes() { assert_eq!(deserialized.as_ref(), orig_src); } +#[wasm_bindgen_test] +fn bytes_as_array() { + let src = [1, 2, 3]; + // Convert to a JS value + let serializer = Serializer::new().serialize_bytes_as_arrays(true); + let bytes = &serde_bytes::Bytes::new(&src); + let value = bytes.serialize(&serializer).unwrap(); + // Make sure the JS value is an Array. + value.dyn_ref::().unwrap(); + // Now, try to deserialize back. + let deserialized: serde_bytes::ByteBuf = from_value(value).unwrap(); + assert_eq!(deserialized.as_ref(), src); +} + #[wasm_bindgen_test] fn options() { test_via_into(Some(0_u32), 0_u32);