git_cliff_core/
command.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use crate::error::Result;
use std::io::{
	Error as IoError,
	ErrorKind as IoErrorKind,
	Write,
};
use std::process::{
	Command,
	Stdio,
};
use std::thread;
use std::{
	env,
	str,
};

/// Runs the given OS command and returns the output as string.
///
/// Use `input` parameter to specify a text to write to stdin.
/// Environment variables are set accordingly to `envs`.
pub fn run(
	command: &str,
	input: Option<String>,
	envs: Vec<(&str, &str)>,
) -> Result<String> {
	let mut child = if cfg!(target_os = "windows") {
		Command::new("cmd")
			.envs(envs)
			.args(["/C", command])
			.stdin(Stdio::piped())
			.stdout(Stdio::piped())
			.current_dir(env::current_dir()?)
			.spawn()
	} else {
		Command::new("sh")
			.envs(envs)
			.args(["-c", command])
			.stdin(Stdio::piped())
			.stdout(Stdio::piped())
			.current_dir(env::current_dir()?)
			.spawn()
	}?;
	if let Some(input) = input {
		let mut stdin = child.stdin.take().ok_or_else(|| {
			IoError::new(IoErrorKind::Other, "stdin is not captured")
		})?;
		thread::spawn(move || {
			stdin
				.write_all(input.as_bytes())
				.expect("Failed to write to stdin");
		});
	}
	let output = child.wait_with_output()?;
	if output.status.success() {
		Ok(str::from_utf8(&output.stdout)?.to_string())
	} else {
		for output in [output.stdout, output.stderr] {
			let output = str::from_utf8(&output)?.to_string();
			if !output.is_empty() {
				log::error!("{}", output);
			}
		}
		Err(IoError::new(
			IoErrorKind::Other,
			format!("command exited with {:?}", output.status),
		)
		.into())
	}
}

#[cfg(test)]
mod test {
	use super::*;
	#[test]
	#[cfg(target_family = "unix")]
	fn run_os_command() -> Result<()> {
		assert_eq!(
			"eroc-ffilc-tig",
			run("echo $APP_NAME | rev", None, vec![(
				"APP_NAME",
				env!("CARGO_PKG_NAME")
			)])?
			.trim()
		);
		assert_eq!(
			"eroc-ffilc-tig",
			run("rev", Some(env!("CARGO_PKG_NAME").to_string()), vec![])?.trim()
		);
		assert_eq!("testing", run("echo 'testing'", None, vec![])?.trim());
		assert!(run("some_command", None, vec![]).is_err());
		Ok(())
	}
}