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::convert::{LongRefFromWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi};
25use wasm_bindgen::prelude::*;
26pub use workflow_wasm_macros::CastFromJs;
27
28#[wasm_bindgen(typescript_custom_section)]
29const IWASM32_BINDINGS_CONFIG: &'static str = r#"
30/**
31 * Interface for configuring workflow-rs WASM32 bindings.
32 *
33 * @category General
34 */
35export interface IWASM32BindingsConfig {
36 /**
37 * This option can be used to disable the validation of class names
38 * for instances of classes exported by Rust WASM32 when passing
39 * these classes to WASM32 functions.
40 *
41 * This can be useful to programmatically disable checks when using
42 * a bundler that mangles class symbol names.
43 */
44 validateClassNames : boolean;
45}
46"#;
47
48#[wasm_bindgen]
49extern "C" {
50 #[wasm_bindgen(extends = Object, typescript_type = "IWASM32BindingsConfig")]
51 pub type IWASM32BindingsConfig;
52}
53
54static mut VALIDATE_CLASS_NAMES: bool = true;
55#[wasm_bindgen(js_name = "initWASM32Bindings")]
59pub fn init_wasm32_bindings(config: IWASM32BindingsConfig) -> std::result::Result<(), Error> {
60 if let Some(enable) = config.try_get_bool("validateClassNames")? {
61 unsafe {
62 VALIDATE_CLASS_NAMES = enable;
63 }
64 }
65 Ok(())
66}
67#[inline(always)]
68pub fn validate_class_names() -> bool {
69 unsafe { VALIDATE_CLASS_NAMES }
70}
71
72pub enum Cast<'a, T>
80where
81 T: RefFromWasmAbi<Abi = u32> + LongRefFromWasmAbi<Abi = u32> + 'a,
82{
83 Ref {
84 anchor: <T as RefFromWasmAbi>::Anchor,
85 },
86 OwnedRef {
87 js_value: Option<JsValue>,
88 anchor: Option<<T as RefFromWasmAbi>::Anchor>,
89 },
90 LongRef {
91 anchor: <T as LongRefFromWasmAbi>::Anchor,
92 },
93 Value {
94 value: Option<T>,
95 },
96 _Unreachable(std::convert::Infallible, &'a std::marker::PhantomData<T>),
97}
98
99impl<'a, T> Drop for Cast<'a, T>
100where
101 T: RefFromWasmAbi<Abi = u32> + LongRefFromWasmAbi<Abi = u32> + 'a,
102{
103 fn drop(&mut self) {
104 if let Cast::OwnedRef { js_value, anchor } = self {
105 drop(anchor.take());
108 drop(js_value.take());
109 }
110 }
111}
112
113impl<'a, T> Deref for Cast<'a, T>
114where
115 T: RefFromWasmAbi<Abi = u32> + LongRefFromWasmAbi<Abi = u32> + Deref,
116{
117 type Target = T;
118 fn deref(&self) -> &Self::Target {
119 match self {
120 Cast::Ref { anchor } => anchor,
121 Cast::OwnedRef { anchor, .. } => anchor.as_ref().unwrap(),
122 Cast::LongRef { anchor } => anchor.borrow(),
123 Cast::Value { value } => value.as_ref().unwrap(),
124 Cast::_Unreachable(_, _) => unreachable!(),
125 }
126 }
127}
128
129impl<'a, T> AsRef<T> for Cast<'a, T>
130where
131 T: RefFromWasmAbi<Abi = u32> + LongRefFromWasmAbi<Abi = u32>,
132{
133 fn as_ref(&self) -> &T {
135 match self {
136 Cast::Ref { anchor } => anchor,
137 Cast::OwnedRef { anchor, .. } => anchor.as_ref().unwrap(),
138 Cast::LongRef { anchor } => anchor.borrow(),
139 Cast::Value { value } => value.as_ref().unwrap(),
140 Cast::_Unreachable(_, _) => unreachable!(),
141 }
142 }
143}
144
145impl<'a, T> Cast<'a, T>
146where
147 T: RefFromWasmAbi<Abi = u32> + LongRefFromWasmAbi<Abi = u32> + Clone,
148{
149 pub fn into_owned(mut self) -> T {
152 match &mut self {
153 Cast::Ref { anchor } => (*anchor).clone(),
154 Cast::OwnedRef { js_value, anchor } => {
155 let value = (*anchor.as_ref().unwrap()).clone();
156 drop(anchor.take());
157 drop(js_value.take());
158 value
159 }
160 Cast::LongRef { anchor } => (*anchor).borrow().clone(),
161 Cast::Value { value } => value.take().unwrap(),
162 Cast::_Unreachable(_, _) => unreachable!(),
163 }
164 }
165
166 pub fn value(value: T) -> Self {
167 Cast::Value { value: Some(value) }
168 }
169
170 }
172
173impl<'a, T> From<T> for Cast<'a, T>
175where
176 T: RefFromWasmAbi<Abi = u32> + LongRefFromWasmAbi<Abi = u32>,
177{
178 fn from(value: T) -> Cast<'a, T> {
179 Cast::Value { value: Some(value) }
180 }
181}
182
183pub trait CastFromJs
187where
188 Self: Sized + RefFromWasmAbi<Abi = u32> + LongRefFromWasmAbi<Abi = u32>,
189{
190 fn try_ref_from_js_value<'a, R>(
192 js_value: &'a R,
193 ) -> std::result::Result<<Self as RefFromWasmAbi>::Anchor, Error>
194 where
195 R: AsRef<JsValue> + 'a;
196
197 fn try_ref_from_js_value_as_cast<'a, R>(
198 js_value: &'a R,
199 ) -> std::result::Result<Cast<'a, Self>, Error>
200 where
201 R: AsRef<JsValue> + 'a,
202 {
203 Self::try_ref_from_js_value(js_value).map(|anchor| Cast::Ref { anchor })
204 }
205
206 fn try_long_ref_from_js_value<'a, R>(
208 js: &'a R,
209 ) -> std::result::Result<<Self as LongRefFromWasmAbi>::Anchor, Error>
210 where
211 R: AsRef<JsValue> + 'a;
212
213 fn try_long_ref_from_js_value_as_cast<'a, R>(
214 js: &'a R,
215 ) -> std::result::Result<Cast<Self>, Error>
216 where
217 R: AsRef<JsValue> + 'a,
218 {
219 Self::try_long_ref_from_js_value(js).map(|anchor| Cast::LongRef { anchor })
220 }
221}
222
223pub trait TryCastFromJs
228where
229 Self: CastFromJs + RefFromWasmAbi<Abi = u32> + LongRefFromWasmAbi<Abi = u32> + Clone,
230{
231 type Error: std::fmt::Display + From<Error>;
232
233 fn try_cast_from<'a, R>(value: &'a R) -> std::result::Result<Cast<'a, Self>, Self::Error>
239 where
240 R: AsRef<JsValue> + 'a;
241
242 fn try_owned_from(value: impl AsRef<JsValue>) -> std::result::Result<Self, Self::Error> {
246 Self::try_cast_from(&value).map(|c| c.into_owned())
247 }
248
249 fn try_captured_cast_from(
250 js_value: impl AsRef<JsValue>,
251 ) -> std::result::Result<Cast<'static, Self>, Self::Error> {
252 let js_value = js_value.as_ref().clone();
253 Ok(
254 Self::try_ref_from_js_value(&js_value).map(|anchor| Cast::OwnedRef {
255 js_value: Some(js_value),
256 anchor: Some(anchor),
257 })?,
258 )
259 }
260
261 fn resolve<'a, R>(
265 js: &'a R,
266 create: impl FnOnce() -> std::result::Result<Self, Self::Error>,
267 ) -> std::result::Result<Cast<Self>, Self::Error>
268 where
269 R: AsRef<JsValue> + 'a,
270 {
271 Self::try_ref_from_js_value(js)
272 .map(|anchor| Cast::Ref { anchor })
273 .or_else(|_| create().map(|value| Cast::Value { value: Some(value) }))
274 }
275
276 fn resolve_cast<'a, R>(
283 js: &'a R,
284 create: impl FnOnce() -> std::result::Result<Cast<'a, Self>, Self::Error>,
285 ) -> std::result::Result<Cast<'a, Self>, Self::Error>
286 where
287 R: AsRef<JsValue> + 'a,
288 {
289 Self::try_ref_from_js_value(js)
290 .map(|anchor| Cast::Ref { anchor })
291 .or_else(|_| create())
292 }
293}
294
295pub trait TryCastJsInto<T>
296where
297 T: TryCastFromJs,
298{
299 type Error: From<Error>;
300 fn try_into_cast(&self) -> std::result::Result<Cast<T>, Self::Error>;
301 fn try_into_owned(&self) -> std::result::Result<T, Self::Error>;
302}
303
304impl<T> TryCastJsInto<T> for JsValue
305where
306 T: TryCastFromJs,
307 <T as TryCastFromJs>::Error: From<Error>,
308{
309 type Error = <T as TryCastFromJs>::Error;
310 fn try_into_cast(&self) -> std::result::Result<Cast<T>, Self::Error> {
311 T::try_cast_from(self)
312 }
313
314 fn try_into_owned(&self) -> std::result::Result<T, Self::Error> {
315 T::try_owned_from(self)
316 }
317}
318
319fn get_ptr_u32_safe(
323 class: &str,
324 js: impl AsRef<JsValue>,
325) -> std::result::Result<Option<u32>, Error> {
326 let js = js.as_ref();
327
328 if js.is_undefined() || js.is_null() {
329 return Ok(None);
330 } else if !js.is_object() {
331 return Err(Error::NotAnObjectOfClass(class.to_string()));
332 }
333
334 if validate_class_names() {
335 let ctor = ::js_sys::Reflect::get(js, &JsValue::from_str("constructor"))?;
336 if ctor.is_undefined() {
337 return Err(Error::NoConstructorOfClass(class.to_string()));
338 } else {
339 let name = ::js_sys::Reflect::get(&ctor, &JsValue::from_str("name"))?;
340 if name.is_undefined() {
341 return Err(Error::UnableToObtainConstructorName(class.to_string()));
342 } else {
343 let name = name
344 .as_string()
345 .ok_or(Error::UnableToObtainConstructorName(class.to_string()))?;
346 if name != class {
347 return Err(Error::ClassConstructorMatch(name, class.to_string()));
348 }
349 }
350 }
351 }
352
353 let ptr = ::js_sys::Reflect::get(js, &::wasm_bindgen::JsValue::from_str("__wbg_ptr"))?;
354 if ptr.is_undefined() {
355 return Err(Error::NotWasmAbiPointerForClass(class.to_string()));
356 }
357 let ptr_u32: u32 = ptr
358 .as_f64()
359 .ok_or(Error::NotWasmAbiPointerForClass(class.to_string()))? as u32;
360
361 Ok(Some(ptr_u32))
362}
363
364#[inline]
366pub fn try_ref_from_abi_safe<T>(
367 class: &str,
368 js: impl AsRef<JsValue>,
369) -> std::result::Result<<T as RefFromWasmAbi>::Anchor, Error>
370where
371 T: RefFromWasmAbi<Abi = u32>,
372{
373 let ptr_u32 =
374 get_ptr_u32_safe(class, js)?.ok_or_else(|| Error::NotAnObjectOfClass(class.to_string()))?;
375 Ok(unsafe { T::ref_from_abi(ptr_u32) })
376}
377
378#[inline]
379pub fn try_long_ref_from_abi_safe<T>(
380 class: &str,
381 js: impl AsRef<JsValue>,
382) -> std::result::Result<<T as LongRefFromWasmAbi>::Anchor, Error>
383where
384 T: LongRefFromWasmAbi<Abi = u32>,
385{
386 let ptr_u32 =
387 get_ptr_u32_safe(class, js)?.ok_or_else(|| Error::NotAnObjectOfClass(class.to_string()))?;
388 Ok(unsafe { T::long_ref_from_abi(ptr_u32) })
389}
390
391#[inline]
392pub fn try_ref_mut_from_abi_safe<T>(
393 class: &str,
394 js: impl AsRef<JsValue>,
395) -> std::result::Result<<T as RefMutFromWasmAbi>::Anchor, Error>
396where
397 T: RefMutFromWasmAbi<Abi = u32>,
398{
399 let ptr_u32 =
400 get_ptr_u32_safe(class, js)?.ok_or_else(|| Error::NotAnObjectOfClass(class.to_string()))?;
401 Ok(unsafe { T::ref_mut_from_abi(ptr_u32) })
402}
403
404#[inline]
405pub fn try_clone_from_abi_safe<T>(
406 class: &str,
407 js: impl AsRef<JsValue>,
408) -> std::result::Result<T, Error>
409where
410 T: RefFromWasmAbi<Abi = u32> + Clone,
411{
412 try_ref_from_abi_safe::<T>(class, js).map(|r| r.clone())
413}
414
415#[inline]
416pub fn try_copy_from_abi_safe<T>(
417 class: &str,
418 js: impl AsRef<JsValue>,
419) -> std::result::Result<T, Error>
420where
421 T: RefFromWasmAbi<Abi = u32> + Copy,
422{
423 try_ref_from_abi_safe::<T>(class, js).map(|r| *r)
424}
425
426#[inline]
430pub fn try_ref_from_abi_safe_as_option<T>(
431 class: &str,
432 js: impl AsRef<JsValue>,
433) -> std::result::Result<Option<<T as RefFromWasmAbi>::Anchor>, JsValue>
434where
435 T: RefFromWasmAbi<Abi = u32>,
436{
437 Ok(get_ptr_u32_safe(class, js)?.map(|ptr_u32| unsafe { T::ref_from_abi(ptr_u32) }))
438}
439
440#[inline]
441pub fn try_ref_mut_from_abi_safe_as_option<T>(
442 class: &str,
443 js: impl AsRef<JsValue>,
444) -> std::result::Result<Option<<T as RefMutFromWasmAbi>::Anchor>, JsValue>
445where
446 T: RefMutFromWasmAbi<Abi = u32>,
447{
448 Ok(get_ptr_u32_safe(class, js)?.map(|ptr_u32| unsafe { T::ref_mut_from_abi(ptr_u32) }))
449}
450
451#[inline]
452pub fn try_clone_from_abi_safe_as_option<T>(
453 class: &str,
454 js: impl AsRef<JsValue>,
455) -> std::result::Result<Option<T>, JsValue>
456where
457 T: RefFromWasmAbi<Abi = u32> + Clone,
458{
459 Ok(get_ptr_u32_safe(class, js)?.map(|ptr_u32| unsafe { T::ref_from_abi(ptr_u32).clone() }))
460}
461
462#[inline]
463pub fn try_copy_from_abi_safe_as_option<T>(
464 class: &str,
465 js: impl AsRef<JsValue>,
466) -> std::result::Result<Option<T>, JsValue>
467where
468 T: RefFromWasmAbi<Abi = u32> + Copy,
469{
470 Ok(get_ptr_u32_safe(class, js)?.map(|ptr_u32| unsafe { *T::ref_from_abi(ptr_u32) }))
471}