use super::execution_model::Execution;
use super::ids::ExecutionId;
use super::reducer::{reduce, ExecutionAction, ReducerError};
pub fn replay(
execution_id: ExecutionId,
actions: Vec<ExecutionAction>,
schema_version: Option<&str>,
) -> Result<Execution, ReplayError> {
let mut execution = Execution::with_id(execution_id);
if let Some(version) = schema_version {
execution.schema_version = Some(version.to_string());
}
for (index, action) in actions.into_iter().enumerate() {
reduce(&mut execution, action)
.map_err(|e| ReplayError::ReducerError { index, error: e })?;
}
Ok(execution)
}
#[derive(Debug, thiserror::Error)]
pub enum ReplayError {
#[error("Reducer error at action {index}: {error}")]
ReducerError { index: usize, error: ReducerError },
#[error("Schema version mismatch: expected {expected}, got {actual}")]
SchemaVersionMismatch { expected: String, actual: String },
}
#[derive(Debug, Default)]
pub struct EventLog {
actions: Vec<ExecutionAction>,
}
impl EventLog {
pub fn new() -> Self {
Self::default()
}
pub fn append(&mut self, action: ExecutionAction) {
self.actions.push(action);
}
pub fn actions(&self) -> &[ExecutionAction] {
&self.actions
}
pub fn into_actions(self) -> Vec<ExecutionAction> {
self.actions
}
pub fn len(&self) -> usize {
self.actions.len()
}
pub fn is_empty(&self) -> bool {
self.actions.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::super::ids::StepType;
use super::*;
#[test]
fn test_replay_simple_execution() {
let exec_id = ExecutionId::new();
let actions = vec![
ExecutionAction::Start,
ExecutionAction::Complete {
output: Some("done".into()),
},
];
let execution = replay(exec_id.clone(), actions, None).unwrap();
assert_eq!(execution.id.as_str(), exec_id.as_str());
assert!(execution.state.is_terminal());
assert_eq!(execution.output, Some("done".to_string()));
}
#[test]
fn test_replay_with_steps() {
let exec_id = ExecutionId::new();
let step_id = super::super::ids::StepId::new();
let actions = vec![
ExecutionAction::Start,
ExecutionAction::StepStarted {
step_id: step_id.clone(),
parent_step_id: None,
step_type: StepType::LlmNode,
name: "test step".into(),
source: None,
},
ExecutionAction::StepCompleted {
step_id: step_id.clone(),
output: Some("step output".into()),
duration_ms: 100,
},
ExecutionAction::Complete {
output: Some("done".into()),
},
];
let execution = replay(exec_id, actions, None).unwrap();
assert_eq!(execution.steps.len(), 1);
let step = execution.get_step(&step_id).unwrap();
assert_eq!(step.output, Some("step output".to_string()));
}
}