Skip to main content

agent_line/tools/
command.rs

1use std::process::Command;
2
3use crate::agent::StepError;
4
5/// Output from a shell command.
6pub struct CmdOutput {
7    /// Whether the command exited with status 0.
8    pub success: bool,
9    /// Standard output.
10    pub stdout: String,
11    /// Standard error.
12    pub stderr: String,
13}
14
15/// Run a shell command via `sh -c`.
16pub fn run_cmd(cmd: &str) -> Result<CmdOutput, StepError> {
17    let output = Command::new("sh").arg("-c").arg(cmd).output()?;
18
19    Ok(CmdOutput {
20        success: output.status.success(),
21        stdout: String::from_utf8_lossy(&output.stdout).to_string(),
22        stderr: String::from_utf8_lossy(&output.stderr).to_string(),
23    })
24}
25
26/// Run a shell command via `sh -c` in a specific directory.
27pub fn run_cmd_in_dir(dir_name: &str, cmd: &str) -> Result<CmdOutput, StepError> {
28    let output = Command::new("sh")
29        .arg("-c")
30        .arg(cmd)
31        .current_dir(dir_name)
32        .output()?;
33
34    Ok(CmdOutput {
35        success: output.status.success(),
36        stdout: String::from_utf8_lossy(&output.stdout).to_string(),
37        stderr: String::from_utf8_lossy(&output.stderr).to_string(),
38    })
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn test_run_cmd_in_dir_uses_directory() {
47        let output = run_cmd_in_dir("/tmp", "pwd").unwrap();
48        assert!(output.success);
49        // On macOS /tmp symlinks to /private/tmp
50        let pwd = output.stdout.trim();
51        assert!(pwd == "/tmp" || pwd == "/private/tmp");
52    }
53
54    #[test]
55    fn test_run_cmd_in_dir_nonexistent_dir() {
56        let result = run_cmd_in_dir("/nonexistent_dir_xyz_abc", "ls");
57        assert!(result.is_err());
58    }
59
60    #[test]
61    fn test_run_cmd_in_dir_runs_command() {
62        let output = run_cmd_in_dir("/tmp", "echo hello").unwrap();
63        assert!(output.success);
64        assert_eq!(output.stdout.trim(), "hello");
65    }
66}