serde_wasm_bindgen/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3#![warn(clippy::missing_const_for_fn)]
4
5use js_sys::JsString;
6use wasm_bindgen::prelude::*;
7
8mod de;
9mod error;
10mod ser;
11
12pub use de::Deserializer;
13pub use error::Error;
14pub use ser::Serializer;
15
16type Result<T> = std::result::Result<T, Error>;
17
18fn static_str_to_js(s: &'static str) -> JsString {
19    use std::cell::RefCell;
20    use std::collections::HashMap;
21
22    #[derive(Default)]
23    struct PtrHasher {
24        addr: usize,
25    }
26
27    impl std::hash::Hasher for PtrHasher {
28        fn write(&mut self, _bytes: &[u8]) {
29            unreachable!();
30        }
31
32        fn write_usize(&mut self, addr_or_len: usize) {
33            if self.addr == 0 {
34                self.addr = addr_or_len;
35            }
36        }
37
38        fn finish(&self) -> u64 {
39            self.addr as _
40        }
41    }
42
43    type PtrBuildHasher = std::hash::BuildHasherDefault<PtrHasher>;
44
45    thread_local! {
46        // Since we're mainly optimising for converting the exact same string literal over and over again,
47        // which will always have the same pointer, we can speed things up by indexing by the string's pointer
48        // instead of its value.
49        static CACHE: RefCell<HashMap<*const str, JsString, PtrBuildHasher>> = Default::default();
50    }
51    CACHE.with(|cache| {
52        cache
53            .borrow_mut()
54            .entry(s)
55            .or_insert_with(|| s.into())
56            .clone()
57    })
58}
59
60/// Custom bindings to avoid using fallible `Reflect` for plain objects.
61#[wasm_bindgen]
62extern "C" {
63    type ObjectExt;
64
65    #[wasm_bindgen(method, indexing_getter)]
66    fn get_with_ref_key(this: &ObjectExt, key: &JsString) -> JsValue;
67
68    #[wasm_bindgen(method, indexing_setter)]
69    fn set(this: &ObjectExt, key: JsString, value: JsValue);
70}
71
72/// Converts [`JsValue`] into a Rust type.
73pub fn from_value<T: serde::de::DeserializeOwned>(value: JsValue) -> Result<T> {
74    T::deserialize(Deserializer::from(value))
75}
76
77/// Converts a Rust value into a [`JsValue`].
78pub fn to_value<T: serde::ser::Serialize + ?Sized>(value: &T) -> Result<JsValue> {
79    value.serialize(&Serializer::new())
80}