serde_wasm_bindgen/
ser.rs

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