use crate::core::engine::TaskResult;
use crate::core::{error::JormError, task::TaskType};
use std::process::Stdio;
use tokio::process::Command;
use tokio::time::{timeout, Duration};
pub struct JormExecutor;
impl Default for JormExecutor {
fn default() -> Self {
Self::new()
}
}
impl JormExecutor {
pub fn new() -> Self {
Self
}
pub async fn execute(
&self,
task_name: &str,
task_type: &TaskType,
) -> Result<TaskResult, JormError> {
match task_type {
TaskType::Jorm {
command,
args,
working_dir,
} => {
self.execute_jorm_command(task_name, command, args.as_ref(), working_dir.as_deref())
.await
}
_ => Err(JormError::ExecutionError(format!(
"Jorm executor cannot handle task type: {:?}",
task_type
))),
}
}
async fn execute_jorm_command(
&self,
task_name: &str,
command: &str,
args: Option<&Vec<String>>,
working_dir: Option<&str>,
) -> Result<TaskResult, JormError> {
if command.trim().is_empty() {
return Err(JormError::ExecutionError("Empty jorm command".to_string()));
}
let current_exe = std::env::current_exe().map_err(|e| {
JormError::ExecutionError(format!("Failed to get current executable path: {}", e))
})?;
let mut cmd = Command::new(¤t_exe);
cmd.arg(command);
if let Some(args_vec) = args {
cmd.args(args_vec);
}
cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
if let Some(dir) = working_dir {
cmd.current_dir(dir);
}
let timeout_duration = Duration::from_secs(300);
match timeout(timeout_duration, cmd.output()).await {
Ok(Ok(output)) => {
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
let success = output.status.success();
let error = if !stderr.is_empty() && !success {
Some(stderr)
} else {
None
};
Ok(TaskResult {
task_name: task_name.to_string(),
success,
output: stdout,
error,
})
}
Ok(Err(e)) => Err(JormError::ExecutionError(format!(
"Failed to execute jorm command '{}': {}",
command, e
))),
Err(_) => Err(JormError::ExecutionError(format!(
"Jorm command '{}' timed out after {} seconds",
command,
timeout_duration.as_secs()
))),
}
}
}