1use crate::error::Error;
20use crate::extensions::ObjectExtension;
21use js_sys::Object;
22pub use std::borrow::Borrow;
23pub use std::ops::Deref;
24use wasm_bindgen::__rt::{WasmPtr, WasmRefCell};
25use wasm_bindgen::convert::{LongRefFromWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi};
26use wasm_bindgen::prelude::*;
27pub use workflow_wasm_macros::CastFromJs;
28
29#[wasm_bindgen(typescript_custom_section)]
30const IWASM32_BINDINGS_CONFIG: &'static str = r#"
31/**
32 * Interface for configuring workflow-rs WASM32 bindings.
33 *
34 * @category General
35 */
36export interface IWASM32BindingsConfig {
37 /**
38 * This option can be used to disable the validation of class names
39 * for instances of classes exported by Rust WASM32 when passing
40 * these classes to WASM32 functions.
41 *
42 * This can be useful to programmatically disable checks when using
43 * a bundler that mangles class symbol names.
44 */
45 validateClassNames : boolean;
46}
47"#;
48
49#[wasm_bindgen]
50extern "C" {
51 #[wasm_bindgen(extends = Object, typescript_type = "IWASM32BindingsConfig")]
53 pub type IWASM32BindingsConfig;
54}
55
56static mut VALIDATE_CLASS_NAMES: bool = true;
57#[wasm_bindgen(js_name = "initWASM32Bindings")]
61pub fn init_wasm32_bindings(config: IWASM32BindingsConfig) -> std::result::Result<(), Error> {
62 if let Some(enable) = config.try_get_bool("validateClassNames")? {
63 unsafe {
64 VALIDATE_CLASS_NAMES = enable;
65 }
66 }
67 Ok(())
68}
69#[inline(always)]
71pub fn validate_class_names() -> bool {
72 unsafe { VALIDATE_CLASS_NAMES }
73}
74
75pub enum Cast<'a, T>
83where
84 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>
85 + LongRefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>
86 + 'a,
87{
88 Ref {
90 anchor: <T as RefFromWasmAbi>::Anchor,
92 },
93 OwnedRef {
95 js_value: Option<JsValue>,
97 anchor: Option<<T as RefFromWasmAbi>::Anchor>,
99 },
100 LongRef {
102 anchor: <T as LongRefFromWasmAbi>::Anchor,
104 },
105 Value {
107 value: Option<T>,
109 },
110 _Unreachable(std::convert::Infallible, &'a std::marker::PhantomData<T>),
112}
113
114impl<T> Drop for Cast<'_, T>
115where
116 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>
117 + LongRefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>,
118{
119 fn drop(&mut self) {
120 if let Cast::OwnedRef { js_value, anchor } = self {
121 drop(anchor.take());
124 drop(js_value.take());
125 }
126 }
127}
128
129impl<T> Deref for Cast<'_, T>
130where
131 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>
132 + LongRefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>
133 + Deref,
134{
135 type Target = T;
136 fn deref(&self) -> &Self::Target {
137 match self {
138 Cast::Ref { anchor } => anchor,
139 Cast::OwnedRef { anchor, .. } => anchor.as_ref().unwrap(),
140 Cast::LongRef { anchor } => anchor.borrow(),
141 Cast::Value { value } => value.as_ref().unwrap(),
142 Cast::_Unreachable(_, _) => unreachable!(),
143 }
144 }
145}
146
147impl<T> AsRef<T> for Cast<'_, T>
148where
149 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>
150 + LongRefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>,
151{
152 fn as_ref(&self) -> &T {
154 match self {
155 Cast::Ref { anchor } => anchor,
156 Cast::OwnedRef { anchor, .. } => anchor.as_ref().unwrap(),
157 Cast::LongRef { anchor } => anchor.borrow(),
158 Cast::Value { value } => value.as_ref().unwrap(),
159 Cast::_Unreachable(_, _) => unreachable!(),
160 }
161 }
162}
163
164impl<T> Cast<'_, T>
165where
166 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>
167 + LongRefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>
168 + Clone,
169{
170 pub fn into_owned(mut self) -> T {
173 match &mut self {
174 Cast::Ref { anchor } => (*anchor).clone(),
175 Cast::OwnedRef { js_value, anchor } => {
176 let value = (*anchor.as_ref().unwrap()).clone();
177 drop(anchor.take());
178 drop(js_value.take());
179 value
180 }
181 Cast::LongRef { anchor } => (*anchor).borrow().clone(),
182 Cast::Value { value } => value.take().unwrap(),
183 Cast::_Unreachable(_, _) => unreachable!(),
184 }
185 }
186
187 pub fn value(value: T) -> Self {
189 Cast::Value { value: Some(value) }
190 }
191
192 }
194
195impl<'a, T> From<T> for Cast<'a, T>
197where
198 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>
199 + LongRefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>,
200{
201 fn from(value: T) -> Cast<'a, T> {
202 Cast::Value { value: Some(value) }
203 }
204}
205
206pub trait CastFromJs
210where
211 Self: Sized
212 + RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<Self>>>
213 + LongRefFromWasmAbi<Abi = WasmPtr<WasmRefCell<Self>>>,
214{
215 fn try_ref_from_js_value<'a, R>(
217 js_value: &'a R,
218 ) -> std::result::Result<<Self as RefFromWasmAbi>::Anchor, Error>
219 where
220 R: AsRef<JsValue> + 'a;
221
222 fn try_ref_from_js_value_as_cast<'a, R>(
224 js_value: &'a R,
225 ) -> std::result::Result<Cast<'a, Self>, Error>
226 where
227 R: AsRef<JsValue> + 'a,
228 {
229 Self::try_ref_from_js_value(js_value).map(|anchor| Cast::Ref { anchor })
230 }
231
232 fn try_long_ref_from_js_value<'a, R>(
234 js: &'a R,
235 ) -> std::result::Result<<Self as LongRefFromWasmAbi>::Anchor, Error>
236 where
237 R: AsRef<JsValue> + 'a;
238
239 fn try_long_ref_from_js_value_as_cast<'a, R>(
241 js: &'a R,
242 ) -> std::result::Result<Cast<'a, Self>, Error>
243 where
244 R: AsRef<JsValue> + 'a,
245 {
246 Self::try_long_ref_from_js_value(js).map(|anchor| Cast::LongRef { anchor })
247 }
248}
249
250pub trait TryCastFromJs
255where
256 Self: CastFromJs
257 + RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<Self>>>
258 + LongRefFromWasmAbi<Abi = WasmPtr<WasmRefCell<Self>>>
259 + Clone,
260{
261 type Error: std::fmt::Display + From<Error>;
263
264 fn try_cast_from<'a, R>(value: &'a R) -> std::result::Result<Cast<'a, Self>, Self::Error>
270 where
271 R: AsRef<JsValue> + 'a;
272
273 fn try_owned_from(value: impl AsRef<JsValue>) -> std::result::Result<Self, Self::Error> {
277 Self::try_cast_from(&value).map(|c| c.into_owned())
278 }
279
280 fn try_captured_cast_from(
283 js_value: impl AsRef<JsValue>,
284 ) -> std::result::Result<Cast<'static, Self>, Self::Error> {
285 let js_value = js_value.as_ref().clone();
286 Ok(
287 Self::try_ref_from_js_value(&js_value).map(|anchor| Cast::OwnedRef {
288 js_value: Some(js_value),
289 anchor: Some(anchor),
290 })?,
291 )
292 }
293
294 fn resolve<'a, R>(
298 js: &'a R,
299 create: impl FnOnce() -> std::result::Result<Self, Self::Error>,
300 ) -> std::result::Result<Cast<'a, Self>, Self::Error>
301 where
302 R: AsRef<JsValue> + 'a,
303 {
304 Self::try_ref_from_js_value(js)
305 .map(|anchor| Cast::Ref { anchor })
306 .or_else(|_| create().map(|value| Cast::Value { value: Some(value) }))
307 }
308
309 fn resolve_cast<'a, R>(
316 js: &'a R,
317 create: impl FnOnce() -> std::result::Result<Cast<'a, Self>, Self::Error>,
318 ) -> std::result::Result<Cast<'a, Self>, Self::Error>
319 where
320 R: AsRef<JsValue> + 'a,
321 {
322 Self::try_ref_from_js_value(js)
323 .map(|anchor| Cast::Ref { anchor })
324 .or_else(|_| create())
325 }
326}
327
328pub trait TryCastJsInto<T>
331where
332 T: TryCastFromJs,
333{
334 type Error: From<Error>;
336 fn try_into_cast(&self) -> std::result::Result<Cast<'_, T>, Self::Error>;
338 fn try_into_owned(&self) -> std::result::Result<T, Self::Error>;
340}
341
342impl<T> TryCastJsInto<T> for JsValue
343where
344 T: TryCastFromJs,
345 <T as TryCastFromJs>::Error: From<Error>,
346{
347 type Error = <T as TryCastFromJs>::Error;
348 fn try_into_cast(&self) -> std::result::Result<Cast<'_, T>, Self::Error> {
349 T::try_cast_from(self)
350 }
351
352 fn try_into_owned(&self) -> std::result::Result<T, Self::Error> {
353 T::try_owned_from(self)
354 }
355}
356
357fn get_ptr_u32_safe(
361 class: &str,
362 js: impl AsRef<JsValue>,
363) -> std::result::Result<Option<u32>, Error> {
364 let js = js.as_ref();
365
366 if js.is_undefined() || js.is_null() {
367 return Ok(None);
368 } else if !js.is_object() {
369 return Err(Error::NotAnObjectOfClass(class.to_string()));
370 }
371
372 if validate_class_names() {
373 let ctor = ::js_sys::Reflect::get(js, &JsValue::from_str("constructor"))?;
374 if ctor.is_undefined() {
375 return Err(Error::NoConstructorOfClass(class.to_string()));
376 } else {
377 let name = ::js_sys::Reflect::get(&ctor, &JsValue::from_str("name"))?;
378 if name.is_undefined() {
379 return Err(Error::UnableToObtainConstructorName(class.to_string()));
380 } else {
381 let name = name
382 .as_string()
383 .ok_or(Error::UnableToObtainConstructorName(class.to_string()))?;
384 if name != class {
385 return Err(Error::ClassConstructorMatch(name, class.to_string()));
386 }
387 }
388 }
389 }
390
391 let ptr = ::js_sys::Reflect::get(js, &::wasm_bindgen::JsValue::from_str("__wbg_ptr"))?;
392 if ptr.is_undefined() {
393 return Err(Error::NotWasmAbiPointerForClass(class.to_string()));
394 }
395 let ptr_u32: u32 = ptr
396 .as_f64()
397 .ok_or(Error::NotWasmAbiPointerForClass(class.to_string()))? as u32;
398
399 Ok(Some(ptr_u32))
400}
401
402#[inline]
404pub fn try_ref_from_abi_safe<T>(
405 class: &str,
406 js: impl AsRef<JsValue>,
407) -> std::result::Result<<T as RefFromWasmAbi>::Anchor, Error>
408where
409 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>,
410{
411 let ptr_u32 =
412 get_ptr_u32_safe(class, js)?.ok_or_else(|| Error::NotAnObjectOfClass(class.to_string()))?;
413 Ok(unsafe { T::ref_from_abi(WasmPtr::from_usize(ptr_u32 as usize)) })
414}
415
416#[inline]
417pub fn try_long_ref_from_abi_safe<T>(
419 class: &str,
420 js: impl AsRef<JsValue>,
421) -> std::result::Result<<T as LongRefFromWasmAbi>::Anchor, Error>
422where
423 T: LongRefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>,
424{
425 let ptr_u32 =
426 get_ptr_u32_safe(class, js)?.ok_or_else(|| Error::NotAnObjectOfClass(class.to_string()))?;
427 Ok(unsafe { T::long_ref_from_abi(WasmPtr::from_usize(ptr_u32 as usize)) })
428}
429
430#[inline]
431pub fn try_ref_mut_from_abi_safe<T>(
433 class: &str,
434 js: impl AsRef<JsValue>,
435) -> std::result::Result<<T as RefMutFromWasmAbi>::Anchor, Error>
436where
437 T: RefMutFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>,
438{
439 let ptr_u32 =
440 get_ptr_u32_safe(class, js)?.ok_or_else(|| Error::NotAnObjectOfClass(class.to_string()))?;
441 Ok(unsafe { T::ref_mut_from_abi(WasmPtr::from_usize(ptr_u32 as usize)) })
442}
443
444#[inline]
445pub fn try_clone_from_abi_safe<T>(
447 class: &str,
448 js: impl AsRef<JsValue>,
449) -> std::result::Result<T, Error>
450where
451 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>> + Clone,
452{
453 try_ref_from_abi_safe::<T>(class, js).map(|r| r.clone())
454}
455
456#[inline]
457pub fn try_copy_from_abi_safe<T>(
459 class: &str,
460 js: impl AsRef<JsValue>,
461) -> std::result::Result<T, Error>
462where
463 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>> + Copy,
464{
465 try_ref_from_abi_safe::<T>(class, js).map(|r| *r)
466}
467
468#[inline]
472pub fn try_ref_from_abi_safe_as_option<T>(
473 class: &str,
474 js: impl AsRef<JsValue>,
475) -> std::result::Result<Option<<T as RefFromWasmAbi>::Anchor>, JsValue>
476where
477 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>,
478{
479 Ok(get_ptr_u32_safe(class, js)?
480 .map(|ptr_u32| unsafe { T::ref_from_abi(WasmPtr::from_usize(ptr_u32 as usize)) }))
481}
482
483#[inline]
484pub fn try_ref_mut_from_abi_safe_as_option<T>(
487 class: &str,
488 js: impl AsRef<JsValue>,
489) -> std::result::Result<Option<<T as RefMutFromWasmAbi>::Anchor>, JsValue>
490where
491 T: RefMutFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>>,
492{
493 Ok(get_ptr_u32_safe(class, js)?
494 .map(|ptr_u32| unsafe { T::ref_mut_from_abi(WasmPtr::from_usize(ptr_u32 as usize)) }))
495}
496
497#[inline]
498pub fn try_clone_from_abi_safe_as_option<T>(
501 class: &str,
502 js: impl AsRef<JsValue>,
503) -> std::result::Result<Option<T>, JsValue>
504where
505 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>> + Clone,
506{
507 Ok(get_ptr_u32_safe(class, js)?
508 .map(|ptr_u32| unsafe { T::ref_from_abi(WasmPtr::from_usize(ptr_u32 as usize)).clone() }))
509}
510
511#[inline]
512pub fn try_copy_from_abi_safe_as_option<T>(
515 class: &str,
516 js: impl AsRef<JsValue>,
517) -> std::result::Result<Option<T>, JsValue>
518where
519 T: RefFromWasmAbi<Abi = WasmPtr<WasmRefCell<T>>> + Copy,
520{
521 Ok(get_ptr_u32_safe(class, js)?
522 .map(|ptr_u32| unsafe { *T::ref_from_abi(WasmPtr::from_usize(ptr_u32 as usize)) }))
523}