Skip to main content

ironflow_engine/executor/
mod.rs

1//! Step executor — reconstructs operations from configs and runs them.
2//!
3//! Each step type (shell, HTTP, agent) has its own executor implementing
4//! the [`StepExecutor`] trait. The [`execute_step_config`] function dispatches
5//! to the appropriate executor based on the [`StepConfig`] variant.
6
7mod agent;
8mod http;
9mod shell;
10
11use std::future::Future;
12use std::sync::Arc;
13
14use rust_decimal::Decimal;
15use serde_json::Value;
16
17use ironflow_core::provider::AgentProvider;
18
19use crate::config::StepConfig;
20use crate::error::EngineError;
21
22pub use agent::AgentExecutor;
23pub use http::HttpExecutor;
24pub use shell::ShellExecutor;
25
26/// Result of executing a single step.
27#[derive(Debug)]
28pub struct StepOutput {
29    /// Serialized output (stdout for shell, body for http, value for agent).
30    pub output: Value,
31    /// Wall-clock duration in milliseconds.
32    pub duration_ms: u64,
33    /// Cost in USD (agent steps only).
34    pub cost_usd: Decimal,
35    /// Input token count (agent steps only).
36    pub input_tokens: Option<u64>,
37    /// Output token count (agent steps only).
38    pub output_tokens: Option<u64>,
39}
40
41/// Trait for step executors.
42///
43/// Each step type implements this trait to execute its specific operation
44/// and return a [`StepOutput`].
45pub trait StepExecutor: Send + Sync {
46    /// Execute the step and return structured output.
47    ///
48    /// # Errors
49    ///
50    /// Returns [`EngineError`] if the operation fails.
51    fn execute(
52        &self,
53        provider: &Arc<dyn AgentProvider>,
54    ) -> impl Future<Output = Result<StepOutput, EngineError>> + Send;
55}
56
57/// Execute a [`StepConfig`] and return structured output.
58///
59/// # Errors
60///
61/// Returns [`EngineError::Operation`] if the operation fails.
62///
63/// # Examples
64///
65/// ```no_run
66/// use ironflow_engine::config::{StepConfig, ShellConfig};
67/// use ironflow_engine::executor::execute_step_config;
68/// use ironflow_core::provider::AgentProvider;
69/// use ironflow_core::providers::claude::ClaudeCodeProvider;
70/// use std::sync::Arc;
71///
72/// # async fn example() -> Result<(), ironflow_engine::error::EngineError> {
73/// let provider: Arc<dyn AgentProvider> = Arc::new(ClaudeCodeProvider::new());
74/// let config = StepConfig::Shell(ShellConfig::new("echo hello"));
75/// let output = execute_step_config(&config, &provider).await?;
76/// # Ok(())
77/// # }
78/// ```
79pub async fn execute_step_config(
80    config: &StepConfig,
81    provider: &Arc<dyn AgentProvider>,
82) -> Result<StepOutput, EngineError> {
83    match config {
84        StepConfig::Shell(cfg) => ShellExecutor::new(cfg).execute(provider).await,
85        StepConfig::Http(cfg) => HttpExecutor::new(cfg).execute(provider).await,
86        StepConfig::Agent(cfg) => AgentExecutor::new(cfg).execute(provider).await,
87        StepConfig::Workflow(_) => Err(EngineError::StepConfig(
88            "workflow steps are executed by WorkflowContext, not the executor".to_string(),
89        )),
90    }
91}