use super::executor::{CommandRouter, ExecutionContext};
use super::interpolation::StepInterpolator;
use super::types::determine_command_type;
use crate::cook::execution::errors::MapReduceResult;
use crate::cook::execution::mapreduce::AgentContext;
use crate::cook::workflow::variables::CommandResult as VarCommandResult;
use crate::cook::workflow::StepResult;
use crate::cook::workflow::WorkflowStep;
use std::sync::Arc;
pub struct StepExecutor {
command_router: Arc<CommandRouter>,
interpolator: Arc<StepInterpolator>,
}
impl StepExecutor {
pub fn new(command_router: Arc<CommandRouter>, interpolator: Arc<StepInterpolator>) -> Self {
Self {
command_router,
interpolator,
}
}
pub async fn execute(
&self,
step: &WorkflowStep,
context: &mut AgentContext,
) -> MapReduceResult<StepResult> {
let interpolated_step = self.interpolator.interpolate(step, context).await?;
let exec_context = build_execution_context(context);
let command_result = self
.command_router
.execute(&interpolated_step, &exec_context)
.await?;
let result: StepResult = command_result.into();
capture_output(step, &result, context).await?;
Ok(result)
}
}
fn build_execution_context(context: &AgentContext) -> ExecutionContext {
ExecutionContext {
worktree_path: context.worktree_path.clone(),
worktree_name: context.worktree_name.clone(),
item_id: context.item_id.clone(),
variables: context.variables.clone(),
captured_outputs: context.captured_outputs.clone(),
environment: std::collections::HashMap::new(),
}
}
async fn capture_output(
step: &WorkflowStep,
result: &StepResult,
context: &mut AgentContext,
) -> MapReduceResult<()> {
if let Some(capture_name) = &step.capture {
capture_with_new_format(step, result, context, capture_name).await?;
}
if step.capture_output.is_enabled() && !result.stdout.is_empty() {
capture_with_legacy_format(step, result, context)?;
}
Ok(())
}
async fn capture_with_new_format(
step: &WorkflowStep,
result: &StepResult,
context: &mut AgentContext,
capture_name: &str,
) -> MapReduceResult<()> {
let command_result = VarCommandResult {
stdout: Some(result.stdout.clone()),
stderr: Some(result.stderr.clone()),
exit_code: result.exit_code.unwrap_or(-1),
success: result.success,
duration: std::time::Duration::from_secs(0), };
let capture_format = step.capture_format.unwrap_or_default();
let capture_streams = &step.capture_streams;
context
.variable_store
.capture_command_result(
capture_name,
command_result,
capture_format,
capture_streams,
)
.await
.map_err(
|e| crate::cook::execution::errors::MapReduceError::General {
message: format!("Failed to capture command result: {}", e),
source: None,
},
)?;
context
.captured_outputs
.insert(capture_name.to_string(), result.stdout.clone());
Ok(())
}
fn capture_with_legacy_format(
step: &WorkflowStep,
result: &StepResult,
context: &mut AgentContext,
) -> MapReduceResult<()> {
let command_type = determine_command_type(step)?;
if let Some(var_name) = step.capture_output.get_variable_name(&command_type) {
context
.captured_outputs
.insert(var_name, result.stdout.clone());
}
context
.captured_outputs
.insert("CAPTURED_OUTPUT".to_string(), result.stdout.clone());
Ok(())
}