use alloc::fmt;
use core::{cell, hash, result};
use hashbrown::HashMap;
use js_sys::JsString;
use wasm_bindgen::prelude::*;
mod de;
mod error;
mod ser;
pub use de::Deserializer;
pub use error::Error;
pub use ser::Serializer;
pub type Result<T> = core::result::Result<T, Error>;
fn static_str_to_js(s: &'static str) -> JsString {
use cell::RefCell;
#[derive(Default)]
struct PtrHasher {
addr: usize,
}
impl hash::Hasher for PtrHasher {
fn write(&mut self, _bytes: &[u8]) {
unreachable!();
}
fn write_usize(&mut self, addr_or_len: usize) {
if self.addr == 0 {
self.addr = addr_or_len;
}
}
fn finish(&self) -> u64 {
self.addr as _
}
}
type PtrBuildHasher = hash::BuildHasherDefault<PtrHasher>;
thread_local! {
static CACHE: RefCell<HashMap<*const str, JsString, PtrBuildHasher>> = Default::default();
}
CACHE.with(|cache| {
cache
.borrow_mut()
.entry(s)
.or_insert_with(|| s.into())
.clone()
})
}
#[wasm_bindgen]
extern "C" {
type ObjectExt;
#[wasm_bindgen(method, indexing_getter)]
fn get_with_ref_key(this: &ObjectExt, key: &JsString) -> JsValue;
#[wasm_bindgen(method, indexing_setter)]
fn set(this: &ObjectExt, key: JsString, value: JsValue);
}
pub fn from_value<T: serde::de::DeserializeOwned>(value: JsValue) -> Result<T> {
T::deserialize(Deserializer::from(value))
}
pub fn to_value<T: serde::ser::Serialize + ?Sized>(value: &T) -> Result<JsValue> {
value.serialize(&Serializer::new())
}
pub mod preserve {
use core::{any, fmt};
use serde::{de::Error, Deserialize, Serialize};
use wasm_bindgen::{
convert::{FromWasmAbi, IntoWasmAbi},
JsCast, JsValue,
};
pub(crate) const PRESERVED_VALUE_MAGIC: &str = "1fc430ca-5b7f-4295-92de-33cf2b145d38";
struct Magic;
impl<'de> serde::de::Deserialize<'de> for Magic {
fn deserialize<D: serde::de::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = Magic;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("serde-wasm-bindgen's magic string")
}
fn visit_str<E: Error>(self, s: &str) -> Result<Self::Value, E> {
if s == PRESERVED_VALUE_MAGIC {
Ok(Magic)
} else {
Err(E::invalid_value(serde::de::Unexpected::Str(s), &self))
}
}
}
de.deserialize_str(Visitor)
}
}
#[derive(Serialize)]
#[serde(rename = "1fc430ca-5b7f-4295-92de-33cf2b145d38")]
struct PreservedValueSerWrapper(u32);
#[derive(Deserialize)]
#[serde(rename = "1fc430ca-5b7f-4295-92de-33cf2b145d38")]
struct PreservedValueDeWrapper(Magic, u32);
pub fn serialize<S: serde::Serializer, T: JsCast>(val: &T, ser: S) -> Result<S::Ok, S::Error> {
PreservedValueSerWrapper(val.as_ref().into_abi()).serialize(ser)
}
pub fn deserialize<'de, D: serde::Deserializer<'de>, T: JsCast>(de: D) -> Result<T, D::Error> {
let wrap = PreservedValueDeWrapper::deserialize(de)?;
let val: JsValue = unsafe { FromWasmAbi::from_abi(wrap.1) };
val.dyn_into().map_err(|e| {
D::Error::custom(format_args!(
"incompatible JS value {e:?} for type {}",
any::type_name::<T>()
))
})
}
}