1use std::{
2 ffi::OsStr,
3 io::{self, Read, Write},
4 path::{Path, PathBuf},
5 process::{Child, Command as StdCmd, ExitStatus, Output, Stdio},
6};
7
8use crate::TerminalError;
9
10pub struct Command {
11 pipe_stdout: bool,
12 current_dir: Option<PathBuf>,
13}
14
15impl Command {
16 pub fn new() -> Self {
17 Self {
18 pipe_stdout: false,
19 current_dir: None,
20 }
21 }
22
23 pub fn current_dir(&mut self, dir: &Path) -> &mut Self {
24 self.current_dir = Some(dir.to_path_buf());
25 self
26 }
27
28 pub fn piped(&mut self) -> &mut Self {
29 self.pipe_stdout = true;
30 self
31 }
32
33 pub fn run<I, S>(&self, command: &str, args: I) -> Result<Output, TerminalError>
34 where
35 I: IntoIterator<Item = S>,
36 S: AsRef<OsStr>,
37 {
38 match self.pipe_stdout {
39 true => self.capture_output(command, args),
40 false => self.wait_for_output(command, args),
41 }
42 }
43
44 fn wait_for_output<I, S>(&self, command: &str, args: I) -> Result<Output, TerminalError>
46 where
47 I: IntoIterator<Item = S>,
48 S: AsRef<OsStr>,
49 {
50 let mut cmd = StdCmd::new(command);
51 match &self.current_dir {
52 Some(dir) => Ok(cmd.args(args).current_dir(dir).output()?),
53 None => Ok(cmd.args(args).output()?),
54 }
55 }
56
57 fn capture_output<I, S>(&self, command: &str, args: I) -> Result<Output, TerminalError>
58 where
59 I: IntoIterator<Item = S>,
60 S: AsRef<OsStr>,
61 {
62 let mut child = self.spawn(command, args)?;
63 let exit_status = Self::read_stdout_from(&mut child)?;
64 Ok(Output {
65 status: exit_status,
66 stdout: vec![],
67 stderr: vec![],
68 })
69 }
70
71 fn spawn<I, S>(&self, command: &str, args: I) -> Result<Child, TerminalError>
73 where
74 I: IntoIterator<Item = S>,
75 S: AsRef<OsStr>,
76 {
77 let mut cmd = StdCmd::new(command);
78
79 let child = match &self.current_dir {
80 Some(dir) => cmd
81 .args(args)
82 .current_dir(dir)
83 .stdout(Stdio::piped())
84 .spawn()?,
85 None => cmd.args(args).stdout(Stdio::piped()).spawn()?,
86 };
87 Ok(child)
88 }
89
90 pub fn read_stdout_from(child: &mut Child) -> Result<ExitStatus, TerminalError> {
92 let mut child_stdout = child
93 .stdout
94 .take()
95 .ok_or("There was a problem acquiring stdout from child process")?;
96 let mut buffer = [0; 1024];
97 loop {
98 let bytes_read = child_stdout.read(&mut buffer)?;
99 if bytes_read == 0 {
100 break;
101 }
102 io::stdout().write_all(&buffer[..bytes_read])?;
103 }
104 Ok(child.wait()?)
105 }
106}