Skip to main content

supabase_client_core/
platform.rs

1//! Platform abstractions for cross-platform async operations.
2//!
3//! Provides unified APIs for spawning tasks, sleeping, and timeouts
4//! that work on both native (tokio) and WASM (wasm-bindgen-futures + gloo) targets.
5
6use std::future::Future;
7use std::time::Duration;
8
9// ── Spawn ────────────────────────────────────────────────────────────────────
10
11/// Spawn a future as a background task.
12///
13/// - **Native:** Uses `tokio::spawn` (requires `Send + 'static`).
14/// - **WASM:** Uses `wasm_bindgen_futures::spawn_local` (no `Send` required).
15#[cfg(not(target_arch = "wasm32"))]
16pub fn spawn<F>(future: F) -> SpawnHandle
17where
18    F: Future<Output = ()> + Send + 'static,
19{
20    SpawnHandle {
21        handle: tokio::spawn(future),
22    }
23}
24
25#[cfg(target_arch = "wasm32")]
26pub fn spawn<F>(future: F) -> SpawnHandle
27where
28    F: Future<Output = ()> + 'static,
29{
30    let (abort_tx, abort_rx) = tokio::sync::oneshot::channel::<()>();
31    wasm_bindgen_futures::spawn_local(async move {
32        futures_util::pin_mut!(future);
33        futures_util::future::select(future, abort_rx).await;
34    });
35    SpawnHandle { abort_tx: Some(abort_tx) }
36}
37
38// ── SpawnHandle ──────────────────────────────────────────────────────────────
39
40/// Handle to a spawned background task, allowing cancellation.
41#[cfg(not(target_arch = "wasm32"))]
42pub struct SpawnHandle {
43    handle: tokio::task::JoinHandle<()>,
44}
45
46#[cfg(not(target_arch = "wasm32"))]
47impl SpawnHandle {
48    /// Abort the spawned task.
49    pub fn abort(&self) {
50        self.handle.abort();
51    }
52}
53
54#[cfg(target_arch = "wasm32")]
55pub struct SpawnHandle {
56    abort_tx: Option<tokio::sync::oneshot::Sender<()>>,
57}
58
59#[cfg(target_arch = "wasm32")]
60impl SpawnHandle {
61    /// Abort the spawned task.
62    pub fn abort(&mut self) {
63        if let Some(tx) = self.abort_tx.take() {
64            let _ = tx.send(());
65        }
66    }
67}
68
69// ── Sleep ────────────────────────────────────────────────────────────────────
70
71/// Sleep for the given duration.
72///
73/// - **Native:** Uses `tokio::time::sleep`.
74/// - **WASM:** Uses `gloo_timers::future::sleep`.
75#[cfg(not(target_arch = "wasm32"))]
76pub async fn sleep(duration: Duration) {
77    tokio::time::sleep(duration).await;
78}
79
80#[cfg(target_arch = "wasm32")]
81pub async fn sleep(duration: Duration) {
82    gloo_timers::future::sleep(duration).await;
83}
84
85// ── Timeout ──────────────────────────────────────────────────────────────────
86
87/// Error returned when a timeout expires.
88#[derive(Debug, Clone)]
89pub struct TimeoutError;
90
91impl std::fmt::Display for TimeoutError {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        write!(f, "operation timed out")
94    }
95}
96
97impl std::error::Error for TimeoutError {}
98
99/// Run a future with a timeout.
100///
101/// - **Native:** Uses `tokio::time::timeout`.
102/// - **WASM:** Races the future against `gloo_timers::future::sleep`.
103#[cfg(not(target_arch = "wasm32"))]
104pub async fn timeout<F, T>(duration: Duration, future: F) -> Result<T, TimeoutError>
105where
106    F: Future<Output = T>,
107{
108    tokio::time::timeout(duration, future)
109        .await
110        .map_err(|_| TimeoutError)
111}
112
113#[cfg(target_arch = "wasm32")]
114pub async fn timeout<F, T>(duration: Duration, future: F) -> Result<T, TimeoutError>
115where
116    F: Future<Output = T>,
117{
118    use futures_util::future::{select, Either};
119
120    futures_util::pin_mut!(future);
121    let sleep = gloo_timers::future::sleep(duration);
122    futures_util::pin_mut!(sleep);
123
124    match select(future, sleep).await {
125        Either::Left((output, _)) => Ok(output),
126        Either::Right((_, _)) => Err(TimeoutError),
127    }
128}