git_cliff_core/
command.rs

1use 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
16/// Runs the given OS command and returns the output as string.
17///
18/// Use `input` parameter to specify a text to write to stdin.
19/// Environment variables are set accordingly to `envs`.
20pub 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}