use std::future::Future;
use std::sync::Arc;
use async_trait::async_trait;
#[async_trait]
pub trait NestedSpawnProxy: Send + Sync {
async fn spawn(&self, args: serde_json::Value) -> Result<serde_json::Value, String>;
}
tokio::task_local! {
static NESTED_SPAWN_PROXY: Option<Arc<dyn NestedSpawnProxy>>;
}
pub async fn with_nested_spawn_proxy<F, T>(proxy: Option<Arc<dyn NestedSpawnProxy>>, fut: F) -> T
where
F: Future<Output = T>,
{
NESTED_SPAWN_PROXY.scope(proxy, fut).await
}
pub fn current_nested_spawn_proxy() -> Option<Arc<dyn NestedSpawnProxy>> {
NESTED_SPAWN_PROXY.try_with(|p| p.clone()).ok().flatten()
}
#[cfg(test)]
mod tests {
use super::*;
struct EchoProxy;
#[async_trait]
impl NestedSpawnProxy for EchoProxy {
async fn spawn(&self, args: serde_json::Value) -> Result<serde_json::Value, String> {
Ok(serde_json::json!({ "echoed": args }))
}
}
#[tokio::test]
async fn current_proxy_is_none_outside_scope() {
assert!(current_nested_spawn_proxy().is_none());
}
#[tokio::test]
async fn scope_installs_and_clears_proxy() {
let proxy: Arc<dyn NestedSpawnProxy> = Arc::new(EchoProxy);
with_nested_spawn_proxy(Some(proxy), async {
let got = current_nested_spawn_proxy().expect("proxy installed in scope");
let out = got
.spawn(serde_json::json!({"action": "create"}))
.await
.unwrap();
assert_eq!(out["echoed"]["action"], serde_json::json!("create"));
})
.await;
assert!(current_nested_spawn_proxy().is_none());
}
}