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 closure::{Closure, IntoWasmClosure, WasmClosure},
14 convert::{FromWasmAbi, ReturnWasmAbi},
15 JsCast, JsValue,
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>;
64
65pub type CallbackClosure<T> = dyn FnMut(T) -> std::result::Result<(), JsValue>;
67pub type CallbackClosureWithoutResult<T> = dyn FnMut(T);
69
70pub trait AsCallback: Send + Sync {
73 fn get_id(&self) -> CallbackId;
74 fn get_fn(&self) -> &Function;
75}
76
77pub struct Callback<T: ?Sized> {
82 id: CallbackId,
83 closure: Arc<Mutex<Option<Arc<Closure<T>>>>>,
84 closure_js_value: JsValue,
85}
86
87unsafe impl<T: ?Sized> Send for Callback<T> {}
88unsafe impl<T: ?Sized> Sync for Callback<T> {}
89
90impl<T: ?Sized> std::fmt::Debug for Callback<T> {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 write!(f, "Callback{{ id:\"{}\" }}", self.id)
93 }
94}
95
96impl<T> AsCallback for Callback<T>
97where
98 T: ?Sized + WasmClosure + 'static,
99{
100 fn get_id(&self) -> CallbackId {
101 self.id
102 }
103
104 fn get_fn(&self) -> &Function {
105 let f: &Function = self.as_ref();
106 f }
108}
109
110impl<T: ?Sized> Clone for Callback<T> {
111 fn clone(&self) -> Self {
112 Self {
113 id: self.id,
114 closure: self.closure.clone(),
115 closure_js_value: self.closure_js_value.clone(),
116 }
117 }
118}
119
120impl<T> Default for Callback<T>
121where
122 T: ?Sized + WasmClosure + 'static,
123{
124 fn default() -> Self {
125 Self {
126 id: CallbackId::new(),
127 closure: Arc::new(Mutex::new(None)),
128 closure_js_value: JsValue::null(),
129 }
130 }
131}
132
133macro_rules! create_fns {
134 ($(
135 ($name: ident, $($var:ident)*)
136 )*) => ($(
137 pub fn $name<$($var,)* R>(callback:T)->Callback<dyn FnMut($($var,)*)->R>
138 where
139 T: 'static + FnMut($($var,)*)->R,
140 $($var: FromWasmAbi + 'static,)*
141 R: ReturnWasmAbi + 'static
142 {
143 Callback::create(callback)
144 }
145
146 )*)
147}
148
149impl<T> Callback<T> {
150 create_fns! {
151 (new_with_args_0, )
152 (new_with_args_1, A)
153 (new_with_args_2, A B)
154 (new_with_args_3, A B C)
155 (new_with_args_4, A B C D)
156 (new_with_args_5, A B C D E)
157 (new_with_args_6, A B C D E F)
158 (new_with_args_7, A B C D E F G)
159 (new_with_args_8, A B C D E F G H)
160 }
161
162 pub fn new<A, R>(callback: T) -> Callback<dyn FnMut(A) -> R>
164 where
165 T: 'static + FnMut(A) -> R,
166 A: FromWasmAbi + 'static,
167 R: ReturnWasmAbi + 'static,
168 {
169 Callback::create(callback)
170 }
171}
172
173impl<T> Callback<T>
174where
175 T: ?Sized + WasmClosure + 'static,
176{
177 pub fn create<F>(t: F) -> Self
179 where
180 F: IntoWasmClosure<T> + 'static,
181 {
182 let mut callback = Callback::<T>::default();
183 callback.set_closure(t);
184
185 callback
186 }
187
188 pub fn set_closure<F>(&mut self, t: F)
190 where
191 F: IntoWasmClosure<T> + 'static,
192 {
193 let closure = Closure::new(t);
194 let closure_js_value = closure.as_ref().clone();
195
196 *self.closure.lock().unwrap() = Some(Arc::new(closure));
197 self.closure_js_value = closure_js_value;
198 }
199
200 pub fn into_js<J>(&self) -> &J
202 where
203 J: JsCast,
204 {
205 self.closure_js_value.as_ref().unchecked_ref()
206 }
207
208 pub fn closure(&self) -> CallbackResult<Arc<Closure<T>>> {
211 match self.closure.lock() {
212 Ok(locked) => match locked.as_ref() {
213 Some(c) => Ok(c.clone()),
214 None => Err(CallbackError::ClosureNotInitialized),
215 },
216 Err(err) => Err(CallbackError::LockError(err.to_string())),
217 }
218 }
219}
220
221impl<T> AsRef<JsValue> for Callback<T>
222where
223 T: ?Sized + WasmClosure + 'static,
224{
225 fn as_ref(&self) -> &JsValue {
226 self.closure_js_value.as_ref().unchecked_ref()
227 }
228}
229
230impl<T> From<Callback<T>> for JsValue
231where
232 T: ?Sized + WasmClosure + 'static,
233{
234 fn from(callback: Callback<T>) -> Self {
235 callback.closure_js_value.unchecked_into()
236 }
237}
238
239impl<T> AsRef<js_sys::Function> for Callback<T>
240where
241 T: ?Sized + WasmClosure + 'static,
242{
243 fn as_ref(&self) -> &js_sys::Function {
244 self.closure_js_value.as_ref().unchecked_ref()
245 }
246}
247
248#[derive(Clone)]
271pub struct CallbackMap {
272 inner: Arc<Mutex<HashMap<CallbackId, Arc<dyn AsCallback>>>>,
273}
274
275impl std::fmt::Debug for CallbackMap {
276 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277 write!(f, "CallbackMap{{...}}")
278 }
279}
280
281impl Default for CallbackMap {
282 fn default() -> Self {
283 Self::new()
284 }
285}
286
287impl CallbackMap {
288 pub fn new() -> Self {
290 Self {
291 inner: Arc::new(Mutex::new(HashMap::new())),
292 }
293 }
294
295 pub fn clear(&self) {
296 self.inner.lock().unwrap().clear();
297 }
298
299 pub fn inner(&self) -> MutexGuard<HashMap<CallbackId, Arc<dyn AsCallback>>> {
301 self.inner.lock().unwrap()
302 }
303
304 pub fn retain<L>(&self, callback: L) -> CallbackResult<Option<Arc<dyn AsCallback>>>
306 where
307 L: Sized + AsCallback + 'static,
308 {
309 let id = callback.get_id();
310
311 let v = self
312 .inner
313 .lock()
314 .map_err(|err| CallbackError::LockError(err.to_string()))?
315 .insert(id, Arc::new(callback));
316
317 Ok(v)
318 }
319
320 pub fn remove(&self, id: &CallbackId) -> CallbackResult<Option<Arc<dyn AsCallback>>> {
322 let v = self
323 .inner
324 .lock()
325 .map_err(|err| CallbackError::LockError(err.to_string()))?
326 .remove(id);
327 Ok(v)
328 }
329}
330
331pub use workflow_wasm_macros::callback;
416
417use crate::printable::Printable;