workflow_core/
abortable.rs

1//!
2//! Abortable trigger, can be used to cancel (abort) an asynchronous task.
3//!
4
5use wasm_bindgen::prelude::*;
6
7use std::sync::{
8    atomic::{AtomicBool, Ordering},
9    Arc,
10};
11
12/// Error emitted by [`Abortable`].
13/// @category General
14#[wasm_bindgen]
15pub struct Aborted;
16
17impl std::error::Error for Aborted {}
18
19impl std::fmt::Debug for Aborted {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        write!(f, "task aborted")
22    }
23}
24
25impl std::fmt::Display for Aborted {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        write!(f, "task aborted")
28    }
29}
30
31///
32/// Abortable trigger wraps an `Arc<AtomicBool>`, which can be cloned
33/// to signal task terminating using an atomic bool.
34///
35/// ```text
36/// let abortable = Abortable::default();
37/// let result = my_task(abortable).await?;
38/// // ... elsewhere
39/// abortable.abort();
40/// ```
41///
42/// @category General
43#[derive(Default, Clone)]
44#[wasm_bindgen]
45pub struct Abortable(Arc<AtomicBool>);
46
47#[wasm_bindgen]
48impl Abortable {
49    #[wasm_bindgen(constructor)]
50    pub fn new() -> Self {
51        Self(Arc::new(AtomicBool::new(false)))
52    }
53
54    #[inline]
55    #[wasm_bindgen(js_name=isAborted)]
56    pub fn is_aborted(&self) -> bool {
57        self.0.load(Ordering::SeqCst)
58    }
59
60    #[inline]
61    pub fn abort(&self) {
62        self.0.store(true, Ordering::SeqCst);
63    }
64
65    #[inline]
66    pub fn check(&self) -> Result<(), Aborted> {
67        if self.is_aborted() {
68            Err(Aborted)
69        } else {
70            Ok(())
71        }
72    }
73
74    #[inline]
75    pub fn reset(&self) {
76        self.0.store(false, Ordering::SeqCst);
77    }
78}
79
80impl TryFrom<&JsValue> for Abortable {
81    type Error = JsValue;
82    fn try_from(value: &JsValue) -> Result<Self, Self::Error> {
83        use wasm_bindgen::convert::*;
84
85        let idx = IntoWasmAbi::into_abi(value);
86        #[link(wasm_import_module = "__wbindgen_placeholder__")]
87        #[cfg(all(
88            target_arch = "wasm32",
89            not(any(target_os = "emscripten", target_os = "wasi"))
90        ))]
91        extern "C" {
92            fn __wbg_abortable_unwrap(ptr: u32) -> u32;
93        }
94        #[cfg(not(all(
95            target_arch = "wasm32",
96            not(any(target_os = "emscripten", target_os = "wasi"))
97        )))]
98        unsafe fn __wbg_abortable_unwrap(_: u32) -> u32 {
99            panic!("cannot convert from JsValue outside of the wasm target")
100        }
101        let ptr = unsafe { __wbg_abortable_unwrap(idx) };
102        if ptr == 0 {
103            wasm_bindgen::__rt::std::result::Result::Err(value.clone())
104        } else {
105            unsafe {
106                wasm_bindgen::__rt::std::result::Result::Ok(
107                    <Self as FromWasmAbi>::from_abi(ptr).clone(),
108                )
109            }
110        }
111    }
112}