use std::sync::{
Arc,
atomic::{AtomicBool, Ordering},
};
#[derive(Debug, Clone)]
pub(crate) struct AgentRunCanceled;
impl std::fmt::Display for AgentRunCanceled {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("prompt canceled")
}
}
impl std::error::Error for AgentRunCanceled {}
pub(crate) fn is_run_canceled(error: &anyhow::Error) -> bool {
error.downcast_ref::<AgentRunCanceled>().is_some()
}
#[derive(Debug, Clone)]
pub(crate) struct AgentCancellationHandle {
flag: Arc<AtomicBool>,
}
impl AgentCancellationHandle {
pub(crate) fn cancel(&self) {
self.flag.store(true, Ordering::SeqCst);
}
}
#[derive(Debug, Clone, Default)]
pub(crate) struct AgentCancellation {
flags: Vec<Arc<AtomicBool>>,
}
impl AgentCancellation {
pub(crate) fn new(flag: Arc<AtomicBool>) -> Self {
Self { flags: vec![flag] }
}
pub(crate) fn child_token(&self) -> (Self, AgentCancellationHandle) {
let child_flag = Arc::new(AtomicBool::new(false));
let mut flags = self.flags.clone();
flags.push(Arc::clone(&child_flag));
(Self { flags }, AgentCancellationHandle { flag: child_flag })
}
pub(crate) fn is_canceled(&self) -> bool {
self.flags.iter().any(|flag| flag.load(Ordering::SeqCst))
}
pub(crate) fn check(&self) -> anyhow::Result<()> {
if self.is_canceled() {
Err(AgentRunCanceled.into())
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cancellation_error_preserves_display_and_downcast() {
let error: anyhow::Error = AgentRunCanceled.into();
assert_eq!(error.to_string(), "prompt canceled");
assert!(is_run_canceled(&error));
}
}