test_process_executor/
lib.rs1use anyhow::anyhow;
2use std::ffi::OsStr;
3use std::fmt;
4use std::process::Command;
5use std::str::from_utf8;
6
7pub struct Executor<K, V>
10where
11 K: AsRef<OsStr> + Clone,
12 V: AsRef<OsStr> + Clone,
13{
14 env: Vec<(K, V)>,
15}
16
17impl<K, V> Executor<K, V>
18where
19 K: AsRef<OsStr> + Clone,
20 V: AsRef<OsStr> + Clone,
21{
22 pub fn new(env: Vec<(K, V)>) -> Self {
27 Executor { env }
28 }
29
30 pub fn run<I, S>(&self, args: I)
47 where
48 I: IntoIterator<Item = S>,
49 S: AsRef<OsStr>,
50 {
51 Execution::run(args, self.env.clone())
52 }
53}
54
55struct Execution<S: AsRef<OsStr>> {
56 cmd: S,
57 args: Vec<S>,
58 result: Option<std::process::Output>,
59}
60
61impl<S: AsRef<OsStr>> Execution<S> {
62 fn run<I, K, V, E>(args: I, envs: E)
63 where
64 I: IntoIterator<Item = S>,
65 K: AsRef<OsStr>,
66 V: AsRef<OsStr>,
67 E: IntoIterator<Item = (K, V)>,
68 {
69 let mut iter = args.into_iter();
70 let mut exec = Execution {
71 cmd: iter
72 .next()
73 .ok_or_else(|| anyhow!("Missing command"))
74 .unwrap(),
75 args: iter.collect::<Vec<S>>(),
76 result: None,
77 };
78
79 exec.result = Some(
80 Command::new(&exec.cmd)
81 .args(&exec.args)
82 .envs(envs)
83 .output()
84 .expect("Failed to execute command"),
85 );
86 assert!(
87 exec.result.as_ref().unwrap().status.success(),
88 format!("{}", exec)
89 );
90 }
91}
92
93impl<S: AsRef<OsStr>> fmt::Display for Execution<S> {
94 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95 let mut cmd = Vec::new();
96 cmd.push(self.cmd.as_ref());
97 for arg in &self.args {
98 cmd.push(arg.as_ref());
99 }
100 let cmd: Vec<String> = cmd
101 .into_iter()
102 .map(|osstr| osstr.to_string_lossy().to_string())
103 .collect();
104 write!(f, "\x1b[95m{}\x1b[0m", cmd.join(" "))?;
105 if let Some(out) = self.result.as_ref() {
106 if !out.status.success() {
107 write!(f, "\n{}", out.status)?;
108 }
109 if !out.stdout.is_empty() {
110 write!(f, "\n\x1b[92m{}\x1b[0m", from_utf8(&out.stdout).unwrap())?;
111 }
112 if !out.stderr.is_empty() {
113 write!(f, "\n\x1b[91m{}\x1b[0m", from_utf8(&out.stderr).unwrap())?;
114 }
115 }
116 Ok(())
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn export_right_environment_variable() {
126 let executor = Executor::new(vec![("FOO", "BAR")]);
127 executor.run(vec!["/bin/bash", "-c", "[ \"$FOO\" == \"BAR\" ]"]);
128 }
129
130 #[test]
131 #[should_panic]
132 fn export_wrong_environment_variable() {
133 let executor = Executor::new(vec![("FOO", "BAZINGA")]);
134 executor.run(vec!["/bin/bash", "-c", "[ \"$FOO\" == \"BAR\" ]"]);
135 }
136}