algocline_core/execution/
state.rs1use serde::{Deserialize, Serialize};
9use serde_json::Value as JsonValue;
10
11use super::cancel::{CancelInfo, FailureInfo};
12use super::pause::PauseInfo;
13use crate::TokenUsage;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(tag = "state", rename_all = "snake_case")]
21pub enum ExecutionState {
22 Running,
24 Paused(PauseInfo),
26 Done(ExecutionResult),
28 Cancelled(CancelInfo),
30 Failed(FailureInfo),
32}
33
34impl ExecutionState {
35 pub fn tag(&self) -> ExecutionStateTag {
37 ExecutionStateTag::from(self)
38 }
39
40 pub fn is_terminal(&self) -> bool {
42 matches!(self, Self::Done(_) | Self::Cancelled(_) | Self::Failed(_))
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(rename_all = "snake_case")]
52pub enum ExecutionStateTag {
53 Running,
55 Paused,
57 Done,
59 Cancelled,
61 Failed,
63}
64
65impl From<&ExecutionState> for ExecutionStateTag {
66 fn from(state: &ExecutionState) -> Self {
68 match state {
69 ExecutionState::Running => Self::Running,
70 ExecutionState::Paused(_) => Self::Paused,
71 ExecutionState::Done(_) => Self::Done,
72 ExecutionState::Cancelled(_) => Self::Cancelled,
73 ExecutionState::Failed(_) => Self::Failed,
74 }
75 }
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct ExecutionResult {
85 pub value: JsonValue,
87 pub usage: Option<TokenUsage>,
89 pub finished_at: i64,
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use crate::execution::cancel::{CancelCode, CancelInfo, CancelReason};
97 use crate::execution::pause::{PauseInfo, PauseKind};
98
99 fn make_pause_info() -> PauseInfo {
100 PauseInfo {
101 kind: PauseKind::Single,
102 prompts: vec![],
103 paused_at: 0,
104 }
105 }
106
107 fn make_cancel_info() -> CancelInfo {
108 CancelInfo {
109 reason: CancelReason {
110 code: CancelCode::User,
111 detail: None,
112 requested_at: 0,
113 },
114 observed_at: 0,
115 state_before: Box::new(ExecutionState::Running),
116 }
117 }
118
119 fn make_failure_info() -> FailureInfo {
120 crate::execution::cancel::FailureInfo {
121 message: "test".into(),
122 kind: crate::execution::cancel::FailureKind::Other,
123 occurred_at: 0,
124 }
125 }
126
127 #[test]
128 fn execution_state_tag_from_state() {
129 assert_eq!(
130 ExecutionStateTag::from(&ExecutionState::Running),
131 ExecutionStateTag::Running
132 );
133 assert_eq!(
134 ExecutionStateTag::from(&ExecutionState::Paused(make_pause_info())),
135 ExecutionStateTag::Paused
136 );
137 assert_eq!(
138 ExecutionStateTag::from(&ExecutionState::Done(ExecutionResult {
139 value: serde_json::Value::Null,
140 usage: None,
141 finished_at: 0,
142 })),
143 ExecutionStateTag::Done
144 );
145 assert_eq!(
146 ExecutionStateTag::from(&ExecutionState::Cancelled(make_cancel_info())),
147 ExecutionStateTag::Cancelled
148 );
149 assert_eq!(
150 ExecutionStateTag::from(&ExecutionState::Failed(make_failure_info())),
151 ExecutionStateTag::Failed
152 );
153 }
154
155 #[test]
156 fn execution_state_serde_roundtrip() {
157 let states: Vec<ExecutionState> = vec![
158 ExecutionState::Running,
159 ExecutionState::Paused(make_pause_info()),
160 ExecutionState::Done(ExecutionResult {
161 value: serde_json::json!({"ok": true}),
162 usage: None,
163 finished_at: 1_700_000_000_000,
164 }),
165 ExecutionState::Cancelled(make_cancel_info()),
166 ExecutionState::Failed(make_failure_info()),
167 ];
168
169 for state in states {
170 let json = serde_json::to_string(&state).expect("serialize");
171 let _: ExecutionState = serde_json::from_str(&json).expect("deserialize");
172 }
173 }
174}