tendermint_testgen/
command.rs

1use std::{
2    io::{self, Read},
3    process,
4};
5
6/// A thin wrapper around process::Command to facilitate running external commands.
7pub struct Command {
8    program: Option<String>,
9    args: Vec<String>,
10    dir: Option<String>,
11}
12
13/// The result of a command execution if the child process managed to execute
14pub struct CommandRun {
15    pub status: process::ExitStatus,
16    pub stdout: String,
17    pub stderr: String,
18}
19
20impl Command {
21    /// Check whether the given program can be executed
22    pub fn exists_program(program: &str) -> bool {
23        Command::new().program(program).spawn().is_ok()
24    }
25
26    /// Construct a new Command
27    pub fn new() -> Command {
28        Command {
29            program: None,
30            args: vec![],
31            dir: None,
32        }
33    }
34
35    /// Set the program to run
36    pub fn program(&mut self, program: &str) -> &mut Self {
37        self.program = Some(program.to_owned());
38        self
39    }
40
41    /// Add a new program argument
42    pub fn arg(&mut self, arg: &str) -> &mut Self {
43        self.args.push(arg.to_owned());
44        self
45    }
46
47    /// Add a new program argument, concatenated from several parts
48    pub fn arg_from_parts(&mut self, parts: Vec<&str>) -> &mut Self {
49        let arg = parts.join("");
50        self.args.push(arg);
51        self
52    }
53
54    /// Set the working directory for the child process
55    pub fn current_dir(&mut self, dir: &str) -> &mut Self {
56        self.dir = Some(dir.to_owned());
57        self
58    }
59
60    /// Execute the command as a child process, and extract its status, stdout, stderr.
61    pub fn spawn(&mut self) -> io::Result<CommandRun> {
62        match &self.program {
63            None => Err(io::Error::new(io::ErrorKind::InvalidInput, "")),
64            Some(program) => {
65                let mut command = process::Command::new(program);
66                command
67                    .args(&self.args)
68                    .stdout(process::Stdio::piped())
69                    .stderr(process::Stdio::piped());
70                if let Some(dir) = &self.dir {
71                    command.current_dir(dir);
72                }
73                let mut process = command.spawn()?;
74                let status = process.wait()?;
75                let mut stdout = String::new();
76                process.stdout.unwrap().read_to_string(&mut stdout)?;
77                let mut stderr = String::new();
78                process.stderr.unwrap().read_to_string(&mut stderr)?;
79                Ok(CommandRun {
80                    status,
81                    stdout,
82                    stderr,
83                })
84            },
85        }
86    }
87}
88
89impl Default for Command {
90    fn default() -> Self {
91        Self::new()
92    }
93}