pub mod file;
pub mod http;
pub mod jorm;
pub mod python;
pub mod rust;
pub mod shell;
#[cfg(test)]
mod tests;
use crate::core::engine::{ExecutionResult, TaskResult};
use crate::core::{dag::Dag, error::JormError, task::TaskType};
use file::FileExecutor;
use http::HttpExecutor;
use jorm::JormExecutor;
use python::PythonExecutor;
use rust::RustExecutor;
use shell::ShellExecutor;
use std::collections::{HashMap, HashSet, VecDeque};
pub struct TaskExecutor {
shell_executor: ShellExecutor,
http_executor: HttpExecutor,
python_executor: PythonExecutor,
rust_executor: RustExecutor,
file_executor: FileExecutor,
jorm_executor: JormExecutor,
}
impl Default for TaskExecutor {
fn default() -> Self {
Self::new()
}
}
impl TaskExecutor {
pub fn new() -> Self {
Self {
shell_executor: ShellExecutor::new(),
http_executor: HttpExecutor::new(),
python_executor: PythonExecutor::new(),
rust_executor: RustExecutor::new(),
file_executor: FileExecutor::new(),
jorm_executor: JormExecutor::new(),
}
}
pub async fn execute_dag(&self, dag: &Dag) -> Result<ExecutionResult, JormError> {
let execution_order = self.resolve_execution_order(dag)?;
let mut task_results = Vec::new();
let mut overall_success = true;
for task_name in execution_order {
if let Some(task) = dag.tasks.get(&task_name) {
println!("Executing task: {}", task_name);
match self.execute_task(&task_name, &task.task_type).await {
Ok(result) => {
if !result.success {
overall_success = false;
println!(
"Task '{}' failed: {}",
task_name,
result.error.as_deref().unwrap_or("Unknown error")
);
} else {
println!("Task '{}' completed successfully", task_name);
}
task_results.push(result);
}
Err(e) => {
overall_success = false;
let error_msg = format!("Task execution error: {}", e);
println!("Task '{}' failed with error: {}", task_name, error_msg);
task_results.push(TaskResult {
task_name: task_name.clone(),
success: false,
output: String::new(),
error: Some(error_msg),
});
}
}
}
}
let message = if overall_success {
format!("DAG '{}' executed successfully", dag.name)
} else {
format!("DAG '{}' completed with some failures", dag.name)
};
Ok(ExecutionResult {
success: overall_success,
message,
task_results,
})
}
fn resolve_execution_order(&self, dag: &Dag) -> Result<Vec<String>, JormError> {
let mut in_degree = HashMap::new();
let mut graph = HashMap::new();
for task_name in dag.tasks.keys() {
in_degree.insert(task_name.clone(), 0);
graph.insert(task_name.clone(), Vec::new());
}
for (task_name, dependencies) in &dag.dependencies {
for dep in dependencies {
graph.get_mut(dep).unwrap().push(task_name.clone());
*in_degree.get_mut(task_name).unwrap() += 1;
}
}
let mut queue = VecDeque::new();
for (task_name, °ree) in &in_degree {
if degree == 0 {
queue.push_back(task_name.clone());
}
}
let mut execution_order = Vec::new();
let mut processed = HashSet::new();
while let Some(current_task) = queue.pop_front() {
execution_order.push(current_task.clone());
processed.insert(current_task.clone());
if let Some(dependents) = graph.get(¤t_task) {
for dependent in dependents {
if let Some(degree) = in_degree.get_mut(dependent) {
*degree -= 1;
if *degree == 0 {
queue.push_back(dependent.clone());
}
}
}
}
}
if execution_order.len() != dag.tasks.len() {
return Err(JormError::ExecutionError(
"Circular dependency detected - cannot resolve execution order".to_string(),
));
}
Ok(execution_order)
}
pub async fn execute_task(
&self,
task_name: &str,
task_type: &TaskType,
) -> Result<TaskResult, JormError> {
match task_type {
TaskType::Shell { .. } => self.shell_executor.execute(task_name, task_type).await,
TaskType::Http { .. } => self.http_executor.execute(task_name, task_type).await,
TaskType::Python { .. } => self.python_executor.execute(task_name, task_type).await,
TaskType::Rust { .. } => self.rust_executor.execute(task_name, task_type).await,
TaskType::FileCopy { .. } | TaskType::FileMove { .. } | TaskType::FileDelete { .. } => {
self.file_executor.execute(task_name, task_type).await
}
TaskType::Jorm { .. } => self.jorm_executor.execute(task_name, task_type).await,
}
}
}