use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ExecutionLog {
pub tasks: HashMap<String, TaskRecord>,
pub current_step: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskRecord {
pub status: TaskStatus,
pub result: Option<Value>,
pub error: Option<String>,
pub started_at: String,
pub completed_at: Option<String>,
pub attempt: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TaskStatus {
Running,
Completed,
Failed,
Interrupted,
}
impl ExecutionLog {
pub fn new() -> Self {
Self { tasks: HashMap::new(), current_step: 0 }
}
pub fn get_result(&self, task_id: &str) -> Option<&Value> {
self.tasks.get(task_id).and_then(|record| {
if record.status == TaskStatus::Completed { record.result.as_ref() } else { None }
})
}
pub fn is_completed(&self, task_id: &str) -> bool {
self.tasks.get(task_id).is_some_and(|record| record.status == TaskStatus::Completed)
}
pub fn record_start(&mut self, task_id: &str) {
let now = chrono::Utc::now().to_rfc3339();
let record = self.tasks.entry(task_id.to_string()).or_insert(TaskRecord {
status: TaskStatus::Running,
result: None,
error: None,
started_at: now.clone(),
completed_at: None,
attempt: 0,
});
record.status = TaskStatus::Running;
record.attempt += 1;
if record.attempt == 1 {
record.started_at = now;
}
}
pub fn record_completion(&mut self, task_id: &str, result: Value) {
let now = chrono::Utc::now().to_rfc3339();
if let Some(record) = self.tasks.get_mut(task_id) {
record.status = TaskStatus::Completed;
record.result = Some(result);
record.error = None;
record.completed_at = Some(now);
} else {
self.tasks.insert(
task_id.to_string(),
TaskRecord {
status: TaskStatus::Completed,
result: Some(result),
error: None,
started_at: now.clone(),
completed_at: Some(now),
attempt: 1,
},
);
}
}
pub fn record_failure(&mut self, task_id: &str, error: &str) {
let now = chrono::Utc::now().to_rfc3339();
if let Some(record) = self.tasks.get_mut(task_id) {
record.status = TaskStatus::Failed;
record.error = Some(error.to_string());
record.completed_at = Some(now);
} else {
self.tasks.insert(
task_id.to_string(),
TaskRecord {
status: TaskStatus::Failed,
result: None,
error: Some(error.to_string()),
started_at: now.clone(),
completed_at: Some(now),
attempt: 1,
},
);
}
}
pub fn current_step(&self) -> usize {
self.current_step
}
pub fn advance_step(&mut self) -> usize {
self.current_step += 1;
self.current_step
}
}