use std::{
ffi::OsStr,
io::{self, Read, Write},
process::{Child, Command as StdCmd, ExitStatus, Output, Stdio},
};
use crate::TerminalError;
pub struct Command {
pipe_stdout: bool,
}
impl Command {
pub fn new() -> Self {
Self { pipe_stdout: false }
}
pub fn piped(&mut self) -> &mut Self {
self.pipe_stdout = true;
self
}
pub fn run<I, S>(&self, command: &str, args: I) -> Result<Output, TerminalError>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
match self.pipe_stdout {
true => self.capture_output(command, args),
false => self.wait_for_output(command, args),
}
}
fn wait_for_output<I, S>(&self, command: &str, args: I) -> Result<Output, TerminalError>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut cmd = StdCmd::new(command);
Ok(cmd.args(args).output()?)
}
fn capture_output<I, S>(&self, command: &str, args: I) -> Result<Output, TerminalError>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut child = self.spawn(command, args)?;
let exit_status = Self::read_stdout_from(&mut child)?;
Ok(Output {
status: exit_status,
stdout: vec![],
stderr: vec![],
})
}
fn spawn<I, S>(&self, command: &str, args: I) -> Result<Child, TerminalError>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut cmd = StdCmd::new(command);
let child = cmd.args(args).stdout(Stdio::piped()).spawn()?;
Ok(child)
}
pub fn read_stdout_from(child: &mut Child) -> Result<ExitStatus, TerminalError> {
let mut child_stdout = child
.stdout
.take()
.ok_or("There was a problem acquiring stdout from child process")?;
let mut buffer = [0; 1024];
loop {
let bytes_read = child_stdout.read(&mut buffer)?;
if bytes_read == 0 {
break;
}
io::stdout().write_all(&buffer[..bytes_read])?;
}
Ok(child.wait()?)
}
}