//! Shell 执行器 — 通过 tokio::process 执行 shell 命令 use serde::{Deserialize, Serialize}; use std::process::Stdio; /// Shell 命令执行结果 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ShellResult { /// 标准输出 pub stdout: String, /// 标准错误 pub stderr: String, /// 退出码 pub exit_code: Option, /// 执行耗时(毫秒) pub duration_ms: u64, } /// Shell 命令执行请求 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ShellRequest { /// 要执行的命令 pub command: String, /// 工作目录 pub working_dir: Option, /// 环境变量 pub env: std::collections::HashMap, /// 超时时间(秒),None 表示不超时 pub timeout_secs: Option, } /// 执行 Shell 命令 /// /// TODO: 完整实现,支持超时、环境变量、工作目录等 pub async fn execute(request: ShellRequest) -> anyhow::Result { let start = std::time::Instant::now(); let mut cmd = if cfg!(windows) { let mut c = tokio::process::Command::new("cmd"); c.arg("/C").arg(&request.command); c.stdout(Stdio::piped()).stderr(Stdio::piped()); c } else { let mut c = tokio::process::Command::new("sh"); c.arg("-c").arg(&request.command); c.stdout(Stdio::piped()).stderr(Stdio::piped()); c }; if let Some(dir) = &request.working_dir { cmd.current_dir(dir); } for (key, value) in &request.env { cmd.env(key, value); } let output = match request.timeout_secs { Some(secs) => tokio::time::timeout( std::time::Duration::from_secs(secs), cmd.output(), ) .await .map_err(|_| anyhow::anyhow!("命令执行超时: {}秒", secs))??, None => cmd.output().await?, }; let duration = start.elapsed().as_millis() as u64; Ok(ShellResult { stdout: String::from_utf8_lossy(&output.stdout).to_string(), stderr: String::from_utf8_lossy(&output.stderr).to_string(), exit_code: output.status.code(), duration_ms: duration, }) }