taskflow_rs/executor/handlers/
shell.rs1use async_trait::async_trait;
2use std::collections::HashMap;
3
4use crate::TaskResult;
5use crate::error::{Result, TaskFlowError};
6use crate::task::{Task, TaskHandler};
7
8pub struct ShellTaskHandler;
9
10impl ShellTaskHandler {
11 pub fn new() -> Self {
12 Self
13 }
14}
15
16#[async_trait]
17impl TaskHandler for ShellTaskHandler {
18 async fn execute(&self, task: &Task) -> Result<TaskResult> {
19 let start_time = std::time::Instant::now();
20
21 let command = task
22 .definition
23 .payload
24 .get("command")
25 .and_then(|v| v.as_str())
26 .ok_or_else(|| {
27 TaskFlowError::InvalidConfiguration("Missing 'command' in payload".to_string())
28 })?;
29
30 let args: Vec<&str> = task
31 .definition
32 .payload
33 .get("args")
34 .and_then(|v| v.as_array())
35 .map(|arr| arr.iter().filter_map(|v| v.as_str()).collect())
36 .unwrap_or_default();
37
38 let output = tokio::process::Command::new(command)
39 .args(&args)
40 .output()
41 .await
42 .map_err(|e| TaskFlowError::ExecutionError(e.to_string()))?;
43
44 let execution_time = start_time.elapsed().as_millis() as u64;
45 let success = output.status.success();
46
47 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
48 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
49
50 let mut metadata = HashMap::new();
51 metadata.insert(
52 "exit_code".to_string(),
53 output.status.code().unwrap_or(-1).to_string(),
54 );
55
56 Ok(TaskResult {
57 success,
58 output: Some(stdout),
59 error: if success { None } else { Some(stderr) },
60 execution_time_ms: execution_time,
61 metadata,
62 })
63 }
64
65 fn task_type(&self) -> &str {
66 "shell_command"
67 }
68}