serde_wasm_bindgen/
ser.rs

1use js_sys::{Array, JsString, Map, Number, Object, Uint8Array};
2use serde::ser::{self, Error as _, Serialize};
3use wasm_bindgen::convert::RefFromWasmAbi;
4use wasm_bindgen::prelude::*;
5use wasm_bindgen::JsCast;
6
7use crate::preserve::PRESERVED_VALUE_MAGIC;
8use crate::{static_str_to_js, Error, ObjectExt};
9
10type Result<T = JsValue> = super::Result<T>;
11
12/// Wraps other serializers into an enum tagged variant form.
13///
14/// Results in `{"Variant": ...payload...}` for compatibility with serde-json.
15pub struct VariantSerializer<S> {
16    variant: &'static str,
17    inner: S,
18}
19
20impl<S> VariantSerializer<S> {
21    const fn new(variant: &'static str, inner: S) -> Self {
22        Self { variant, inner }
23    }
24
25    fn end(self, inner: impl FnOnce(S) -> Result) -> Result {
26        let value = inner(self.inner)?;
27        let obj = Object::new();
28        obj.unchecked_ref::<ObjectExt>()
29            .set(static_str_to_js(self.variant), value);
30        Ok(obj.into())
31    }
32}
33
34impl<S: ser::SerializeTupleStruct<Ok = JsValue, Error = Error>> ser::SerializeTupleVariant
35    for VariantSerializer<S>
36{
37    type Ok = JsValue;
38    type Error = Error;
39
40    fn serialize_field<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<()> {
41        self.inner.serialize_field(value)
42    }
43
44    fn end(self) -> Result {
45        self.end(S::end)
46    }
47}
48
49impl<S: ser::SerializeStruct<Ok = JsValue, Error = Error>> ser::SerializeStructVariant
50    for VariantSerializer<S>
51{
52    type Ok = JsValue;
53    type Error = Error;
54
55    fn serialize_field<T: ?Sized + Serialize>(
56        &mut self,
57        key: &'static str,
58        value: &T,
59    ) -> Result<()> {
60        self.inner.serialize_field(key, value)
61    }
62
63    fn end(self) -> Result {
64        self.end(S::end)
65    }
66}
67
68/// Serializes Rust iterables and tuples into JS arrays.
69pub struct ArraySerializer<'s> {
70    serializer: &'s Serializer,
71    target: Array,
72    idx: u32,
73}
74
75impl<'s> ArraySerializer<'s> {
76    fn new(serializer: &'s Serializer) -> Self {
77        Self {
78            serializer,
79            target: Array::new(),
80            idx: 0,
81        }
82    }
83}
84
85impl ser::SerializeSeq for ArraySerializer<'_> {
86    type Ok = JsValue;
87    type Error = Error;
88
89    fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<()> {
90        self.target.set(self.idx, value.serialize(self.serializer)?);
91        self.idx += 1;
92        Ok(())
93    }
94
95    fn end(self) -> Result {
96        Ok(self.target.into())
97    }
98}
99
100impl ser::SerializeTuple for ArraySerializer<'_> {
101    type Ok = JsValue;
102    type Error = Error;
103
104    fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<()> {
105        ser::SerializeSeq::serialize_element(self, value)
106    }
107
108    fn end(self) -> Result {
109        ser::SerializeSeq::end(self)
110    }
111}
112
113impl ser::SerializeTupleStruct for ArraySerializer<'_> {
114    type Ok = JsValue;
115    type Error = Error;
116
117    fn serialize_field<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<()> {
118        ser::SerializeTuple::serialize_element(self, value)
119    }
120
121    fn end(self) -> Result {
122        ser::SerializeTuple::end(self)
123    }
124}
125
126pub enum MapResult {
127    Map(Map),
128    Object(Object),
129}
130
131/// Serializes Rust maps into JS `Map` or plain JS objects.
132///
133/// Plain JS objects are used if `serialize_maps_as_objects` is set to `true`,
134/// but then only string keys are supported.
135pub struct MapSerializer<'s> {
136    serializer: &'s Serializer,
137    target: MapResult,
138    next_key: Option<JsValue>,
139}
140
141impl<'s> MapSerializer<'s> {
142    pub fn new(serializer: &'s Serializer, as_object: bool) -> Self {
143        Self {
144            serializer,
145            target: if as_object {
146                MapResult::Object(Object::new())
147            } else {
148                MapResult::Map(Map::new())
149            },
150            next_key: None,
151        }
152    }
153}
154
155impl ser::SerializeMap for MapSerializer<'_> {
156    type Ok = JsValue;
157    type Error = Error;
158
159    fn serialize_key<T: ?Sized + Serialize>(&mut self, key: &T) -> Result<()> {
160        debug_assert!(self.next_key.is_none());
161        self.next_key = Some(key.serialize(self.serializer)?);
162        Ok(())
163    }
164
165    fn serialize_value<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<()> {
166        let key = self.next_key.take().unwrap_throw();
167        let value_ser = value.serialize(self.serializer)?;
168        match &self.target {
169            MapResult::Map(map) => {
170                map.set(&key, &value_ser);
171            }
172            MapResult::Object(object) => {
173                let key = key.dyn_into::<JsString>().map_err(|_| {
174                    Error::custom("Map key is not a string and cannot be an object key")
175                })?;
176                object.unchecked_ref::<ObjectExt>().set(key, value_ser);
177            }
178        }
179        Ok(())
180    }
181
182    fn end(self) -> Result {
183        debug_assert!(self.next_key.is_none());
184        match self.target {
185            MapResult::Map(map) => Ok(map.into()),
186            MapResult::Object(object) => Ok(object.into()),
187        }
188    }
189}
190
191/// Serializes Rust structs into plain JS objects.
192pub struct ObjectSerializer<'s> {
193    serializer: &'s Serializer,
194    target: ObjectExt,
195}
196
197impl<'s> ObjectSerializer<'s> {
198    pub fn new(serializer: &'s Serializer) -> Self {
199        Self {
200            serializer,
201            target: Object::new().unchecked_into::<ObjectExt>(),
202        }
203    }
204}
205
206impl ser::SerializeStruct for ObjectSerializer<'_> {
207    type Ok = JsValue;
208    type Error = Error;
209
210    fn serialize_field<T: ?Sized + Serialize>(
211        &mut self,
212        key: &'static str,
213        value: &T,
214    ) -> Result<()> {
215        let value = value.serialize(self.serializer)?;
216        self.target.set(static_str_to_js(key), value);
217        Ok(())
218    }
219
220    fn end(self) -> Result {
221        Ok(self.target.into())
222    }
223}
224
225/// A [`serde::Serializer`] that converts supported Rust values into a [`JsValue`].
226#[derive(Default)]
227pub struct Serializer {
228    serialize_missing_as_null: bool,
229    serialize_maps_as_objects: bool,
230    serialize_large_number_types_as_bigints: bool,
231    serialize_bytes_as_arrays: bool,
232}
233
234impl Serializer {
235    /// Creates a new default [`Serializer`].
236    pub const fn new() -> Self {
237        Self {
238            serialize_missing_as_null: false,
239            serialize_maps_as_objects: false,
240            serialize_large_number_types_as_bigints: false,
241            serialize_bytes_as_arrays: false,
242        }
243    }
244
245    /// Creates a JSON compatible serializer. This uses null instead of undefined, and
246    /// uses plain objects instead of ES maps. So you will get the same result of
247    /// `JsValue::from_serde`, and you can stringify results to JSON and store
248    /// it without data loss.
249    pub const fn json_compatible() -> Self {
250        Self {
251            serialize_missing_as_null: true,
252            serialize_maps_as_objects: true,
253            serialize_large_number_types_as_bigints: false,
254            serialize_bytes_as_arrays: true,
255        }
256    }
257
258    /// Set to `true` to serialize `()`, unit structs and `Option::None` to `null`
259    /// instead of `undefined` in JS. `false` by default.
260    pub const fn serialize_missing_as_null(mut self, value: bool) -> Self {
261        self.serialize_missing_as_null = value;
262        self
263    }
264
265    /// Set to `true` to serialize maps into plain JavaScript objects instead of
266    /// ES2015 `Map`s. `false` by default.
267    pub const fn serialize_maps_as_objects(mut self, value: bool) -> Self {
268        self.serialize_maps_as_objects = value;
269        self
270    }
271
272    /// Set to `true` to serialize 64-bit numbers to JavaScript `BigInt` instead of
273    /// plain numbers. `false` by default.
274    pub const fn serialize_large_number_types_as_bigints(mut self, value: bool) -> Self {
275        self.serialize_large_number_types_as_bigints = value;
276        self
277    }
278
279    /// Set to `true` to serialize bytes into plain JavaScript arrays instead of
280    /// ES2015 `Uint8Array`s. `false` by default.
281    pub const fn serialize_bytes_as_arrays(mut self, value: bool) -> Self {
282        self.serialize_bytes_as_arrays = value;
283        self
284    }
285}
286
287macro_rules! forward_to_into {
288    ($($name:ident($ty:ty);)*) => {
289        $(fn $name(self, v: $ty) -> Result {
290            Ok(v.into())
291        })*
292    };
293}
294
295impl<'s> ser::Serializer for &'s Serializer {
296    type Ok = JsValue;
297    type Error = Error;
298
299    type SerializeSeq = ArraySerializer<'s>;
300    type SerializeTuple = ArraySerializer<'s>;
301    type SerializeTupleStruct = ArraySerializer<'s>;
302    type SerializeTupleVariant = VariantSerializer<ArraySerializer<'s>>;
303    type SerializeMap = MapSerializer<'s>;
304    type SerializeStruct = ObjectSerializer<'s>;
305    type SerializeStructVariant = VariantSerializer<ObjectSerializer<'s>>;
306
307    forward_to_into! {
308        serialize_bool(bool);
309
310        serialize_i8(i8);
311        serialize_i16(i16);
312        serialize_i32(i32);
313
314        serialize_u8(u8);
315        serialize_u16(u16);
316        serialize_u32(u32);
317
318        serialize_f32(f32);
319        serialize_f64(f64);
320
321        serialize_str(&str);
322    }
323
324    /// Serializes `i64` into a `BigInt` or a JS number.
325    ///
326    /// If `serialize_large_number_types_as_bigints` is set to `false`,
327    /// `i64` is serialized as a JS number. But in this mode only numbers
328    /// within the safe integer range are supported.
329    fn serialize_i64(self, v: i64) -> Result {
330        if self.serialize_large_number_types_as_bigints {
331            return Ok(v.into());
332        }
333
334        // Note: don't try to "simplify" by using `.abs()` as it can overflow,
335        // but range check can't.
336        const MIN_SAFE_INTEGER: i64 = Number::MIN_SAFE_INTEGER as i64;
337        const MAX_SAFE_INTEGER: i64 = Number::MAX_SAFE_INTEGER as i64;
338
339        if (MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&v) {
340            self.serialize_f64(v as _)
341        } else {
342            Err(Error::custom(format_args!(
343                "{} can't be represented as a JavaScript number",
344                v
345            )))
346        }
347    }
348
349    /// Serializes `u64` into a `BigInt` or a JS number.
350    ///
351    /// If `serialize_large_number_types_as_bigints` is set to `false`,
352    /// `u64` is serialized as a JS number. But in this mode only numbers
353    /// within the safe integer range are supported.
354    fn serialize_u64(self, v: u64) -> Result {
355        if self.serialize_large_number_types_as_bigints {
356            return Ok(v.into());
357        }
358
359        if v <= Number::MAX_SAFE_INTEGER as u64 {
360            self.serialize_f64(v as _)
361        } else {
362            Err(Error::custom(format_args!(
363                "{} can't be represented as a JavaScript number",
364                v
365            )))
366        }
367    }
368
369    /// Serializes `i128` into a `BigInt`.
370    fn serialize_i128(self, v: i128) -> Result {
371        Ok(JsValue::from(v))
372    }
373
374    /// Serializes `u128` into a `BigInt`.
375    fn serialize_u128(self, v: u128) -> Result {
376        Ok(JsValue::from(v))
377    }
378
379    /// Serializes `char` into a JS string.
380    fn serialize_char(self, v: char) -> Result {
381        Ok(JsString::from(v).into())
382    }
383
384    /// Serializes `bytes` into a JS `Uint8Array` or a plain JS array.
385    ///
386    /// If `serialize_bytes_as_arrays` is set to `true`, bytes are serialized as plain JS arrays.
387    fn serialize_bytes(self, v: &[u8]) -> Result {
388        // Create a `Uint8Array` view into a Rust slice, and immediately copy it to the JS memory.
389        //
390        // This is necessary because any allocation in WebAssembly can require reallocation of the
391        // backing memory, which will invalidate existing views (including `Uint8Array`).
392        let view = unsafe { Uint8Array::view(v) };
393        if self.serialize_bytes_as_arrays {
394            Ok(JsValue::from(Array::from(view.as_ref())))
395        } else {
396            Ok(JsValue::from(Uint8Array::new(view.as_ref())))
397        }
398    }
399
400    /// Serializes `None` into `undefined` or `null`.
401    ///
402    /// If `serialize_missing_as_null` is set to `true`, `None` is serialized as `null`.
403    fn serialize_none(self) -> Result {
404        self.serialize_unit()
405    }
406
407    /// Serializes `Some(T)` as `T`.
408    fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result {
409        value.serialize(self)
410    }
411
412    /// Serializes `()` into `undefined` or `null`.
413    ///
414    /// If `serialize_missing_as_null` is set to `true`, `()` is serialized as `null`.
415    fn serialize_unit(self) -> Result {
416        Ok(if self.serialize_missing_as_null {
417            JsValue::NULL
418        } else {
419            JsValue::UNDEFINED
420        })
421    }
422
423    /// Serializes unit structs into `undefined` or `null`.
424    fn serialize_unit_struct(self, _name: &'static str) -> Result {
425        self.serialize_unit()
426    }
427
428    /// For compatibility with serde-json, serializes unit variants as "Variant" strings.
429    fn serialize_unit_variant(
430        self,
431        _name: &'static str,
432        _variant_index: u32,
433        variant: &'static str,
434    ) -> Result {
435        Ok(static_str_to_js(variant).into())
436    }
437
438    /// Serializes newtype structs as their inner values.
439    fn serialize_newtype_struct<T: ?Sized + Serialize>(
440        self,
441        name: &'static str,
442        value: &T,
443    ) -> Result {
444        if name == PRESERVED_VALUE_MAGIC {
445            let abi = value.serialize(self)?.unchecked_into_f64() as u32;
446            // `PreservedValueSerWrapper` gives us ABI of a reference to a `JsValue` that is
447            // guaranteed to be alive only during this call.
448            // We must clone it before giving away the value to the caller.
449            return Ok(unsafe { JsValue::ref_from_abi(abi) }.as_ref().clone());
450        }
451        value.serialize(self)
452    }
453
454    /// Serializes newtype variants as their inner values.
455    fn serialize_newtype_variant<T: ?Sized + Serialize>(
456        self,
457        _name: &'static str,
458        _variant_index: u32,
459        variant: &'static str,
460        value: &T,
461    ) -> Result {
462        VariantSerializer::new(variant, self.serialize_newtype_struct(variant, value)?).end(Ok)
463    }
464
465    /// Serializes any Rust iterable as a JS Array.
466    // TODO: Figure out if there is a way to detect and serialize `Set` differently.
467    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
468        Ok(ArraySerializer::new(self))
469    }
470
471    /// Serializes Rust tuples as JS arrays.
472    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
473        self.serialize_seq(Some(len))
474    }
475
476    /// Serializes Rust tuple structs as JS arrays.
477    fn serialize_tuple_struct(
478        self,
479        _name: &'static str,
480        len: usize,
481    ) -> Result<Self::SerializeTupleStruct> {
482        self.serialize_tuple(len)
483    }
484
485    /// Serializes Rust tuple variants as `{"Variant": [ ...tuple... ]}`.
486    fn serialize_tuple_variant(
487        self,
488        _name: &'static str,
489        _variant_index: u32,
490        variant: &'static str,
491        len: usize,
492    ) -> Result<Self::SerializeTupleVariant> {
493        Ok(VariantSerializer::new(
494            variant,
495            self.serialize_tuple_struct(variant, len)?,
496        ))
497    }
498
499    /// Serializes Rust maps into JS `Map` or plain JS objects.
500    ///
501    /// See [`MapSerializer`] for more details.
502    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
503        Ok(MapSerializer::new(self, self.serialize_maps_as_objects))
504    }
505
506    /// Serializes Rust typed structs into plain JS objects.
507    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
508        Ok(ObjectSerializer::new(self))
509    }
510
511    /// Serializes Rust struct-like variants into `{"Variant": { ...fields... }}`.
512    fn serialize_struct_variant(
513        self,
514        _name: &'static str,
515        _variant_index: u32,
516        variant: &'static str,
517        len: usize,
518    ) -> Result<Self::SerializeStructVariant> {
519        Ok(VariantSerializer::new(
520            variant,
521            self.serialize_struct(variant, len)?,
522        ))
523    }
524}