use futures::future::BoxFuture;
#[cfg(not(feature = "wasm"))]
use futures::task::FutureObj;
#[cfg(feature = "wasm")]
use futures::task::LocalFutureObj;
use std::{future::Future, sync::OnceLock, time::Duration};
use tracing::Span;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskCompletionResult {
Completed,
Panicked,
Cancelled,
RuntimeAborted,
}
pub type TaskCompletion = BoxFuture<'static, TaskCompletionResult>;
#[cfg(feature = "wasm")]
mod wasm;
#[cfg(all(feature = "tokio-runtime", not(feature = "wasm")))]
mod tokio;
static GLOBAL_ASYNC_MANAGER: OnceLock<Box<dyn AsyncManager>> = OnceLock::new();
pub fn set_global_async_manager(manager: Box<dyn AsyncManager>) {
GLOBAL_ASYNC_MANAGER
.set(manager)
.expect("Global async manager can only be set once.");
}
fn get_default_async_manager() -> Box<dyn AsyncManager> {
cfg_if::cfg_if! {
if #[cfg(feature = "wasm")] {
return Box::new(wasm::WasmBindgenAsyncManager::default());
} else if #[cfg(feature = "tokio-runtime")] {
Box::new(tokio::TokioAsyncManager::default())
} else {
unimplemented!(
"No async runtime configured. Enable the `tokio-runtime` or `wasm` feature, \
or call `set_global_async_manager` before performing async operations."
);
}
}
}
fn get_global_async_manager() -> &'static dyn AsyncManager {
GLOBAL_ASYNC_MANAGER
.get_or_init(|| get_default_async_manager())
.as_ref()
}
pub trait AsyncManager: std::fmt::Debug + Send + Sync {
#[cfg(not(feature = "wasm"))]
fn spawn(&self, future: FutureObj<'static, TaskCompletionResult>, span: Span) -> TaskCompletion;
#[cfg(feature = "wasm")]
fn spawn(
&self,
future: LocalFutureObj<'static, TaskCompletionResult>,
span: Span,
) -> TaskCompletion;
fn sleep(&self, duration: Duration) -> BoxFuture<'static, ()>;
}
#[cfg(not(feature = "wasm"))]
pub fn spawn<F>(future: F, span: Span) -> TaskCompletion
where
F: Future<Output = ()> + Send + 'static,
{
spawn_with_result(
async move {
future.await;
TaskCompletionResult::Completed
},
span,
)
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn spawn_with_result<F>(future: F, span: Span) -> TaskCompletion
where
F: Future<Output = TaskCompletionResult> + Send + 'static,
{
get_global_async_manager().spawn(FutureObj::new(Box::new(future)), span)
}
#[cfg(feature = "wasm")]
pub fn spawn<F>(future: F, span: Span) -> TaskCompletion
where
F: Future<Output = ()> + 'static,
{
spawn_with_result(
async move {
future.await;
TaskCompletionResult::Completed
},
span,
)
}
#[cfg(feature = "wasm")]
pub(crate) fn spawn_with_result<F>(future: F, span: Span) -> TaskCompletion
where
F: Future<Output = TaskCompletionResult> + 'static,
{
get_global_async_manager().spawn(LocalFutureObj::new(Box::new(future)), span)
}
pub async fn sleep(duration: Duration) {
get_global_async_manager().sleep(duration).await;
}
#[macro_export]
macro_rules! spawn {
($future:expr) => {{
let _ = $crate::util::async_manager::spawn(
$future,
tracing::span!(tracing::Level::INFO, "Buttplug Async Task"),
);
}};
($name:expr, $future:expr) => {{
let _ =
$crate::util::async_manager::spawn($future, tracing::span!(tracing::Level::INFO, $name));
}};
}