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
use crate::error::Result;
use std::io::{
Error as IoError,
ErrorKind as IoErrorKind,
Write,
};
use std::process::{
Command,
Stdio,
};
use std::str;
use std::thread;
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")
.args(&["/C", command])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
} else {
Command::new("sh")
.envs(envs)
.args(&["-c", command])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.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 {
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(())
}
}