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