1use core::cell::RefCell;
2use js_sys::Function;
3use std::fmt::Debug;
4use std::future::Future;
5use std::rc::Rc;
6use std::task::Poll;
7use std::task::Waker;
8use wasm_bindgen::prelude::Closure;
9use wasm_bindgen::JsValue;
10
11#[derive(Debug)]
13pub struct CallbackPair<A, B>
14where
15 A: 'static + ?Sized,
16 B: 'static + ?Sized,
17{
18 inner: Rc<RefCell<CallbackPairInner<A, B>>>,
19}
20
21impl<A, B> CallbackPair<A, B>
22where
23 A: 'static + ?Sized,
24 B: 'static + ?Sized,
25{
26 pub fn new<X, Y>(x: X, y: Y) -> CallbackPair<A, B>
27 where
28 Self: From<(X, Y)>,
29 {
30 Self::from((x, y))
31 }
32
33 pub fn as_functions(&self) -> (Function, Function) {
34 let left: JsValue = self
35 .inner
36 .borrow()
37 .cb
38 .as_ref()
39 .unwrap()
40 .as_ref()
41 .0
42 .as_ref()
43 .into();
44 let right: JsValue = self
45 .inner
46 .borrow()
47 .cb
48 .as_ref()
49 .unwrap()
50 .as_ref()
51 .1
52 .as_ref()
53 .into();
54 (left.into(), right.into())
55 }
56
57 pub fn as_closures(&self) -> Rc<(Closure<A>, Closure<B>)> {
58 Rc::clone(self.inner.borrow().cb.as_ref().unwrap())
59 }
60}
61
62impl Default for CallbackPair<dyn FnMut(JsValue), dyn FnMut(JsValue)> {
65 fn default() -> Self {
66 Self::from((|data| Ok(data), |err| Err(err)))
67 }
68}
69
70impl<A, B> Future for CallbackPair<A, B>
72where
73 A: 'static + ?Sized,
74 B: 'static + ?Sized,
75{
76 type Output = Result<JsValue, JsValue>;
77
78 fn poll(
79 self: std::pin::Pin<&mut Self>,
80 cx: &mut std::task::Context<'_>,
81 ) -> std::task::Poll<Self::Output> {
82 let mut inner = self.inner.borrow_mut();
83 if let Some(val) = inner.result.take() {
84 return Poll::Ready(val);
85 }
86 inner.task = Some(cx.waker().clone());
87 Poll::Pending
88 }
89}
90
91macro_rules! from_impl {
93 (($($a:ty),*), ($($b:ty),*), ($($alist:ident),*), ($($blist:ident),*)) => {
99 impl<A, B> From<(A, B)> for CallbackPair<dyn FnMut($($a,)*), dyn FnMut($($b,)*)>
100 where
101 A: 'static + FnOnce($($a,)*) -> Result<JsValue, JsValue>,
102 B: 'static + FnOnce($($b,)*) -> Result<JsValue, JsValue>,
103 {
104 fn from(cb: (A, B)) -> Self {
105 let inner = CallbackPairInner::new();
106 let state = Rc::clone(&inner);
107 let cb0 = cb.0;
108 let left = Closure::once(move |$($alist),*| CallbackPairInner::finish(&state, cb0($($alist),*)));
109 let state = Rc::clone(&inner);
110 let cb1 = cb.1;
111 let right = Closure::once(move |$($blist),*| CallbackPairInner::finish(&state, cb1($($blist),*)));
112 let ptr = Rc::new((left, right));
113 inner.borrow_mut().cb = Some(ptr);
114 CallbackPair { inner }
115 }
116 }
117 };
118 (($($a:ident,)*), ($($b:ident,)*)) => {
120 from_impl!(($(from_impl!(@rep $a JsValue)),*), ($(from_impl!(@rep $b JsValue)),*), ($($a),*), ($($b),*));
121 };
122 (@left ($($a:ident,)*); $head:ident $($tail:tt)*) => {
124 from_impl!(($($a,)*), ($head, $($tail,)*));
125 from_impl!(@left ($($a,)*); $($tail)*);
126 };
127 (@right ($($b:ident,)*); $head:ident $($tail:tt)*) => {
129 from_impl!(($head, $($tail,)*), ($($b,)*));
130 from_impl!(@right ($($b,)*); $($tail)*);
131 };
132 ($head:ident $($tail:tt)*) => {
134 from_impl!(($head, $($tail,)*), ($head, $($tail,)*));
136 from_impl!(@left ($head, $($tail,)*); $($tail)*);
138 from_impl!(($head, $($tail,)*), ());
140 from_impl!(@right ($head, $($tail,)*); $($tail)*);
142 from_impl!((), ($head, $($tail,)*));
144 from_impl!($($tail)*);
146 };
147 (@rep $_t:tt $sub:ty) => {
149 $sub
150 };
151 () => {
153 from_impl!((), ());
154 };
155 (@left ($($a:ident,)*); ) => {};
156 (@right ($($b:ident,)*); ) => {};
157}
158
159from_impl!(a0 a1 a2 a3 a4 a5 a6); #[derive(Debug)]
162pub struct CallbackPairInner<A, B>
163where
164 A: 'static + ?Sized,
165 B: 'static + ?Sized,
166{
167 cb: Option<Rc<(Closure<A>, Closure<B>)>>,
168 result: Option<Result<JsValue, JsValue>>,
169 task: Option<Waker>,
170}
171
172impl<A, B> CallbackPairInner<A, B>
173where
174 A: 'static + ?Sized,
175 B: 'static + ?Sized,
176{
177 pub fn new() -> Rc<RefCell<CallbackPairInner<A, B>>> {
178 Rc::new(RefCell::new(CallbackPairInner {
179 cb: None,
180 task: None,
181 result: None,
182 }))
183 }
184
185 pub fn finish(state: &RefCell<CallbackPairInner<A, B>>, val: Result<JsValue, JsValue>) {
186 let task = {
187 let mut state = state.borrow_mut();
188 debug_assert!(state.result.is_none());
189 debug_assert!(state.cb.is_some());
190 drop(state.cb.take());
191 state.result = Some(val);
192 state.task.take()
193 };
194 if let Some(task) = task {
195 task.wake()
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use crate::CallbackPair;
203 use std::rc::Rc;
204 use wasm_bindgen::JsCast;
205 use wasm_bindgen_test::*;
206 use web_sys::{window, IdbOpenDbRequest};
207
208 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
209
210 #[wasm_bindgen_test]
213 #[rustfmt::skip]
214 fn should_compile_with_any_args() {
215 let _r = CallbackPair::new(|| Ok("".into()), || Err("".into()));
216 let _r = CallbackPair::new(|_a| Ok("".into()), || Err("".into()));
217 let _r = CallbackPair::new(|_a, _b| Ok("".into()), || Err("".into()));
218 let _r = CallbackPair::new(|_a, _b, _c| Ok("".into()), || Err("".into()));
219 let _r = CallbackPair::new(|_a, _b, _c, _d| Ok("".into()), || Err("".into()));
220 let _r = CallbackPair::new(|_a, _b, _c, _d, _e| Ok("".into()), || Err("".into()));
221 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f| Ok("".into()), || Err("".into()));
222 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f, _g| Ok("".into()), || Err("".into()));
223 let _r = CallbackPair::new(|| Ok("".into()), |_a| Err("".into()));
224 let _r = CallbackPair::new(|_a| Ok("".into()), |_a| Err("".into()));
225 let _r = CallbackPair::new(|_a, _b| Ok("".into()), |_a| Err("".into()));
226 let _r = CallbackPair::new(|_a, _b, _c| Ok("".into()), |_a| Err("".into()));
227 let _r = CallbackPair::new(|_a, _b, _c, _d| Ok("".into()), |_a| Err("".into()));
228 let _r = CallbackPair::new(|_a, _b, _c, _d, _e| Ok("".into()), |_a| Err("".into()));
229 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f| Ok("".into()), |_a| Err("".into()));
230 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f, _g| Ok("".into()), |_a| Err("".into()));
231 let _r = CallbackPair::new(|| Ok("".into()), |_a, _b| Err("".into()));
232 let _r = CallbackPair::new(|_a| Ok("".into()), |_a, _b| Err("".into()));
233 let _r = CallbackPair::new(|_a, _b| Ok("".into()), |_a, _b| Err("".into()));
234 let _r = CallbackPair::new(|_a, _b, _c| Ok("".into()), |_a, _b| Err("".into()));
235 let _r = CallbackPair::new(|_a, _b, _c, _d| Ok("".into()), |_a, _b| Err("".into()));
236 let _r = CallbackPair::new(|_a, _b, _c, _d, _e| Ok("".into()), |_a, _b| Err("".into()));
237 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f| Ok("".into()), |_a, _b| Err("".into()));
238 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f, _g| Ok("".into()), |_a, _b| Err("".into()));
239 let _r = CallbackPair::new(|| Ok("".into()), |_a, _b, _c| Err("".into()));
240 let _r = CallbackPair::new(|_a| Ok("".into()), |_a, _b, _c| Err("".into()));
241 let _r = CallbackPair::new(|_a, _b| Ok("".into()), |_a, _b, _c| Err("".into()));
242 let _r = CallbackPair::new(|_a, _b, _c| Ok("".into()), |_a, _b, _c| Err("".into()));
243 let _r = CallbackPair::new(|_a, _b, _c, _d| Ok("".into()), |_a, _b, _c| Err("".into()));
244 let _r = CallbackPair::new(|_a, _b, _c, _d, _e| Ok("".into()), |_a, _b, _c| Err("".into()));
245 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f| Ok("".into()), |_a, _b, _c| Err("".into()));
246 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f, _g| Ok("".into()), |_a, _b, _c| Err("".into()));
247 let _r = CallbackPair::new(|| Ok("".into()), |_a, _b, _c, _d| Err("".into()));
248 let _r = CallbackPair::new(|_a| Ok("".into()), |_a, _b, _c, _d| Err("".into()));
249 let _r = CallbackPair::new(|_a, _b| Ok("".into()), |_a, _b, _c, _d| Err("".into()));
250 let _r = CallbackPair::new(|_a, _b, _c| Ok("".into()), |_a, _b, _c, _d| Err("".into()));
251 let _r = CallbackPair::new(|_a, _b, _c, _d| Ok("".into()), |_a, _b, _c, _d| Err("".into()));
252 let _r = CallbackPair::new(|_a, _b, _c, _d, _e| Ok("".into()), |_a, _b, _c, _d| Err("".into()));
253 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f| Ok("".into()), |_a, _b, _c, _d| Err("".into()));
254 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f, _g| Ok("".into()), |_a, _b, _c, _d| Err("".into()));
255 let _r = CallbackPair::new(|| Ok("".into()), |_a, _b, _c, _d, _e| Err("".into()));
256 let _r = CallbackPair::new(|_a| Ok("".into()), |_a, _b, _c, _d, _e| Err("".into()));
257 let _r = CallbackPair::new(|_a, _b| Ok("".into()), |_a, _b, _c, _d, _e| Err("".into()));
258 let _r = CallbackPair::new(|_a, _b, _c| Ok("".into()), |_a, _b, _c, _d, _e| Err("".into()));
259 let _r = CallbackPair::new(|_a, _b, _c, _d| Ok("".into()), |_a, _b, _c, _d, _e| Err("".into()));
260 let _r = CallbackPair::new(|_a, _b, _c, _d, _e| Ok("".into()), |_a, _b, _c, _d, _e| Err("".into()));
261 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f| Ok("".into()), |_a, _b, _c, _d, _e| Err("".into()));
262 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f, _g| Ok("".into()), |_a, _b, _c, _d, _e| Err("".into()));
263 let _r = CallbackPair::new(|| Ok("".into()), |_a, _b, _c, _d, _e, _f| Err("".into()));
264 let _r = CallbackPair::new(|_a| Ok("".into()), |_a, _b, _c, _d, _e, _f| Err("".into()));
265 let _r = CallbackPair::new(|_a, _b| Ok("".into()), |_a, _b, _c, _d, _e, _f| Err("".into()));
266 let _r = CallbackPair::new(|_a, _b, _c| Ok("".into()), |_a, _b, _c, _d, _e, _f| Err("".into()));
267 let _r = CallbackPair::new(|_a, _b, _c, _d| Ok("".into()), |_a, _b, _c, _d, _e, _f| Err("".into()));
268 let _r = CallbackPair::new(|_a, _b, _c, _d, _e| Ok("".into()), |_a, _b, _c, _d, _e, _f| Err("".into()));
269 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f| Ok("".into()), |_a, _b, _c, _d, _e, _f| Err("".into()));
270 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f, _g| Ok("".into()), |_a, _b, _c, _d, _e, _f| Err("".into()));
271 let _r = CallbackPair::new(|| Ok("".into()), |_a, _b, _c, _d, _e, _f, _g| Err("".into()));
272 let _r = CallbackPair::new(|_a| Ok("".into()), |_a, _b, _c, _d, _e, _f, _g| Err("".into()));
273 let _r = CallbackPair::new(|_a, _b| Ok("".into()), |_a, _b, _c, _d, _e, _f, _g| Err("".into()));
274 let _r = CallbackPair::new(|_a, _b, _c| Ok("".into()), |_a, _b, _c, _d, _e, _f, _g| Err("".into()));
275 let _r = CallbackPair::new(|_a, _b, _c, _d| Ok("".into()), |_a, _b, _c, _d, _e, _f, _g| Err("".into()));
276 let _r = CallbackPair::new(|_a, _b, _c, _d, _e| Ok("".into()), |_a, _b, _c, _d, _e, _f, _g| Err("".into()));
277 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f| Ok("".into()), |_a, _b, _c, _d, _e, _f, _g| Err("".into()));
278 let _r = CallbackPair::new(|_a, _b, _c, _d, _e, _f, _g| Ok("".into()), |_a, _b, _c, _d, _e, _f, _g| Err("".into()));
279 }
280
281 #[wasm_bindgen_test]
282 async fn inner_dropped_after_await() {
283 let future = CallbackPair::new(|| Ok("".into()), || Err("".into()));
284 let req: IdbOpenDbRequest = window()
285 .expect("window not available")
286 .indexed_db()
287 .unwrap()
288 .expect("idb not available")
289 .open("my_db")
290 .expect("Failed to get idb request");
291 let functions = future.as_functions();
292 req.set_onerror(Some(&functions.1));
293 let inner_ref = {
294 let weak_ref = Rc::downgrade(&future.inner);
295 req.set_onsuccess(Some(&functions.0));
296 assert_eq!(weak_ref.upgrade().is_some(), true); weak_ref
298 };
299 assert_eq!(inner_ref.upgrade().is_some(), true); future.await.unwrap();
301 assert_eq!(inner_ref.upgrade().is_none(), true); }
303
304 #[wasm_bindgen_test]
305 async fn closure_dropped_after_await() {
306 let future = CallbackPair::new(|| Ok("".into()), || Err("".into()));
307 let req: IdbOpenDbRequest = window()
308 .expect("window not available")
309 .indexed_db()
310 .unwrap()
311 .expect("idb not available")
312 .open("my_db")
313 .expect("Failed to get idb request");
314 let wref = {
315 let closures = future.as_closures();
316 let weak_ref = Rc::downgrade(&closures);
317 req.set_onsuccess(Some(closures.0.as_ref().as_ref().unchecked_ref()));
318 req.set_onerror(Some(closures.1.as_ref().as_ref().unchecked_ref()));
319 assert_eq!(weak_ref.upgrade().is_some(), true); weak_ref
321 };
322 assert_eq!(wref.upgrade().is_some(), true); future.await.unwrap();
324 assert_eq!(wref.upgrade().is_none(), true); }
326
327 #[wasm_bindgen_test]
328 async fn new_promise_left_resolve() {
329 let future = CallbackPair::default();
330 web_sys::window()
331 .unwrap()
332 .set_timeout_with_callback_and_timeout_and_arguments_0(future.as_functions().0.as_ref(), 200)
333 .unwrap();
334 let result = future.await;
335 assert_eq!(result.is_ok(), true); }
337
338 #[wasm_bindgen_test]
339 async fn new_promise_right_reject() {
340 let future = CallbackPair::default();
341 web_sys::window()
342 .unwrap()
343 .set_timeout_with_callback_and_timeout_and_arguments_0(future.as_functions().1.as_ref(), 200)
344 .unwrap();
345 let result = future.await;
346 assert_eq!(result.is_err(), true); }
348}