use std::future::Future;
#[derive(Debug, Clone, Default)]
pub struct SpawnContext {
pub conv_id: Option<String>,
pub agent_id: Option<String>,
pub turn_uid: Option<String>,
pub depth: u32,
}
tokio::task_local! {
static SPAWN_CONTEXT: SpawnContext;
}
impl SpawnContext {
pub async fn scope<F>(self, fut: F) -> F::Output
where
F: Future,
{
SPAWN_CONTEXT.scope(self, fut).await
}
}
pub fn current() -> Option<SpawnContext> {
SPAWN_CONTEXT.try_with(|c| c.clone()).ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn current_is_none_outside_scope() {
assert!(current().is_none());
}
#[tokio::test]
async fn scope_installs_and_restores() {
let ctx = SpawnContext {
conv_id: Some("P".into()),
agent_id: Some("agent-P".into()),
turn_uid: Some("turn-1".into()),
depth: 2,
};
ctx.scope(async {
let seen = current().expect("context visible inside scope");
assert_eq!(seen.conv_id.as_deref(), Some("P"));
assert_eq!(seen.agent_id.as_deref(), Some("agent-P"));
assert_eq!(seen.turn_uid.as_deref(), Some("turn-1"));
assert_eq!(seen.depth, 2);
})
.await;
assert!(current().is_none());
}
}