use tokio_util::sync::CancellationToken;
use zeph_config::security::TimeoutConfig;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TurnId(pub u64);
impl TurnId {
#[must_use]
pub fn next(self) -> TurnId {
TurnId(self.0.saturating_add(1))
}
}
impl std::fmt::Display for TurnId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone)]
pub struct TurnContext {
pub id: TurnId,
pub cancel_token: CancellationToken,
pub timeouts: TimeoutConfig,
pub tool_allowlist: Option<Vec<String>>,
}
impl TurnContext {
#[must_use]
pub fn new(id: TurnId, cancel_token: CancellationToken, timeouts: TimeoutConfig) -> Self {
Self {
id,
cancel_token,
timeouts,
tool_allowlist: None,
}
}
}
const _: () = {
fn assert_send_static<T: Send + 'static>() {}
fn check() {
assert_send_static::<TurnContext>();
assert_send_static::<TurnId>();
}
let _ = check;
};
#[cfg(test)]
mod tests {
use tokio_util::sync::CancellationToken;
use zeph_config::security::TimeoutConfig;
use super::*;
#[test]
fn turn_id_next_increments() {
assert_eq!(TurnId(3).next(), TurnId(4));
}
#[test]
fn turn_id_next_saturates_at_max() {
assert_eq!(TurnId(u64::MAX).next(), TurnId(u64::MAX));
}
#[test]
fn turn_id_display() {
assert_eq!(TurnId(42).to_string(), "42");
}
#[test]
fn turn_context_new_fields() {
let token = CancellationToken::new();
let ctx = TurnContext::new(TurnId(1), token.clone(), TimeoutConfig::default());
assert_eq!(ctx.id, TurnId(1));
assert!(ctx.tool_allowlist.is_none());
}
#[test]
fn turn_context_clone_shares_cancel_token() {
let ctx = TurnContext::new(
TurnId(0),
CancellationToken::new(),
TimeoutConfig::default(),
);
let cloned = ctx.clone();
ctx.cancel_token.cancel();
assert!(cloned.cancel_token.is_cancelled());
}
}