use serde::{Deserialize, Serialize};
use crate::error::Error;
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum InterruptionPhase {
Before,
After,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum InterruptionKind {
Custom,
ApprovalPending {
tool_use_id: String,
},
ScheduledPause {
phase: InterruptionPhase,
node: String,
},
}
pub fn interrupt<T>(payload: serde_json::Value) -> Result<T, Error> {
Err(Error::Interrupted {
kind: InterruptionKind::Custom,
payload,
})
}
pub fn interrupt_with<T>(kind: InterruptionKind, payload: serde_json::Value) -> Result<T, Error> {
Err(Error::Interrupted { kind, payload })
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn interrupt_returns_custom_kind() {
let err = interrupt::<()>(json!({"need": "review"})).unwrap_err();
match err {
Error::Interrupted { kind, payload } => {
assert!(matches!(kind, InterruptionKind::Custom));
assert_eq!(payload, json!({"need": "review"}));
}
other => panic!("expected Interrupted, got {other:?}"),
}
}
#[test]
fn interrupt_with_carries_typed_kind() {
let err = interrupt_with::<()>(
InterruptionKind::ApprovalPending {
tool_use_id: "tu-1".into(),
},
serde_json::Value::Null,
)
.unwrap_err();
match err {
Error::Interrupted { kind, .. } => {
assert_eq!(
kind,
InterruptionKind::ApprovalPending {
tool_use_id: "tu-1".into()
}
);
}
other => panic!("expected Interrupted, got {other:?}"),
}
}
#[test]
fn interruption_kind_serializes_with_typed_tag() {
let custom = serde_json::to_value(&InterruptionKind::Custom).unwrap();
assert_eq!(custom, json!({"type": "custom"}));
let approval = serde_json::to_value(&InterruptionKind::ApprovalPending {
tool_use_id: "tu-1".into(),
})
.unwrap();
assert_eq!(
approval,
json!({"type": "approval_pending", "tool_use_id": "tu-1"})
);
}
}