Skip to main content

pipecrab_runtime/
maybe.rs

1//! Target-conditional `Send`/`Sync` bounds: real on native, vacuous on wasm32.
2//!
3//! The pipeline runs as *one logical task* on every target. On native that task
4//! should still be `Send` so a work-stealing executor (e.g. multi-threaded
5//! tokio) can spawn and migrate it like any other task. On `wasm32` there is
6//! only one thread and JS handles (`JsValue`) are `!Send`, so the same bounds
7//! must vanish. These aliases express "Send where it exists" once, so the rest
8//! of the codebase is written a single time.
9
10/// `Send` on native targets; no requirement on `wasm32`.
11#[cfg(not(target_arch = "wasm32"))]
12pub trait MaybeSend: Send {}
13#[cfg(not(target_arch = "wasm32"))]
14impl<T: ?Sized + Send> MaybeSend for T {}
15
16/// `Send` on native targets; no requirement on `wasm32`.
17#[cfg(target_arch = "wasm32")]
18pub trait MaybeSend {}
19#[cfg(target_arch = "wasm32")]
20impl<T: ?Sized> MaybeSend for T {}
21
22/// `Send + Sync` on native targets; no requirement on `wasm32`.
23#[cfg(not(target_arch = "wasm32"))]
24pub trait MaybeSendSync: Send + Sync {}
25#[cfg(not(target_arch = "wasm32"))]
26impl<T: ?Sized + Send + Sync> MaybeSendSync for T {}
27
28/// `Send + Sync` on native targets; no requirement on `wasm32`.
29#[cfg(target_arch = "wasm32")]
30pub trait MaybeSendSync {}
31#[cfg(target_arch = "wasm32")]
32impl<T: ?Sized> MaybeSendSync for T {}
33
34/// Apply [`async_trait`](https://docs.rs/async-trait) with the target-correct
35/// `Send`-ness, in one line instead of the two-attribute `cfg_attr` dance.
36///
37/// Every async trait definition and impl in pipecrab needs the same pair —
38/// plain `#[async_trait]` on native (its boxed futures are `Send`, matching the
39/// [`MaybeSend`]/[`MaybeSendSync`] bounds), and `#[async_trait(?Send)]` on
40/// `wasm32` (where they can't be). Writing both by hand is easy to get subtly
41/// wrong (swap the two `cfg`s and native silently loses `Send`). Wrap the item
42/// instead — the trait definition *or* the impl block:
43///
44/// ```
45/// use pipecrab_runtime::{maybe_async_trait, MaybeSend};
46///
47/// maybe_async_trait! {
48///     pub trait Widget: MaybeSend {
49///         async fn poll(&mut self) -> u32;
50///     }
51/// }
52///
53/// struct Zero;
54/// maybe_async_trait! {
55///     impl Widget for Zero {
56///         async fn poll(&mut self) -> u32 { 0 }
57///     }
58/// }
59/// ```
60///
61/// The macro pulls in `async_trait` through this crate, so a stage or interface crate
62/// that uses it needs only a `pipecrab-runtime` dependency, not a direct one on
63/// `async-trait`.
64#[macro_export]
65macro_rules! maybe_async_trait {
66    ($item:item) => {
67        #[cfg_attr(target_arch = "wasm32", $crate::async_trait::async_trait(?Send))]
68        #[cfg_attr(not(target_arch = "wasm32"), $crate::async_trait::async_trait)]
69        $item
70    };
71}