use std::ffi::OsStr;
use std::io;
use std::process::{Command, ExitStatus, Stdio};
use crate::error::{ScriptError, ScriptResult};
use crate::output::Output;
#[derive(Debug)]
pub struct CommandBuilder {
cmd: Command,
}
impl CommandBuilder {
pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
Self {
cmd: Command::new(program),
}
}
pub fn arg<S: AsRef<OsStr>>(mut self, arg: S) -> Self {
let _ = self.cmd.arg(arg);
self
}
pub fn args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let _ = self.cmd.args(args);
self
}
pub fn env<K, V>(mut self, key: K, value: V) -> Self
where
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
let _ = self.cmd.env(key, value);
self
}
pub fn envs<I, K, V>(mut self, vars: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
let _ = self.cmd.envs(vars);
self
}
pub fn env_clear(mut self) -> Self {
let _ = self.cmd.env_clear();
self
}
pub fn env_remove<K>(mut self, key: K) -> Self
where
K: AsRef<OsStr>,
{
let _ = self.cmd.env_remove(key);
self
}
pub fn current_dir<D>(mut self, dir: D) -> Self
where
D: AsRef<std::path::Path>,
{
let _ = self.cmd.current_dir(dir);
self
}
pub fn stdin<T: Into<Stdio>>(mut self, stdin: T) -> Self {
let _ = self.cmd.stdin(stdin);
self
}
pub fn stdout<T: Into<Stdio>>(mut self, stdout: T) -> Self {
let _ = self.cmd.stdout(stdout);
self
}
pub fn stderr<T: Into<Stdio>>(mut self, stderr: T) -> Self {
let _ = self.cmd.stderr(stderr);
self
}
pub fn capture_output(mut self) -> Self {
let _ = self.cmd.stdout(Stdio::piped());
let _ = self.cmd.stderr(Stdio::piped());
self
}
pub fn inherit_stdin(mut self) -> Self {
let _ = self.cmd.stdin(Stdio::inherit());
self
}
pub fn inherit_stdout(mut self) -> Self {
let _ = self.cmd.stdout(Stdio::inherit());
self
}
pub fn inherit_stderr(mut self) -> Self {
let _ = self.cmd.stderr(Stdio::inherit());
self
}
pub fn null_stdin(mut self) -> Self {
let _ = self.cmd.stdin(Stdio::null());
self
}
pub fn null_stdout(mut self) -> Self {
let _ = self.cmd.stdout(Stdio::null());
self
}
pub fn null_stderr(mut self) -> Self {
let _ = self.cmd.stderr(Stdio::null());
self
}
#[cfg(unix)]
pub fn process_group(mut self, pgroup: i32) -> Self {
use std::os::unix::process::CommandExt;
self.cmd.process_group(pgroup);
self
}
#[cfg(unix)]
pub fn setsid(mut self, create: bool) -> Self {
use std::os::unix::process::CommandExt;
self.cmd.setsid(create);
self
}
#[cfg(unix)]
pub fn uid(mut self, id: u32) -> Self {
use std::os::unix::process::CommandExt;
self.cmd.uid(id);
self
}
#[cfg(unix)]
pub fn gid(mut self, id: u32) -> Self {
use std::os::unix::process::CommandExt;
self.cmd.gid(id);
self
}
#[cfg(windows)]
pub fn creation_flags(mut self, flags: u32) -> Self {
use std::os::windows::process::CommandExt;
let _ = self.cmd.creation_flags(flags);
self
}
#[cfg(not(windows))]
pub fn creation_flags(self, _flags: u32) -> Self {
self
}
pub fn spawn(&mut self) -> io::Result<std::process::Child> {
self.cmd.spawn()
}
pub fn output(&mut self) -> io::Result<Output> {
let output = self.cmd.output()?;
Ok(Output::new(output.stdout, output.stderr, output.status))
}
pub fn execute(&mut self) -> ScriptResult<Output> {
self.output().map_err(ScriptError::IoError)
}
pub fn status(&mut self) -> io::Result<ExitStatus> {
self.cmd.status()
}
pub fn execute_with_timeout(self, timeout: std::time::Duration) -> ScriptResult<Output> {
use std::sync::mpsc;
use std::thread;
let (tx, rx) = mpsc::channel();
let mut builder = self;
let handle = thread::spawn(move || {
let result = builder.cmd.output();
drop(tx.send(result));
});
drop(handle);
match rx.recv_timeout(timeout) {
Ok(Ok(output)) => Ok(Output::new(output.stdout, output.stderr, output.status)),
Ok(Err(e)) => Err(ScriptError::IoError(e)),
Err(mpsc::RecvTimeoutError::Timeout) => Err(ScriptError::Timeout(timeout)),
Err(mpsc::RecvTimeoutError::Disconnected) => Err(ScriptError::Other(
"Thread disconnected unexpectedly".into(),
)),
}
}
}