use std::{
cell::{Ref, RefCell},
fmt::Debug,
rc::Rc,
};
use wasm_bindgen::{
convert::{FromWasmAbi, IntoWasmAbi},
describe::WasmDescribe,
prelude::Closure,
JsValue, UnwrapThrowExt,
};
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub struct Void;
impl WasmDescribe for Void {
fn describe() {
JsValue::describe()
}
}
impl FromWasmAbi for Void {
type Abi = <JsValue as FromWasmAbi>::Abi;
unsafe fn from_abi(js: Self::Abi) -> Self {
JsValue::from_abi(js);
Void
}
}
impl From<Void> for JsValue {
fn from(_: Void) -> Self {
JsValue::undefined()
}
}
pub struct Callback<T, U = ()> {
closure: Rc<RefCell<dyn FnMut(T) -> U>>,
js: Rc<RefCell<Option<Closure<dyn FnMut(T) -> U>>>>,
}
impl<T, U> Callback<T, U>
where
T: 'static,
U: 'static,
{
pub fn new(f: impl FnMut(T) -> U + 'static) -> Self {
Self {
closure: Rc::new(RefCell::new(f)),
js: Default::default(),
}
}
pub fn to_closure(&self) -> impl FnMut(T) -> U + 'static {
let closure = self.closure.clone();
move |arg| {
let mut f = closure.borrow_mut();
f(arg)
}
}
pub fn call(&self, arg: T) -> U {
let mut f = self.closure.borrow_mut();
f(arg)
}
pub fn premap<V>(
&self,
mut f: impl FnMut(V) -> T + 'static,
) -> Callback<V, U> {
Callback {
closure: Rc::new(RefCell::new({
let closure = self.closure.clone();
move |v| {
let t = f(v);
let mut g = closure.borrow_mut();
g(t)
}
})),
js: Default::default(),
}
}
pub fn postmap<V>(
&self,
mut f: impl FnMut(U) -> V + 'static,
) -> Callback<T, V> {
Callback {
closure: Rc::new(RefCell::new({
let closure = self.closure.clone();
move |t| {
let mut g = closure.borrow_mut();
let u = g(t);
f(u)
}
})),
js: Default::default(),
}
}
pub fn as_js(&self) -> Ref<'_, JsValue>
where
T: FromWasmAbi,
U: IntoWasmAbi,
{
{
let mut borrow = self.js.borrow_mut();
if borrow.is_none() {
*borrow = Some(Closure::new({
let closure = self.closure.clone();
move |arg| {
let mut f = closure.borrow_mut();
f(arg)
}
}));
}
}
Ref::map(self.js.borrow(), |x| {
x.as_ref().expect_throw("no closure available").as_ref()
})
}
}
impl<T: 'static> Callback<T> {
pub fn noop() -> Self {
Callback::new(|_| ())
}
}
impl<T, U> Default for Callback<T, U>
where
T: 'static,
U: Default + 'static,
{
fn default() -> Self {
Self::new(|_| U::default())
}
}
impl<F, T, U> From<F> for Callback<T, U>
where
F: FnMut(T) -> U + 'static,
T: 'static,
U: 'static,
{
fn from(value: F) -> Self {
Self::new(value)
}
}
impl<T, U> Debug for Callback<T, U> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Callback(|_| { … })")
}
}
impl<T, U> PartialEq for Callback<T, U> {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.closure, &other.closure) && Rc::ptr_eq(&self.js, &other.js)
}
}
impl<T, U> Eq for Callback<T, U> {}
impl<T, U> Clone for Callback<T, U> {
fn clone(&self) -> Self {
Self {
closure: self.closure.clone(),
js: self.js.clone(),
}
}
}