git_cliff_core/
command.rs

1use std::io::{Error as IoError, Write};
2use std::process::{Command, Stdio};
3use std::{env, str, thread};
4
5use crate::error::Result;
6
7/// Runs the given OS command and returns the output as string.
8///
9/// Use `input` parameter to specify a text to write to stdin.
10/// Environment variables are set accordingly to `envs`.
11pub fn run(command: &str, input: Option<String>, envs: Vec<(&str, &str)>) -> Result<String> {
12    log::trace!("Running command: {:?}", command);
13    let mut child = if cfg!(target_os = "windows") {
14        Command::new("cmd")
15            .envs(envs)
16            .args(["/C", command])
17            .stdin(Stdio::piped())
18            .stdout(Stdio::piped())
19            .current_dir(env::current_dir()?)
20            .spawn()
21    } else {
22        Command::new("sh")
23            .envs(envs)
24            .args(["-c", command])
25            .stdin(Stdio::piped())
26            .stdout(Stdio::piped())
27            .current_dir(env::current_dir()?)
28            .spawn()
29    }?;
30    if let Some(input) = input {
31        let mut stdin = child
32            .stdin
33            .take()
34            .ok_or_else(|| IoError::other("stdin is not captured"))?;
35        thread::spawn(move || {
36            stdin
37                .write_all(input.as_bytes())
38                .expect("Failed to write to stdin");
39        });
40    }
41    let output = child.wait_with_output()?;
42    if output.status.success() {
43        Ok(str::from_utf8(&output.stdout)?.to_string())
44    } else {
45        for output in [output.stdout, output.stderr] {
46            let output = str::from_utf8(&output)?.to_string();
47            if !output.is_empty() {
48                log::error!("{}", output);
49            }
50        }
51        Err(IoError::other(format!("command exited with {:?}", output.status)).into())
52    }
53}
54
55#[cfg(test)]
56mod test {
57    use super::*;
58
59    #[test]
60    #[cfg(target_family = "unix")]
61    fn run_os_command() -> Result<()> {
62        assert_eq!(
63            "eroc-ffilc-tig",
64            run("echo $APP_NAME | rev", None, vec![(
65                "APP_NAME",
66                env!("CARGO_PKG_NAME")
67            )])?
68            .trim()
69        );
70        assert_eq!(
71            "eroc-ffilc-tig",
72            run("rev", Some(env!("CARGO_PKG_NAME").to_string()), vec![])?.trim()
73        );
74        assert_eq!("testing", run("echo 'testing'", None, vec![])?.trim());
75        assert!(run("some_command", None, vec![]).is_err());
76        Ok(())
77    }
78}