ricecoder_execution/
manager.rs1use crate::error::{ExecutionError, ExecutionResult};
4use crate::models::{ExecutionMode, ExecutionPlan, ExecutionState};
5use crate::progress_tracker::ProgressTracker;
6use chrono::Utc;
7use std::collections::HashMap;
8use uuid::Uuid;
9
10pub struct ExecutionManager {
16 active_executions: HashMap<String, ExecutionState>,
18 plans: HashMap<String, ExecutionPlan>,
20 progress_trackers: HashMap<String, ProgressTracker>,
22}
23
24impl Default for ExecutionManager {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl ExecutionManager {
31 pub fn new() -> Self {
33 ExecutionManager {
34 active_executions: HashMap::new(),
35 plans: HashMap::new(),
36 progress_trackers: HashMap::new(),
37 }
38 }
39
40 pub fn register_plan(&mut self, plan: ExecutionPlan) -> ExecutionResult<String> {
44 let plan_id = plan.id.clone();
45 self.plans.insert(plan_id.clone(), plan);
46 Ok(plan_id)
47 }
48
49 pub fn get_plan(&self, plan_id: &str) -> ExecutionResult<ExecutionPlan> {
51 self.plans
52 .get(plan_id)
53 .cloned()
54 .ok_or_else(|| ExecutionError::PlanError(format!("Plan not found: {}", plan_id)))
55 }
56
57 pub fn start_execution(
62 &mut self,
63 plan_id: &str,
64 mode: ExecutionMode,
65 ) -> ExecutionResult<String> {
66 let plan = self.get_plan(plan_id)?;
67
68 let execution_id = Uuid::new_v4().to_string();
69 let state = ExecutionState {
70 execution_id: execution_id.clone(),
71 current_step_index: 0,
72 completed_steps: Vec::new(),
73 mode,
74 paused_at: Utc::now(),
75 };
76
77 let progress_tracker = ProgressTracker::new(&plan);
79
80 self.active_executions.insert(execution_id.clone(), state);
81 self.progress_trackers
82 .insert(execution_id.clone(), progress_tracker);
83
84 tracing::info!(
85 execution_id = %execution_id,
86 plan_id = %plan_id,
87 mode = ?mode,
88 "Execution started"
89 );
90
91 Ok(execution_id)
92 }
93
94 pub fn pause_execution(&mut self, execution_id: &str) -> ExecutionResult<()> {
98 let state = self
99 .active_executions
100 .get_mut(execution_id)
101 .ok_or_else(|| {
102 ExecutionError::ValidationError(format!("Execution not found: {}", execution_id))
103 })?;
104
105 state.paused_at = Utc::now();
106
107 tracing::info!(
108 execution_id = %execution_id,
109 "Execution paused"
110 );
111
112 Ok(())
113 }
114
115 pub fn resume_execution(&mut self, execution_id: &str) -> ExecutionResult<()> {
119 let state = self
120 .active_executions
121 .get_mut(execution_id)
122 .ok_or_else(|| {
123 ExecutionError::ValidationError(format!("Execution not found: {}", execution_id))
124 })?;
125
126 state.paused_at = Utc::now();
128
129 tracing::info!(
130 execution_id = %execution_id,
131 "Execution resumed"
132 );
133
134 Ok(())
135 }
136
137 pub fn cancel_execution(&mut self, execution_id: &str) -> ExecutionResult<()> {
141 self.active_executions.remove(execution_id).ok_or_else(|| {
142 ExecutionError::ValidationError(format!("Execution not found: {}", execution_id))
143 })?;
144
145 self.progress_trackers.remove(execution_id);
147
148 tracing::info!(
149 execution_id = %execution_id,
150 "Execution cancelled"
151 );
152
153 Ok(())
154 }
155
156 pub fn get_execution_state(&self, execution_id: &str) -> ExecutionResult<ExecutionState> {
158 self.active_executions
159 .get(execution_id)
160 .cloned()
161 .ok_or_else(|| {
162 ExecutionError::ValidationError(format!("Execution not found: {}", execution_id))
163 })
164 }
165
166 pub fn get_active_executions(&self) -> Vec<ExecutionState> {
168 self.active_executions.values().cloned().collect()
169 }
170
171 pub fn is_active(&self, execution_id: &str) -> bool {
173 self.active_executions.contains_key(execution_id)
174 }
175
176 #[allow(dead_code)]
178 pub(crate) fn update_execution_state(
179 &mut self,
180 execution_id: &str,
181 state: ExecutionState,
182 ) -> ExecutionResult<()> {
183 self.active_executions
184 .insert(execution_id.to_string(), state);
185 Ok(())
186 }
187
188 pub fn get_progress_tracker_mut(
192 &mut self,
193 execution_id: &str,
194 ) -> ExecutionResult<&mut ProgressTracker> {
195 self.progress_trackers.get_mut(execution_id).ok_or_else(|| {
196 ExecutionError::ValidationError(format!(
197 "Progress tracker not found for execution: {}",
198 execution_id
199 ))
200 })
201 }
202
203 pub fn get_progress_tracker(&self, execution_id: &str) -> ExecutionResult<&ProgressTracker> {
207 self.progress_trackers.get(execution_id).ok_or_else(|| {
208 ExecutionError::ValidationError(format!(
209 "Progress tracker not found for execution: {}",
210 execution_id
211 ))
212 })
213 }
214
215 pub fn on_progress<F>(&mut self, execution_id: &str, callback: F) -> ExecutionResult<()>
219 where
220 F: Fn(crate::progress_tracker::ProgressUpdate) + Send + Sync + 'static,
221 {
222 let tracker = self.get_progress_tracker_mut(execution_id)?;
223 tracker.on_progress(callback);
224 Ok(())
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn test_create_manager() {
234 let manager = ExecutionManager::new();
235 assert_eq!(manager.active_executions.len(), 0);
236 assert_eq!(manager.plans.len(), 0);
237 }
238
239 #[test]
240 fn test_register_plan() {
241 let mut manager = ExecutionManager::new();
242 let plan = ExecutionPlan::new("test".to_string(), vec![]);
243
244 let plan_id = manager.register_plan(plan.clone()).unwrap();
245 assert_eq!(plan_id, plan.id);
246 assert!(manager.get_plan(&plan_id).is_ok());
247 }
248
249 #[test]
250 fn test_start_execution() {
251 let mut manager = ExecutionManager::new();
252 let plan = ExecutionPlan::new("test".to_string(), vec![]);
253 let plan_id = manager.register_plan(plan).unwrap();
254
255 let execution_id = manager
256 .start_execution(&plan_id, ExecutionMode::Automatic)
257 .unwrap();
258
259 assert!(manager.is_active(&execution_id));
260 }
261
262 #[test]
263 fn test_pause_resume_execution() {
264 let mut manager = ExecutionManager::new();
265 let plan = ExecutionPlan::new("test".to_string(), vec![]);
266 let plan_id = manager.register_plan(plan).unwrap();
267
268 let execution_id = manager
269 .start_execution(&plan_id, ExecutionMode::Automatic)
270 .unwrap();
271
272 manager.pause_execution(&execution_id).unwrap();
273 assert!(manager.is_active(&execution_id));
274
275 manager.resume_execution(&execution_id).unwrap();
276 assert!(manager.is_active(&execution_id));
277 }
278
279 #[test]
280 fn test_cancel_execution() {
281 let mut manager = ExecutionManager::new();
282 let plan = ExecutionPlan::new("test".to_string(), vec![]);
283 let plan_id = manager.register_plan(plan).unwrap();
284
285 let execution_id = manager
286 .start_execution(&plan_id, ExecutionMode::Automatic)
287 .unwrap();
288
289 assert!(manager.is_active(&execution_id));
290 manager.cancel_execution(&execution_id).unwrap();
291 assert!(!manager.is_active(&execution_id));
292 }
293
294 #[test]
295 fn test_get_execution_state() {
296 let mut manager = ExecutionManager::new();
297 let plan = ExecutionPlan::new("test".to_string(), vec![]);
298 let plan_id = manager.register_plan(plan).unwrap();
299
300 let execution_id = manager
301 .start_execution(&plan_id, ExecutionMode::StepByStep)
302 .unwrap();
303
304 let state = manager.get_execution_state(&execution_id).unwrap();
305 assert_eq!(state.execution_id, execution_id);
306 assert_eq!(state.mode, ExecutionMode::StepByStep);
307 assert_eq!(state.current_step_index, 0);
308 }
309
310 #[test]
311 fn test_get_active_executions() {
312 let mut manager = ExecutionManager::new();
313 let plan1 = ExecutionPlan::new("test1".to_string(), vec![]);
314 let plan2 = ExecutionPlan::new("test2".to_string(), vec![]);
315
316 let plan_id1 = manager.register_plan(plan1).unwrap();
317 let plan_id2 = manager.register_plan(plan2).unwrap();
318
319 let _exec_id1 = manager
320 .start_execution(&plan_id1, ExecutionMode::Automatic)
321 .unwrap();
322 let _exec_id2 = manager
323 .start_execution(&plan_id2, ExecutionMode::DryRun)
324 .unwrap();
325
326 let active = manager.get_active_executions();
327 assert_eq!(active.len(), 2);
328 }
329
330 #[test]
331 fn test_nonexistent_plan() {
332 let manager = ExecutionManager::new();
333 assert!(manager.get_plan("nonexistent").is_err());
334 }
335
336 #[test]
337 fn test_nonexistent_execution() {
338 let manager = ExecutionManager::new();
339 assert!(manager.get_execution_state("nonexistent").is_err());
340 }
341
342 #[test]
343 fn test_progress_tracker_created() {
344 let mut manager = ExecutionManager::new();
345 let plan = ExecutionPlan::new("test".to_string(), vec![]);
346 let plan_id = manager.register_plan(plan).unwrap();
347
348 let execution_id = manager
349 .start_execution(&plan_id, ExecutionMode::Automatic)
350 .unwrap();
351
352 let tracker = manager.get_progress_tracker(&execution_id);
353 assert!(tracker.is_ok());
354 }
355
356 #[test]
357 fn test_progress_tracker_cleanup() {
358 let mut manager = ExecutionManager::new();
359 let plan = ExecutionPlan::new("test".to_string(), vec![]);
360 let plan_id = manager.register_plan(plan).unwrap();
361
362 let execution_id = manager
363 .start_execution(&plan_id, ExecutionMode::Automatic)
364 .unwrap();
365
366 assert!(manager.get_progress_tracker(&execution_id).is_ok());
367
368 manager.cancel_execution(&execution_id).unwrap();
369
370 assert!(manager.get_progress_tracker(&execution_id).is_err());
371 }
372
373 #[test]
374 fn test_progress_callback_registration() {
375 let mut manager = ExecutionManager::new();
376 let plan = ExecutionPlan::new("test".to_string(), vec![]);
377 let plan_id = manager.register_plan(plan).unwrap();
378
379 let execution_id = manager
380 .start_execution(&plan_id, ExecutionMode::Automatic)
381 .unwrap();
382
383 let result = manager.on_progress(&execution_id, |_progress| {
384 });
386
387 assert!(result.is_ok());
388 }
389}