Skip to main content

smcp_computer/inputs/
utils.rs

1/*!
2* 文件名: utils
3* 作者: JQQ
4* 创建日期: 2025/12/16
5* 最后修改日期: 2025/12/16
6* 版权: 2023 JQQ. All rights reserved.
7* 依赖: std::process::Command
8* 描述: 输入处理相关的工具函数
9*/
10
11use crate::errors::ComputerError;
12use std::process::Command;
13
14/// 执行 shell 命令并返回输出 / Execute shell command and return output
15///
16/// # Arguments
17///
18/// * `command` - 要执行的命令 / Command to execute
19/// * `args` - 命令参数 / Command arguments
20///
21/// # Returns
22///
23/// 返回命令的标准输出(去除首尾空白) / Returns command stdout (trimmed)
24///
25/// # Examples
26///
27/// ```
28/// use smcp_computer::inputs::utils::run_command;
29///
30/// #[tokio::main]
31/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
32///     let output = run_command("echo", &["hello".to_string()]).await?;
33///     assert_eq!(output, "hello");
34///     Ok(())
35/// }
36/// ```
37pub async fn run_command(command: &str, args: &[String]) -> Result<String, ComputerError> {
38    let output = if cfg!(target_os = "windows") {
39        // Windows: Use cmd /C for shell mode
40        let mut cmd = Command::new("cmd");
41        cmd.arg("/C");
42        cmd.arg(command);
43        for arg in args {
44            cmd.arg(arg);
45        }
46        cmd.output()
47    } else {
48        // Unix: Use sh -c for shell mode
49        let mut cmd = Command::new("sh");
50        cmd.arg("-c");
51        // Combine command and args into a single shell string
52        let shell_cmd = if args.is_empty() {
53            command.to_string()
54        } else {
55            format!("{} {}", command, args.join(" "))
56        };
57        cmd.arg(&shell_cmd);
58        cmd.output()
59    }
60    .map_err(|e| ComputerError::RuntimeError(format!("Command execution failed: {}", e)))?;
61
62    if !output.status.success() {
63        let stderr = String::from_utf8_lossy(&output.stderr);
64        return Err(ComputerError::RuntimeError(format!(
65            "Command failed with exit code {}: {}",
66            output.status.code().unwrap_or(-1),
67            stderr
68        )));
69    }
70
71    Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[tokio::test]
79    async fn test_run_command_simple() {
80        let output = run_command("echo", &["hello".to_string()]).await.unwrap();
81        assert_eq!(output, "hello");
82    }
83
84    #[tokio::test]
85    async fn test_run_command_no_args() {
86        let output = run_command("pwd", &[]).await.unwrap();
87        // pwd should return something (current directory)
88        assert!(!output.is_empty());
89    }
90
91    #[tokio::test]
92    async fn test_run_command_failure() {
93        let result = run_command("nonexistent_command", &[]).await;
94        assert!(result.is_err());
95    }
96}