wasm_bridge_js/conversions/
into_closure.rs

1use std::rc::Rc;
2
3use wasm_bindgen::{
4    convert::{FromWasmAbi, ReturnWasmAbi},
5    prelude::Closure,
6    JsValue,
7};
8
9use crate::*;
10
11pub(crate) type MakeClosure<T> = Box<dyn Fn(DataHandle<T>) -> (JsValue, DropHandle)>;
12
13pub trait IntoMakeClosure<T, Params, Results> {
14    fn into_make_closure(self) -> MakeClosure<T>;
15}
16
17impl<T, R, F> IntoMakeClosure<T, (), R> for F
18where
19    T: 'static,
20    F: Fn(Caller<T>) -> R + 'static,
21    R: ToJsValue + 'static,
22    Result<R::ReturnAbi, JsValue>: ReturnWasmAbi, // TODO: unnecessary return bound?
23{
24    fn into_make_closure(self) -> MakeClosure<T> {
25        let self_rc = Rc::new(self);
26
27        let make_closure = move |handle: DataHandle<T>| {
28            let caller = Caller::new(handle);
29            let self_clone = self_rc.clone();
30
31            let closure = Closure::<dyn Fn() -> Result<R::ReturnAbi, JsValue>>::new(move || {
32                self_clone(caller.clone()).into_return_abi()
33            });
34
35            DropHandle::from_closure(closure)
36        };
37
38        Box::new(make_closure)
39    }
40}
41
42macro_rules! into_make_closure_single {
43    ($ty:ty) => {
44        impl<T, R, F> IntoMakeClosure<T, $ty, R> for F
45        where
46            T: 'static,
47            F: Fn(Caller<T>, $ty) -> R + 'static,
48            R: ToJsValue + 'static,
49            Result<R::ReturnAbi, JsValue>: ReturnWasmAbi, // TODO: unnecessary return bound?
50        {
51            fn into_make_closure(self) -> MakeClosure<T> {
52                let self_rc = Rc::new(self);
53
54                let make_closure = move |handle: DataHandle<T>| {
55                    let caller = Caller::new(handle);
56                    let self_clone = self_rc.clone();
57
58                    let closure = Closure::<dyn Fn($ty) -> Result<R::ReturnAbi, JsValue>>::new(
59                        move |arg: $ty| self_clone(caller.clone(), arg).into_return_abi(),
60                    );
61
62                    DropHandle::from_closure(closure)
63                };
64
65                Box::new(make_closure)
66            }
67        }
68    };
69}
70
71into_make_closure_single!(i32);
72into_make_closure_single!(i64);
73into_make_closure_single!(u32);
74into_make_closure_single!(u64);
75into_make_closure_single!(f32);
76into_make_closure_single!(f64);
77
78impl<T, P0, P1, R, F> IntoMakeClosure<T, (P0, P1), R> for F
79where
80    T: 'static,
81    F: Fn(Caller<T>, P0, P1) -> R + 'static,
82    P0: FromWasmAbi + 'static,
83    P1: FromWasmAbi + 'static,
84    R: ToJsValue + 'static,
85    Result<R::ReturnAbi, JsValue>: ReturnWasmAbi, // TODO: unnecessary return bound?
86{
87    fn into_make_closure(self) -> MakeClosure<T> {
88        let self_rc = Rc::new(self);
89
90        let make_closure = move |handle: DataHandle<T>| {
91            let caller = Caller::new(handle);
92            let self_clone = self_rc.clone();
93
94            let closure = Closure::<dyn Fn(P0, P1) -> Result<R::ReturnAbi, JsValue>>::new(
95                move |p0: P0, p1: P1| self_clone(caller.clone(), p0, p1).into_return_abi(),
96            );
97
98            DropHandle::from_closure(closure)
99        };
100
101        Box::new(make_closure)
102    }
103}
104
105macro_rules! into_make_closure_many {
106    ($(($param: ident, $name: ident)),*) => {
107        impl<T, $($name, )* R, F> IntoMakeClosure<T, ($($name),*), R> for F
108        where
109            T: 'static,
110            F: Fn(Caller<T>, $($name),*) -> R + 'static,
111            $($name: FromWasmAbi + 'static,)*
112            R: ToJsValue + 'static,
113            Result<R::ReturnAbi, JsValue>: ReturnWasmAbi, // TODO: unnecessary return bound?
114        {
115            fn into_make_closure(self) -> MakeClosure<T> {
116                let self_rc = Rc::new(self);
117
118                let make_closure = move |handle: DataHandle<T>| {
119                    let caller = Caller::new(handle);
120                    let self_clone = self_rc.clone();
121
122                    let closure =
123                        Closure::<dyn Fn($($name),*) -> Result<R::ReturnAbi, JsValue>>::new(move |$($param: $name),*| {
124                            self_clone(caller.clone(), $($param),*).into_return_abi()
125                        });
126
127                    DropHandle::from_closure(closure)
128                };
129
130                Box::new(make_closure)
131            }
132        }
133    };
134}
135
136// #[rustfmt::skip]
137// into_make_closure_many!((p0, P0), (p1, P1));
138#[rustfmt::skip]
139into_make_closure_many!((p0, P0), (p1, P1), (p2, P2));
140#[rustfmt::skip]
141into_make_closure_many!((p0, P0), (p1, P1), (p2, P2), (p3, P3));
142#[rustfmt::skip]
143into_make_closure_many!((p0, P0), (p1, P1), (p2, P2), (p3, P3), (p4, P4));
144#[rustfmt::skip]
145into_make_closure_many!((p0, P0), (p1, P1), (p2, P2), (p3, P3), (p4, P4), (p5, P5));
146#[rustfmt::skip]
147into_make_closure_many!((p0, P0), (p1, P1), (p2, P2), (p3, P3), (p4, P4), (p5, P5), (p6, P6));
148#[rustfmt::skip]
149into_make_closure_many!((p0, P0), (p1, P1), (p2, P2), (p3, P3), (p4, P4), (p5, P5), (p6, P6), (p7, P7));
150
151// js-sys doesn't support closures with more than 8 arguments
152// TODO: a workaround can exist though
153
154// #[rustfmt::skip]
155// into_make_closure_many!((p0, P0), (p1, P1), (p2, P2), (p3, P3), (p4, P4), (p5, P5), (p6, P6), (p7, P7), (p8, P8));
156// #[rustfmt::skip]
157// into_make_closure_many!((p0, P0), (p1, P1), (p2, P2), (p3, P3), (p4, P4), (p5, P5), (p6, P6), (p7, P7), (p8, P8), (p9, P9));
158// #[rustfmt::skip]
159// into_make_closure_many!((p0, P0), (p1, P1), (p2, P2), (p3, P3), (p4, P4), (p5, P5), (p6, P6), (p7, P7), (p8, P8), (p9, P9), (p10, P10));
160// #[rustfmt::skip]
161// into_make_closure_many!((p0, P0), (p1, P1), (p2, P2), (p3, P3), (p4, P4), (p5, P5), (p6, P6), (p7, P7), (p8, P8), (p9, P9), (p10, P10), (p11, P11));