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}