workflow_wasm/
callback.rs1use js_sys::Function;
7use std::{
8 collections::HashMap,
9 sync::{Arc, Mutex, MutexGuard},
10};
11use thiserror::Error;
12use wasm_bindgen::{
13 JsCast, JsValue,
14 closure::{Closure, IntoWasmClosure, WasmClosure},
15 convert::{FromWasmAbi, ReturnWasmAbi},
16};
17use workflow_core::id::Id;
18
19pub type CallbackId = Id;
21
22#[derive(Error, Debug, Clone)]
24pub enum CallbackError {
25 #[error("String {0:?}")]
27 String(String),
28
29 #[error("JsValue {0:?}")]
31 JsValue(Printable),
32
33 #[error("LockError: Unable to lock closure, {0:?}")]
35 LockError(String),
36
37 #[error("ClosureNotIntialized, Please use `callback.set_closure()`")]
38 ClosureNotInitialized,
40}
41
42unsafe impl Send for CallbackError {}
43unsafe impl Sync for CallbackError {}
44
45impl From<JsValue> for CallbackError {
46 fn from(value: JsValue) -> Self {
47 CallbackError::JsValue(value.into())
48 }
49}
50
51impl From<CallbackError> for JsValue {
52 fn from(err: CallbackError) -> Self {
53 JsValue::from_str(&err.to_string())
54 }
55}
56
57impl From<String> for CallbackError {
58 fn from(str: String) -> Self {
59 Self::String(str)
60 }
61}
62
63pub type CallbackResult<T> = std::result::Result<T, CallbackError>;
65
66pub type CallbackClosure<T> = dyn FnMut(T) -> std::result::Result<(), JsValue>;
68pub type CallbackClosureWithoutResult<T> = dyn FnMut(T);
70
71pub trait AsCallback: Send + Sync {
74 fn get_id(&self) -> CallbackId;
76 fn get_fn(&self) -> &Function;
78}
79
80pub struct Callback<T: ?Sized> {
85 id: CallbackId,
86 closure: Arc<Mutex<Option<Arc<Closure<T>>>>>,
87 closure_js_value: JsValue,
88}
89
90unsafe impl<T: ?Sized> Send for Callback<T> {}
91unsafe impl<T: ?Sized> Sync for Callback<T> {}
92
93impl<T: ?Sized> std::fmt::Debug for Callback<T> {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 write!(f, "Callback{{ id:\"{}\" }}", self.id)
96 }
97}
98
99impl<T> AsCallback for Callback<T>
100where
101 T: ?Sized + WasmClosure + 'static,
102{
103 fn get_id(&self) -> CallbackId {
104 self.id
105 }
106
107 fn get_fn(&self) -> &Function {
108 let f: &Function = self.as_ref();
109 f }
111}
112
113impl<T: ?Sized> Clone for Callback<T> {
114 fn clone(&self) -> Self {
115 Self {
116 id: self.id,
117 closure: self.closure.clone(),
118 closure_js_value: self.closure_js_value.clone(),
119 }
120 }
121}
122
123impl<T> Default for Callback<T>
124where
125 T: ?Sized + WasmClosure + 'static,
126{
127 fn default() -> Self {
128 Self {
129 id: CallbackId::new(),
130 closure: Arc::new(Mutex::new(None)),
131 closure_js_value: JsValue::null(),
132 }
133 }
134}
135
136macro_rules! create_fns {
137 ($(
138 ($name: ident, $($var:ident)*)
139 )*) => ($(
140 pub fn $name<$($var,)* R>(callback:T)->Callback<dyn FnMut($($var,)*)->R>
142 where
143 T: 'static + FnMut($($var,)*)->R,
144 $($var: FromWasmAbi + 'static,)*
145 R: ReturnWasmAbi + 'static
146 {
147 Callback::create(callback)
148 }
149
150 )*)
151}
152
153impl<T> Callback<T> {
154 create_fns! {
155 (new_with_args_0, )
156 (new_with_args_1, A)
157 (new_with_args_2, A B)
158 (new_with_args_3, A B C)
159 (new_with_args_4, A B C D)
160 (new_with_args_5, A B C D E)
161 (new_with_args_6, A B C D E F)
162 (new_with_args_7, A B C D E F G)
163 (new_with_args_8, A B C D E F G H)
164 }
165
166 pub fn new<A, R>(callback: T) -> Callback<dyn FnMut(A) -> R>
168 where
169 T: 'static + FnMut(A) -> R,
170 A: FromWasmAbi + 'static,
171 R: ReturnWasmAbi + 'static,
172 {
173 Callback::create(callback)
174 }
175}
176
177impl<T> Callback<T>
178where
179 T: ?Sized + WasmClosure + 'static,
180{
181 pub fn create<F>(t: F) -> Self
183 where
184 F: IntoWasmClosure<T> + 'static,
185 {
186 let mut callback = Callback::<T>::default();
187 callback.set_closure(t);
188
189 callback
190 }
191
192 pub fn set_closure<F>(&mut self, t: F)
194 where
195 F: IntoWasmClosure<T> + 'static,
196 {
197 let closure = Closure::new(t);
198 let closure_js_value = closure.as_ref().clone();
199
200 *self.closure.lock().unwrap() = Some(Arc::new(closure));
201 self.closure_js_value = closure_js_value;
202 }
203
204 pub fn into_js<J>(&self) -> &J
206 where
207 J: JsCast,
208 {
209 self.closure_js_value.as_ref().unchecked_ref()
210 }
211
212 pub fn closure(&self) -> CallbackResult<Arc<Closure<T>>> {
215 match self.closure.lock() {
216 Ok(locked) => match locked.as_ref() {
217 Some(c) => Ok(c.clone()),
218 None => Err(CallbackError::ClosureNotInitialized),
219 },
220 Err(err) => Err(CallbackError::LockError(err.to_string())),
221 }
222 }
223}
224
225impl<T> AsRef<JsValue> for Callback<T>
226where
227 T: ?Sized + WasmClosure + 'static,
228{
229 fn as_ref(&self) -> &JsValue {
230 self.closure_js_value.as_ref().unchecked_ref()
231 }
232}
233
234impl<T> From<Callback<T>> for JsValue
235where
236 T: ?Sized + WasmClosure + 'static,
237{
238 fn from(callback: Callback<T>) -> Self {
239 callback.closure_js_value.unchecked_into()
240 }
241}
242
243impl<T> AsRef<js_sys::Function> for Callback<T>
244where
245 T: ?Sized + WasmClosure + 'static,
246{
247 fn as_ref(&self) -> &js_sys::Function {
248 self.closure_js_value.as_ref().unchecked_ref()
249 }
250}
251
252#[derive(Clone)]
275pub struct CallbackMap {
276 inner: Arc<Mutex<HashMap<CallbackId, Arc<dyn AsCallback>>>>,
277}
278
279impl std::fmt::Debug for CallbackMap {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 write!(f, "CallbackMap{{...}}")
282 }
283}
284
285impl Default for CallbackMap {
286 fn default() -> Self {
287 Self::new()
288 }
289}
290
291impl CallbackMap {
292 pub fn new() -> Self {
294 Self {
295 inner: Arc::new(Mutex::new(HashMap::new())),
296 }
297 }
298
299 pub fn clear(&self) {
301 self.inner.lock().unwrap().clear();
302 }
303
304 pub fn inner(&self) -> MutexGuard<'_, HashMap<CallbackId, Arc<dyn AsCallback>>> {
306 self.inner.lock().unwrap()
307 }
308
309 pub fn retain<L>(&self, callback: L) -> CallbackResult<Option<Arc<dyn AsCallback>>>
311 where
312 L: Sized + AsCallback + 'static,
313 {
314 let id = callback.get_id();
315
316 let v = self
317 .inner
318 .lock()
319 .map_err(|err| CallbackError::LockError(err.to_string()))?
320 .insert(id, Arc::new(callback));
321
322 Ok(v)
323 }
324
325 pub fn remove(&self, id: &CallbackId) -> CallbackResult<Option<Arc<dyn AsCallback>>> {
327 let v = self
328 .inner
329 .lock()
330 .map_err(|err| CallbackError::LockError(err.to_string()))?
331 .remove(id);
332 Ok(v)
333 }
334}
335
336pub use workflow_wasm_macros::callback;
421
422use crate::printable::Printable;