1use crate::core::engine::TaskResult;
2use crate::core::{error::JormError, task::TaskType};
3use std::process::Stdio;
4use tokio::process::Command;
5use tokio::time::{timeout, Duration};
6
7pub struct PythonExecutor;
8
9impl Default for PythonExecutor {
10 fn default() -> Self {
11 Self::new()
12 }
13}
14
15impl PythonExecutor {
16 pub fn new() -> Self {
17 Self
18 }
19
20 pub async fn execute(
21 &self,
22 task_name: &str,
23 task_type: &TaskType,
24 ) -> Result<TaskResult, JormError> {
25 match task_type {
26 TaskType::Python {
27 script,
28 args,
29 working_dir,
30 } => {
31 self.execute_python_script(task_name, script, args.as_ref(), working_dir.as_deref())
32 .await
33 }
34 _ => Err(JormError::ExecutionError(format!(
35 "Python executor cannot handle task type: {:?}",
36 task_type
37 ))),
38 }
39 }
40
41 async fn execute_python_script(
42 &self,
43 task_name: &str,
44 script: &str,
45 args: Option<&Vec<String>>,
46 working_dir: Option<&str>,
47 ) -> Result<TaskResult, JormError> {
48 let mut cmd = Command::new("python3");
50 cmd.arg(script)
51 .stdout(Stdio::piped())
52 .stderr(Stdio::piped());
53
54 if let Some(script_args) = args {
56 cmd.args(script_args);
57 }
58
59 if let Some(dir) = working_dir {
61 cmd.current_dir(dir);
62 }
63
64 let timeout_duration = Duration::from_secs(60); let result = timeout(timeout_duration, cmd.output()).await;
68
69 let output = match result {
71 Ok(Ok(output)) => output,
72 Ok(Err(_)) | Err(_) => {
73 let mut cmd = Command::new("python");
75 cmd.arg(script)
76 .stdout(Stdio::piped())
77 .stderr(Stdio::piped());
78
79 if let Some(script_args) = args {
80 cmd.args(script_args);
81 }
82
83 if let Some(dir) = working_dir {
84 cmd.current_dir(dir);
85 }
86
87 match timeout(timeout_duration, cmd.output()).await {
88 Ok(Ok(output)) => output,
89 Ok(Err(e)) => {
90 return Err(JormError::ExecutionError(format!(
91 "Failed to execute Python script '{}': {}",
92 script, e
93 )));
94 }
95 Err(_) => {
96 return Err(JormError::ExecutionError(format!(
97 "Python script '{}' timed out after {} seconds",
98 script,
99 timeout_duration.as_secs()
100 )));
101 }
102 }
103 }
104 };
105
106 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
107 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
108
109 let success = output.status.success();
110 let error = if !stderr.is_empty() && !success {
111 Some(stderr)
112 } else {
113 None
114 };
115
116 Ok(TaskResult {
117 task_name: task_name.to_string(),
118 success,
119 output: stdout,
120 error,
121 })
122 }
123}