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 PythonExecutor;
impl Default for PythonExecutor {
fn default() -> Self {
Self::new()
}
}
impl PythonExecutor {
pub fn new() -> Self {
Self
}
pub async fn execute(
&self,
task_name: &str,
task_type: &TaskType,
) -> Result<TaskResult, JormError> {
match task_type {
TaskType::Python {
script,
args,
working_dir,
} => {
self.execute_python_script(task_name, script, args.as_ref(), working_dir.as_deref())
.await
}
_ => Err(JormError::ExecutionError(format!(
"Python executor cannot handle task type: {:?}",
task_type
))),
}
}
async fn execute_python_script(
&self,
task_name: &str,
script: &str,
args: Option<&Vec<String>>,
working_dir: Option<&str>,
) -> Result<TaskResult, JormError> {
let mut cmd = Command::new("python3");
cmd.arg(script)
.stdout(Stdio::piped())
.stderr(Stdio::piped());
if let Some(script_args) = args {
cmd.args(script_args);
}
if let Some(dir) = working_dir {
cmd.current_dir(dir);
}
let timeout_duration = Duration::from_secs(60);
let result = timeout(timeout_duration, cmd.output()).await;
let output = match result {
Ok(Ok(output)) => output,
Ok(Err(_)) | Err(_) => {
let mut cmd = Command::new("python");
cmd.arg(script)
.stdout(Stdio::piped())
.stderr(Stdio::piped());
if let Some(script_args) = args {
cmd.args(script_args);
}
if let Some(dir) = working_dir {
cmd.current_dir(dir);
}
match timeout(timeout_duration, cmd.output()).await {
Ok(Ok(output)) => output,
Ok(Err(e)) => {
return Err(JormError::ExecutionError(format!(
"Failed to execute Python script '{}': {}",
script, e
)));
}
Err(_) => {
return Err(JormError::ExecutionError(format!(
"Python script '{}' timed out after {} seconds",
script,
timeout_duration.as_secs()
)));
}
}
}
};
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,
})
}
}