use async_trait::async_trait;
use std::collections::HashMap;
use crate::TaskResult;
use crate::error::{Result, TaskFlowError};
use crate::task::{Task, TaskHandler};
pub struct ShellTaskHandler;
impl ShellTaskHandler {
pub fn new() -> Self {
Self
}
}
#[async_trait]
impl TaskHandler for ShellTaskHandler {
async fn execute(&self, task: &Task) -> Result<TaskResult> {
let start_time = std::time::Instant::now();
let command = task
.definition
.payload
.get("command")
.and_then(|v| v.as_str())
.ok_or_else(|| {
TaskFlowError::InvalidConfiguration("Missing 'command' in payload".to_string())
})?;
let args: Vec<&str> = task
.definition
.payload
.get("args")
.and_then(|v| v.as_array())
.map(|arr| arr.iter().filter_map(|v| v.as_str()).collect())
.unwrap_or_default();
let output = tokio::process::Command::new(command)
.args(&args)
.output()
.await
.map_err(|e| TaskFlowError::ExecutionError(e.to_string()))?;
let execution_time = start_time.elapsed().as_millis() as u64;
let success = output.status.success();
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
let mut metadata = HashMap::new();
metadata.insert(
"exit_code".to_string(),
output.status.code().unwrap_or(-1).to_string(),
);
Ok(TaskResult {
success,
output: Some(stdout),
error: if success { None } else { Some(stderr) },
execution_time_ms: execution_time,
metadata,
})
}
fn task_type(&self) -> &str {
"shell_command"
}
}