use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "PascalCase")]
pub enum ExecutionState {
#[default]
Created,
Planning,
Running,
Paused,
Waiting(WaitReason),
Completed,
Failed,
Cancelled,
}
impl ExecutionState {
pub fn is_terminal(&self) -> bool {
matches!(self, Self::Completed | Self::Failed | Self::Cancelled)
}
pub fn can_resume(&self) -> bool {
matches!(self, Self::Paused | Self::Waiting(_))
}
pub fn is_active(&self) -> bool {
matches!(self, Self::Running | Self::Waiting(_))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WaitReason {
Approval,
UserInput,
RateLimit,
External,
Checkpoint,
CostThreshold,
MemoryPressure,
SubAgent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "PascalCase")]
pub enum StepState {
#[default]
Pending,
Running,
Completed,
Failed,
Skipped,
}
impl StepState {
pub fn is_terminal(&self) -> bool {
matches!(self, Self::Completed | Self::Failed | Self::Skipped)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_execution_state_terminal() {
assert!(!ExecutionState::Created.is_terminal());
assert!(!ExecutionState::Planning.is_terminal());
assert!(!ExecutionState::Running.is_terminal());
assert!(!ExecutionState::Paused.is_terminal());
assert!(ExecutionState::Completed.is_terminal());
assert!(ExecutionState::Failed.is_terminal());
assert!(ExecutionState::Cancelled.is_terminal());
}
#[test]
fn test_execution_state_can_resume() {
assert!(!ExecutionState::Created.can_resume());
assert!(!ExecutionState::Planning.can_resume());
assert!(!ExecutionState::Running.can_resume());
assert!(ExecutionState::Paused.can_resume());
assert!(ExecutionState::Waiting(WaitReason::Approval).can_resume());
}
#[test]
fn test_execution_state_is_active() {
assert!(!ExecutionState::Created.is_active());
assert!(!ExecutionState::Planning.is_active());
assert!(ExecutionState::Running.is_active());
assert!(!ExecutionState::Paused.is_active());
assert!(ExecutionState::Waiting(WaitReason::Approval).is_active());
assert!(ExecutionState::Waiting(WaitReason::External).is_active());
assert!(!ExecutionState::Completed.is_active());
assert!(!ExecutionState::Failed.is_active());
assert!(!ExecutionState::Cancelled.is_active());
}
#[test]
fn test_execution_state_serde() {
let states = vec![
ExecutionState::Created,
ExecutionState::Planning,
ExecutionState::Running,
ExecutionState::Paused,
ExecutionState::Waiting(WaitReason::Approval),
ExecutionState::Waiting(WaitReason::UserInput),
ExecutionState::Waiting(WaitReason::RateLimit),
ExecutionState::Waiting(WaitReason::External),
ExecutionState::Waiting(WaitReason::Checkpoint),
ExecutionState::Waiting(WaitReason::CostThreshold),
ExecutionState::Waiting(WaitReason::MemoryPressure),
ExecutionState::Waiting(WaitReason::SubAgent),
ExecutionState::Completed,
ExecutionState::Failed,
ExecutionState::Cancelled,
];
for state in states {
let json = serde_json::to_string(&state).unwrap();
let parsed: ExecutionState = serde_json::from_str(&json).unwrap();
assert_eq!(state, parsed);
}
}
#[test]
fn test_step_state_terminal() {
assert!(!StepState::Pending.is_terminal());
assert!(!StepState::Running.is_terminal());
assert!(StepState::Completed.is_terminal());
assert!(StepState::Failed.is_terminal());
assert!(StepState::Skipped.is_terminal());
}
#[test]
fn test_step_state_serde() {
let states = vec![
StepState::Pending,
StepState::Running,
StepState::Completed,
StepState::Failed,
StepState::Skipped,
];
for state in states {
let json = serde_json::to_string(&state).unwrap();
let parsed: StepState = serde_json::from_str(&json).unwrap();
assert_eq!(state, parsed);
}
}
}