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 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 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 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}