gloo_utils/format/
json.rs

1#![cfg(feature = "serde")]
2
3use wasm_bindgen::{JsValue, UnwrapThrowExt};
4mod private {
5    pub trait Sealed {}
6    impl Sealed for wasm_bindgen::JsValue {}
7}
8
9/// Extension trait to provide conversion between [`JsValue`](wasm_bindgen::JsValue) and [`serde`].
10///
11/// Usage of this API requires activating the `serde` feature of the `gloo-utils` crate.
12#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
13pub trait JsValueSerdeExt: private::Sealed {
14    /// Creates a new `JsValue` from the JSON serialization of the object `t`
15    /// provided.
16    ///
17    /// This function will serialize the provided value `t` to a JSON string,
18    /// send the JSON string to JS, parse it into a JS object, and then return
19    /// a handle to the JS object. This is unlikely to be super speedy so it's
20    /// not recommended for large payloads, but it's a nice to have in some
21    /// situations!
22    ///
23    /// Usage of this API requires activating the `serde` feature of
24    /// the `gloo-utils` crate.
25    /// # Example
26    ///
27    /// ```rust
28    /// use wasm_bindgen::JsValue;
29    /// use gloo_utils::format::JsValueSerdeExt;
30    ///
31    /// # fn no_run() {
32    /// let array = vec![1,2,3];
33    /// let obj = JsValue::from_serde(&array);
34    /// # }
35    /// ```
36    /// # Errors
37    ///
38    /// Returns any error encountered when serializing `T` into JSON.
39    ///
40    /// # Panics
41    ///
42    /// Panics if [`serde_json`](serde_json::to_string) generated JSON that couldn't be parsed by [`js_sys`].
43    /// Uses [`unwrap_throw`](UnwrapThrowExt::unwrap_throw) from [`wasm_bindgen::UnwrapThrowExt`].
44    #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
45    fn from_serde<T>(t: &T) -> serde_json::Result<JsValue>
46    where
47        T: serde::ser::Serialize + ?Sized;
48
49    /// Invokes `JSON.stringify` on this value and then parses the resulting
50    /// JSON into an arbitrary Rust value.
51    ///
52    /// This function will first call `JSON.stringify` on the `JsValue` itself.
53    /// The resulting string is then passed into Rust which then parses it as
54    /// JSON into the resulting value. If given `undefined`, object will be silently changed to
55    /// null to avoid panic.
56    ///
57    /// Usage of this API requires activating the `serde` feature of
58    /// the `gloo-utils` crate.
59    ///
60    /// # Example
61    ///
62    /// ```rust
63    /// use wasm_bindgen::JsValue;
64    /// use gloo_utils::format::JsValueSerdeExt;
65    ///
66    /// # fn no_run() {
67    /// assert_eq!(JsValue::from("bar").into_serde::<String>().unwrap(), "bar");
68    /// # }
69    /// ```
70    ///
71    /// # Errors
72    ///
73    /// Returns any error encountered when parsing the JSON into a `T`.
74    ///
75    /// # Panics
76    ///
77    /// Panics if [`js_sys`] couldn't stringify the JsValue. Uses [`unwrap_throw`](UnwrapThrowExt::unwrap_throw)
78    /// from [`wasm_bindgen::UnwrapThrowExt`].
79    #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
80    #[allow(clippy::wrong_self_convention)]
81    fn into_serde<T>(&self) -> serde_json::Result<T>
82    where
83        T: for<'a> serde::de::Deserialize<'a>;
84}
85
86impl JsValueSerdeExt for JsValue {
87    fn from_serde<T>(t: &T) -> serde_json::Result<JsValue>
88    where
89        T: serde::ser::Serialize + ?Sized,
90    {
91        let s = serde_json::to_string(t)?;
92        Ok(js_sys::JSON::parse(&s).unwrap_throw())
93    }
94
95    fn into_serde<T>(&self) -> serde_json::Result<T>
96    where
97        T: for<'a> serde::de::Deserialize<'a>,
98    {
99        // Turns out `JSON.stringify(undefined) === undefined`, so if
100        // we're passed `undefined` reinterpret it as `null` for JSON
101        // purposes.
102        let s = if self.is_undefined() {
103            String::from("null")
104        } else {
105            js_sys::JSON::stringify(self)
106                .map(String::from)
107                .unwrap_throw()
108        };
109        serde_json::from_str(&s)
110    }
111}