command_manager/
lib.rs

1use std::{
2    io::{self, Error, ErrorKind},
3    process::Command,
4};
5
6#[derive(Debug, Clone)]
7pub struct Output {
8    pub stdout: String,
9    pub stderr: String,
10}
11
12/// Runs shell command(s) and returns the output.
13#[allow(dead_code)]
14pub fn run(cmd: &str) -> io::Result<Output> {
15    log::info!("running: {cmd}");
16    match Command::new("sh").args(&["-c", cmd]).output() {
17        Ok(o) => {
18            let stdout = String::from_utf8_lossy(&o.stdout).as_ref().to_owned();
19            let stderr = String::from_utf8_lossy(&o.stderr).as_ref().to_owned();
20
21            if o.status.success() {
22                return Ok(Output { stdout, stderr });
23            }
24
25            match o.status.code() {
26                Some(code) => {
27                    log::warn!("command failed with status code {}: {}", code, stderr);
28                    Err(Error::new(
29                        ErrorKind::Other,
30                        format!("command failed with status code {}: {}", code, stderr),
31                    ))
32                }
33                None => {
34                    log::warn!(
35                        "command terminated by signal with no status code: {}",
36                        stderr
37                    );
38                    Err(Error::new(
39                        ErrorKind::Other,
40                        format!(
41                            "command terminated by signal with no status code: {}",
42                            stderr
43                        ),
44                    ))
45                }
46            }
47        }
48        Err(e) => {
49            log::warn!("command failed: {}", e);
50            Err(e)
51        }
52    }
53}
54
55#[test]
56fn test_bash_run() {
57    let output = run("ls -lah .").unwrap();
58    println!("stdout:\n{}", output.stdout);
59    println!("stderr:\n{}", output.stderr);
60}