mod error;
mod handle;
mod output;
mod result {
pub type Result<T> = std::result::Result<T, crate::error::Error>;
}
pub use self::{error::*, handle::*, output::*, result::*};
pub use std::process::{ChildStderr, ChildStdin, ChildStdout, ExitStatus, Stdio};
use std::{
ffi::OsStr,
fmt::{self, Display},
path::Path,
process,
};
#[derive(Debug)]
pub struct Command {
inner: process::Command,
display: String,
}
impl Display for Command {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.display())
}
}
impl Command {
fn push_display(&mut self, component: &OsStr) {
if !self.display.is_empty() {
self.display.push(' ');
}
self.display.push_str(component.to_string_lossy().as_ref());
}
pub fn new(name: impl AsRef<OsStr>) -> Self {
let name = name.as_ref();
let mut this = Self {
inner: process::Command::new(name),
display: Default::default(),
};
this.push_display(name);
this
}
pub fn try_parse(arg_str: impl AsRef<str>) -> Option<Self> {
let arg_str = arg_str.as_ref();
let mut args = arg_str.split_whitespace();
args.next().map(|name| {
let mut this = Self::new(name);
this.add_args(args);
this
})
}
pub fn parse(arg_str: impl AsRef<str>) -> Self {
Self::try_parse(arg_str).expect("passed an empty string to `parse`")
}
pub fn pure(mut self) -> Self {
self.inner.env_clear();
self
}
pub fn display(&self) -> &str {
&self.display
}
pub fn set_cwd(&mut self, cwd: impl AsRef<Path>) -> &mut Self {
let cwd = cwd.as_ref();
log::debug!("setting cwd to {cwd:?} on command {:?}", self.display);
self.inner.current_dir(cwd);
self
}
pub fn with_cwd(mut self, cwd: impl AsRef<Path>) -> Self {
self.set_cwd(cwd);
self
}
pub fn set_stdin(&mut self, cfg: impl Into<Stdio>) -> &mut Self {
let cfg = cfg.into();
log::debug!("setting stdin to {:?} on command {cfg:?}", self.display);
self.inner.stdin(cfg);
self
}
pub fn with_stdin(mut self, cfg: impl Into<Stdio>) -> Self {
self.set_stdin(cfg);
self
}
pub fn set_stdin_piped(&mut self) -> &mut Self {
self.set_stdin(Stdio::piped());
self
}
pub fn with_stdin_piped(mut self) -> Self {
self.set_stdin_piped();
self
}
pub fn set_stdin_null(&mut self) -> &mut Self {
self.set_stdin(Stdio::null());
self
}
pub fn with_stdin_null(mut self) -> Self {
self.set_stdin_null();
self
}
pub fn set_stdout(&mut self, cfg: impl Into<Stdio>) -> &mut Self {
let cfg = cfg.into();
log::debug!("setting stdout to {:?} on command {:?}", cfg, self.display);
self.inner.stdout(cfg);
self
}
pub fn with_stdout(mut self, cfg: impl Into<Stdio>) -> Self {
self.set_stdout(cfg);
self
}
pub fn set_stdout_piped(&mut self) -> &mut Self {
self.set_stdout(Stdio::piped());
self
}
pub fn with_stdout_piped(mut self) -> Self {
self.set_stdout_piped();
self
}
pub fn set_stdout_null(&mut self) -> &mut Self {
self.set_stdout(Stdio::null());
self
}
pub fn with_stdout_null(mut self) -> Self {
self.set_stdout_null();
self
}
pub fn set_stderr(&mut self, cfg: impl Into<Stdio>) -> &mut Self {
let cfg = cfg.into();
log::debug!("setting stderr to {cfg:?} on command {:?}", self.display);
self.inner.stderr(cfg);
self
}
pub fn with_stderr(mut self, cfg: impl Into<Stdio>) -> Self {
self.set_stderr(cfg);
self
}
pub fn set_stderr_piped(&mut self) -> &mut Self {
self.set_stderr(Stdio::piped());
self
}
pub fn with_stderr_piped(mut self) -> Self {
self.set_stderr_piped();
self
}
pub fn set_stderr_null(&mut self) -> &mut Self {
self.set_stderr(Stdio::null());
self
}
pub fn with_stderr_null(mut self) -> Self {
self.set_stderr_null();
self
}
pub fn add_env_var(&mut self, key: impl AsRef<OsStr>, val: impl AsRef<OsStr>) -> &mut Self {
let key = key.as_ref();
let val = val.as_ref();
log::debug!(
"adding env var {key:?} = {val:?} to command {:?}",
self.display
);
self.inner.env(key, val);
self
}
pub fn with_env_var(mut self, key: impl AsRef<OsStr>, val: impl AsRef<OsStr>) -> Self {
self.add_env_var(key, val);
self
}
pub fn add_env_vars(
&mut self,
vars: impl IntoIterator<Item = (impl AsRef<OsStr>, impl AsRef<OsStr>)>,
) -> &mut Self {
for (key, val) in vars.into_iter() {
self.add_env_var(key, val);
}
self
}
pub fn with_env_vars(
mut self,
vars: impl IntoIterator<Item = (impl AsRef<OsStr>, impl AsRef<OsStr>)>,
) -> Self {
self.add_env_vars(vars);
self
}
pub fn add_arg(&mut self, name: impl AsRef<OsStr>) -> &mut Self {
let name = name.as_ref();
log::debug!("adding arg {name:?} to command {:?}", self.display);
self.inner.arg(name);
self.push_display(name);
self
}
pub fn with_arg(mut self, name: impl AsRef<OsStr>) -> Self {
self.add_arg(name);
self
}
pub fn add_args(&mut self, args: impl IntoIterator<Item = impl AsRef<OsStr>>) -> &mut Self {
for arg in args.into_iter() {
self.add_arg(arg);
}
self
}
pub fn with_args(mut self, args: impl IntoIterator<Item = impl AsRef<OsStr>>) -> Self {
self.add_args(args);
self
}
pub fn add_parsed_args(&mut self, arg_str: impl AsRef<str>) -> &mut Self {
self.add_args(arg_str.as_ref().split_whitespace())
}
pub fn with_parsed_args(mut self, arg_str: impl AsRef<str>) -> Self {
self.add_parsed_args(arg_str);
self
}
fn run_inner(&mut self) -> Result<Handle> {
Error::from_child_result(self.display.clone(), self.inner.spawn())
}
pub fn run(&mut self) -> Result<Handle> {
log::info!("running command {:?}", self.display);
self.run_inner()
}
pub fn run_and_detach(&mut self) -> Result<()> {
log::info!("running command {:?} and detaching", self.display);
#[cfg(unix)]
unsafe {
use std::os::unix::process::CommandExt as _;
let display = self.display.clone();
self.inner.pre_exec(move || match libc::fork() {
-1 => {
let err = std::io::Error::last_os_error();
log::error!("`fork` failed for command {display:?}: {err}");
Err(err)
}
0 => {
if libc::setsid() == -1 {
let err = std::io::Error::last_os_error();
log::error!("`setsid` failed for command {display:?}: {err}");
Err(err)
} else {
Ok(())
}
}
_ => libc::_exit(0),
});
}
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
use windows_sys::Win32::System::Threading::{
CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW,
};
self.inner
.creation_flags(CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW);
}
self.set_stdin_null()
.set_stdout_null()
.set_stderr_null()
.run_inner()
.map(|handle| handle.leak())
}
pub fn run_and_wait(&mut self) -> Result<ExitStatus> {
log::info!("running command {:?} and waiting for exit", self.display);
self.run_inner()?.wait()
}
pub fn run_and_wait_for_output(&mut self) -> Result<Output> {
log::info!("running command {:?} and waiting for output", self.display);
self.set_stdout_piped()
.set_stderr_piped()
.run_inner()?
.wait_for_output()
}
pub fn run_and_wait_for_str<T>(&mut self, f: impl FnOnce(&str) -> T) -> Result<T> {
self.run_and_wait_for_output()?.stdout_str().map(f)
}
pub fn run_and_wait_for_string(&mut self) -> Result<String> {
self.run_and_wait_for_str(ToOwned::to_owned)
}
pub fn run_and_wait_for_trimmed(&mut self) -> Result<String> {
self.run_and_wait_for_str(|s| s.trim().to_string())
}
}