oxihuman_core/
plan_executor.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
7#[derive(Debug, Clone, PartialEq)]
8pub enum StepState {
9 Pending,
10 Running,
11 Done,
12 Failed(String),
13 Skipped,
14}
15
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct PlanStep {
19 pub name: String,
20 pub state: StepState,
21 pub duration_ms: u32,
22}
23
24#[allow(dead_code)]
25pub struct PlanExecutor {
26 steps: Vec<PlanStep>,
27 current: usize,
28 abort_on_failure: bool,
29 aborted: bool,
30}
31
32#[allow(dead_code)]
33impl PlanExecutor {
34 pub fn new(abort_on_failure: bool) -> Self {
35 Self {
36 steps: Vec::new(),
37 current: 0,
38 abort_on_failure,
39 aborted: false,
40 }
41 }
42 pub fn add_step(&mut self, name: &str) {
43 self.steps.push(PlanStep {
44 name: name.to_string(),
45 state: StepState::Pending,
46 duration_ms: 0,
47 });
48 }
49 pub fn complete_current(&mut self, duration_ms: u32) -> bool {
50 if self.current >= self.steps.len() {
51 return false;
52 }
53 self.steps[self.current].state = StepState::Done;
54 self.steps[self.current].duration_ms = duration_ms;
55 self.current += 1;
56 true
57 }
58 pub fn fail_current(&mut self, reason: &str) -> bool {
59 if self.current >= self.steps.len() {
60 return false;
61 }
62 self.steps[self.current].state = StepState::Failed(reason.to_string());
63 self.current += 1;
64 if self.abort_on_failure {
65 self.aborted = true;
66 }
67 true
68 }
69 pub fn skip_current(&mut self) -> bool {
70 if self.current >= self.steps.len() {
71 return false;
72 }
73 self.steps[self.current].state = StepState::Skipped;
74 self.current += 1;
75 true
76 }
77 pub fn is_complete(&self) -> bool {
78 !self.aborted && self.current >= self.steps.len()
79 }
80 pub fn is_aborted(&self) -> bool {
81 self.aborted
82 }
83 pub fn step_count(&self) -> usize {
84 self.steps.len()
85 }
86 pub fn done_count(&self) -> usize {
87 self.steps
88 .iter()
89 .filter(|s| s.state == StepState::Done)
90 .count()
91 }
92 pub fn failed_count(&self) -> usize {
93 self.steps
94 .iter()
95 .filter(|s| matches!(s.state, StepState::Failed(_)))
96 .count()
97 }
98 pub fn pending_count(&self) -> usize {
99 self.steps
100 .iter()
101 .filter(|s| s.state == StepState::Pending)
102 .count()
103 }
104 pub fn current_step(&self) -> Option<&PlanStep> {
105 self.steps.get(self.current)
106 }
107 pub fn steps(&self) -> &[PlanStep] {
108 &self.steps
109 }
110 pub fn total_duration_ms(&self) -> u32 {
111 self.steps.iter().map(|s| s.duration_ms).sum()
112 }
113 pub fn reset(&mut self) {
114 for s in &mut self.steps {
115 s.state = StepState::Pending;
116 s.duration_ms = 0;
117 }
118 self.current = 0;
119 self.aborted = false;
120 }
121}
122
123#[allow(dead_code)]
124pub fn new_plan_executor(abort_on_failure: bool) -> PlanExecutor {
125 PlanExecutor::new(abort_on_failure)
126}
127#[allow(dead_code)]
128pub fn pe_add_step(e: &mut PlanExecutor, name: &str) {
129 e.add_step(name);
130}
131#[allow(dead_code)]
132pub fn pe_complete(e: &mut PlanExecutor, ms: u32) -> bool {
133 e.complete_current(ms)
134}
135#[allow(dead_code)]
136pub fn pe_fail(e: &mut PlanExecutor, reason: &str) -> bool {
137 e.fail_current(reason)
138}
139#[allow(dead_code)]
140pub fn pe_skip(e: &mut PlanExecutor) -> bool {
141 e.skip_current()
142}
143#[allow(dead_code)]
144pub fn pe_is_complete(e: &PlanExecutor) -> bool {
145 e.is_complete()
146}
147#[allow(dead_code)]
148pub fn pe_is_aborted(e: &PlanExecutor) -> bool {
149 e.is_aborted()
150}
151#[allow(dead_code)]
152pub fn pe_done_count(e: &PlanExecutor) -> usize {
153 e.done_count()
154}
155#[allow(dead_code)]
156pub fn pe_failed_count(e: &PlanExecutor) -> usize {
157 e.failed_count()
158}
159#[allow(dead_code)]
160pub fn pe_step_count(e: &PlanExecutor) -> usize {
161 e.step_count()
162}
163#[allow(dead_code)]
164pub fn pe_total_ms(e: &PlanExecutor) -> u32 {
165 e.total_duration_ms()
166}
167#[allow(dead_code)]
168pub fn pe_reset(e: &mut PlanExecutor) {
169 e.reset();
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175 #[test]
176 fn test_complete_all() {
177 let mut e = new_plan_executor(true);
178 pe_add_step(&mut e, "init");
179 pe_add_step(&mut e, "build");
180 pe_complete(&mut e, 10);
181 pe_complete(&mut e, 20);
182 assert!(pe_is_complete(&e));
183 assert_eq!(pe_done_count(&e), 2);
184 }
185 #[test]
186 fn test_fail_aborts() {
187 let mut e = new_plan_executor(true);
188 pe_add_step(&mut e, "step1");
189 pe_fail(&mut e, "error");
190 assert!(pe_is_aborted(&e));
191 }
192 #[test]
193 fn test_fail_no_abort() {
194 let mut e = new_plan_executor(false);
195 pe_add_step(&mut e, "step1");
196 pe_fail(&mut e, "error");
197 assert!(!pe_is_aborted(&e));
198 assert_eq!(pe_failed_count(&e), 1);
199 }
200 #[test]
201 fn test_skip() {
202 let mut e = new_plan_executor(true);
203 pe_add_step(&mut e, "s");
204 pe_skip(&mut e);
205 assert!(pe_is_complete(&e));
206 }
207 #[test]
208 fn test_total_duration() {
209 let mut e = new_plan_executor(true);
210 pe_add_step(&mut e, "a");
211 pe_add_step(&mut e, "b");
212 pe_complete(&mut e, 100);
213 pe_complete(&mut e, 200);
214 assert_eq!(pe_total_ms(&e), 300);
215 }
216 #[test]
217 fn test_current_step() {
218 let mut e = new_plan_executor(true);
219 pe_add_step(&mut e, "first");
220 assert_eq!(e.current_step().map(|s| s.name.as_str()), Some("first"));
221 pe_complete(&mut e, 0);
222 assert!(e.current_step().is_none());
223 }
224 #[test]
225 fn test_pending_count() {
226 let mut e = new_plan_executor(true);
227 pe_add_step(&mut e, "a");
228 pe_add_step(&mut e, "b");
229 pe_add_step(&mut e, "c");
230 pe_complete(&mut e, 0);
231 assert_eq!(e.pending_count(), 2);
232 }
233 #[test]
234 fn test_reset() {
235 let mut e = new_plan_executor(true);
236 pe_add_step(&mut e, "x");
237 pe_complete(&mut e, 5);
238 pe_reset(&mut e);
239 assert_eq!(pe_done_count(&e), 0);
240 assert!(!pe_is_complete(&e));
241 }
242 #[test]
243 fn test_step_count() {
244 let mut e = new_plan_executor(false);
245 pe_add_step(&mut e, "a");
246 pe_add_step(&mut e, "b");
247 assert_eq!(pe_step_count(&e), 2);
248 }
249 #[test]
250 fn test_empty_complete_immediately() {
251 let e = new_plan_executor(true);
252 assert!(pe_is_complete(&e));
253 }
254}