Skip to main content

rust_terminal/
command.rs

1use std::{
2    ffi::OsStr,
3    io::{self, Read, Write},
4    process::{Child, Command as StdCmd, ExitStatus, Output, Stdio},
5};
6
7use crate::TerminalError;
8
9pub struct Command {
10    pipe_stdout: bool,
11}
12
13impl Command {
14    pub fn new() -> Self {
15        Self { pipe_stdout: false }
16    }
17
18    pub fn piped(&mut self) -> &mut Self {
19        self.pipe_stdout = true;
20        self
21    }
22
23    pub fn run<I, S>(&self, command: &str, args: I) -> Result<Output, TerminalError>
24    where
25        I: IntoIterator<Item = S>,
26        S: AsRef<OsStr>,
27    {
28        match self.pipe_stdout {
29            true => self.capture_output(command, args),
30            false => self.wait_for_output(command, args),
31        }
32    }
33
34    /// Run command without piping output to parent process.
35    fn wait_for_output<I, S>(&self, command: &str, args: I) -> Result<Output, TerminalError>
36    where
37        I: IntoIterator<Item = S>,
38        S: AsRef<OsStr>,
39    {
40        let mut cmd = StdCmd::new(command);
41        Ok(cmd.args(args).output()?)
42    }
43
44    fn capture_output<I, S>(&self, command: &str, args: I) -> Result<Output, TerminalError>
45    where
46        I: IntoIterator<Item = S>,
47        S: AsRef<OsStr>,
48    {
49        let mut child = self.spawn(command, args)?;
50        let exit_status = Self::read_stdout_from(&mut child)?;
51        Ok(Output {
52            status: exit_status,
53            stdout: vec![],
54            stderr: vec![],
55        })
56    }
57
58    /// Spawn a command and wait for it to finish, pipes stdout to parent process.
59    fn spawn<I, S>(&self, command: &str, args: I) -> Result<Child, TerminalError>
60    where
61        I: IntoIterator<Item = S>,
62        S: AsRef<OsStr>,
63    {
64        let mut cmd = StdCmd::new(command);
65        let child = cmd.args(args).stdout(Stdio::piped()).spawn()?;
66        Ok(child)
67    }
68
69    /// Takes a child process and reads its stdout while waiting for it to finish.
70    pub fn read_stdout_from(child: &mut Child) -> Result<ExitStatus, TerminalError> {
71        let mut child_stdout = child
72            .stdout
73            .take()
74            .ok_or("There was a problem acquiring stdout from child process")?;
75        let mut buffer = [0; 1024];
76        loop {
77            let bytes_read = child_stdout.read(&mut buffer)?;
78            if bytes_read == 0 {
79                break;
80            }
81            io::stdout().write_all(&buffer[..bytes_read])?;
82        }
83        Ok(child.wait()?)
84    }
85}