#[cfg(target_arch = "wasm32")]
mod bindings;
use std::{
cell::UnsafeCell,
fmt,
ops::{self, Deref},
};
use wasm_bindgen::prelude::*;
pub struct Readable<T> {
value: UnsafeCell<T>,
#[cfg(target_arch = "wasm32")]
writable_store: bindings::Writable,
#[cfg(target_arch = "wasm32")]
derived_store: bindings::Readable,
#[allow(clippy::type_complexity)]
#[cfg(target_arch = "wasm32")]
mapped_set_fn: Box<dyn FnMut(&T) -> JsValue>,
#[cfg(target_arch = "wasm32")]
_derived_store_map_fn: Closure<dyn FnMut(JsValue) -> JsValue>,
}
impl<T> fmt::Debug for Readable<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Readable").field(self.deref()).finish()
}
}
impl<T> fmt::Display for Readable<T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.deref().fmt(f)
}
}
impl<T> Default for Readable<T>
where
T: Default + Clone + Into<JsValue> + 'static,
{
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> ops::Deref for Readable<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.value.get() }
}
}
impl<T: 'static> Readable<T> {
#[allow(unused_variables)]
fn init_store<F>(initial_value: UnsafeCell<T>, mapping_fn: F) -> Self
where
F: FnMut(&T) -> JsValue + 'static,
{
#[cfg(target_arch = "wasm32")]
let this = {
let mut mapped_set_fn =
Box::new(mapping_fn) as Box<dyn FnMut(&T) -> JsValue>;
let writable_store = bindings::writable(mapped_set_fn(unsafe {
&*initial_value.get()
}));
let derived_store_map_fn = Closure::new(|v| v);
let derived_store =
bindings::derived(&writable_store, &derived_store_map_fn);
Self {
value: initial_value,
writable_store,
derived_store,
mapped_set_fn,
_derived_store_map_fn: derived_store_map_fn,
}
};
#[cfg(not(target_arch = "wasm32"))]
let this = {
Self {
value: initial_value,
}
};
this
}
pub fn new(initial_value: T) -> Self
where
T: Clone + Into<JsValue>,
{
Self::init_store(UnsafeCell::new(initial_value), |v| v.clone().into())
}
pub fn new_mapped<F>(initial_value: T, mapping_fn: F) -> Self
where
F: FnMut(&T) -> JsValue + 'static,
{
Self::init_store(UnsafeCell::new(initial_value), mapping_fn)
}
pub fn set(&mut self, new_value: T) {
let value = unsafe { &mut *self.value.get() };
*value = new_value;
#[cfg(target_arch = "wasm32")]
self.writable_store.set((self.mapped_set_fn)(value));
}
pub fn set_with<F, O>(&mut self, f: F) -> O
where
F: FnOnce(&mut T) -> O,
{
let value = unsafe { &mut *self.value.get() };
#[allow(clippy::let_and_return)]
let o = f(value);
#[cfg(target_arch = "wasm32")]
{
self.writable_store.set((self.mapped_set_fn)(value));
}
o
}
pub fn get_store(&self) -> JsValue {
#[cfg(not(target_arch = "wasm32"))]
panic!(
"`Readable::get_store()` can only be called \
within `wasm32` targets"
);
#[cfg(target_arch = "wasm32")]
return self.derived_store.clone();
}
}