#![cfg_attr(process_control_docs_rs, feature(doc_cfg))]
#![warn(unused_results)]
use std::fmt;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::io;
#[cfg(any(doc, unix))]
use std::os::raw::c_int;
use std::process;
use std::process::Child;
use std::str;
use std::time::Duration;
mod control;
#[cfg_attr(unix, path = "unix/mod.rs")]
#[cfg_attr(windows, path = "windows/mod.rs")]
mod imp;
type WaitResult<T> = io::Result<Option<T>>;
#[rustfmt::skip]
macro_rules! unix_method {
( $method:ident , $return_type:ty ) => {
#[doc = concat!(
"Equivalent to [`ExitStatusExt::",
stringify!($method),
"`][method].
[method]: ::std::os::unix::process::ExitStatusExt::",
stringify!($method),
)]
#[cfg(any(doc, unix))]
#[cfg_attr(process_control_docs_rs, doc(cfg(unix)))]
#[inline]
#[must_use]
pub fn $method(&self) -> $return_type {
self.inner.$method()
}
};
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[must_use]
pub struct ExitStatus {
inner: imp::ExitStatus,
std: process::ExitStatus,
}
impl ExitStatus {
fn new(inner: imp::ExitStatus, std: process::ExitStatus) -> Self {
debug_assert_eq!(inner, std.into());
Self { inner, std }
}
#[inline]
#[must_use]
pub fn success(self) -> bool {
self.inner.success()
}
#[inline]
#[must_use]
pub fn code(self) -> Option<i64> {
self.inner.code().map(Into::into)
}
unix_method!(continued, bool);
unix_method!(core_dumped, bool);
unix_method!(signal, Option<c_int>);
unix_method!(stopped_signal, Option<c_int>);
#[inline]
#[must_use]
pub fn into_std_lossy(self) -> process::ExitStatus {
self.std
}
}
impl AsMut<Self> for ExitStatus {
#[inline]
fn as_mut(&mut self) -> &mut Self {
self
}
}
impl AsRef<Self> for ExitStatus {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
impl Display for ExitStatus {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
impl From<process::ExitStatus> for ExitStatus {
#[inline]
fn from(value: process::ExitStatus) -> Self {
Self::new(value.into(), value)
}
}
#[derive(Clone, Eq, PartialEq)]
#[must_use]
pub struct Output {
pub status: ExitStatus,
pub stdout: Vec<u8>,
pub stderr: Vec<u8>,
}
impl Output {
#[inline]
#[must_use]
pub fn into_std_lossy(self) -> process::Output {
process::Output {
status: self.status.into_std_lossy(),
stdout: self.stdout,
stderr: self.stderr,
}
}
}
impl AsMut<ExitStatus> for Output {
#[inline]
fn as_mut(&mut self) -> &mut ExitStatus {
&mut self.status
}
}
impl AsRef<ExitStatus> for Output {
#[inline]
fn as_ref(&self) -> &ExitStatus {
&self.status
}
}
struct DebugBuffer<'a>(&'a [u8]);
impl Debug for DebugBuffer<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("\"")?;
let mut string = self.0;
while !string.is_empty() {
let mut invalid = &b""[..];
let valid = str::from_utf8(string).unwrap_or_else(|error| {
let (valid, string) = string.split_at(error.valid_up_to());
let invalid_length =
error.error_len().unwrap_or_else(|| string.len());
invalid = &string[..invalid_length];
unsafe { str::from_utf8_unchecked(valid) }
});
Display::fmt(&valid.escape_debug(), f)?;
string = &string[valid.len()..];
for byte in invalid {
write!(f, "\\x{:02X}", byte)?;
}
string = &string[invalid.len()..];
}
f.write_str("\"")
}
}
impl Debug for Output {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Output")
.field("status", &self.status)
.field("stdout", &DebugBuffer(&self.stdout))
.field("stderr", &DebugBuffer(&self.stderr))
.finish()
}
}
impl From<process::Output> for Output {
#[inline]
fn from(value: process::Output) -> Self {
Self {
status: value.status.into(),
stdout: value.stdout,
stderr: value.stderr,
}
}
}
impl From<Output> for ExitStatus {
#[inline]
fn from(value: Output) -> Self {
value.status
}
}
pub trait PipeFilter:
'static + FnMut(&[u8]) -> io::Result<bool> + Send
{
}
impl<T> PipeFilter for T where
T: 'static + FnMut(&[u8]) -> io::Result<bool> + Send
{
}
#[attr_alias::eval]
#[must_use]
pub trait Control: private::Sealed {
type Result;
#[attr_alias(memory_limit, cfg(any(doc, *)))]
#[attr_alias(memory_limit, cfg_attr(process_control_docs_rs, doc(cfg(*))))]
#[must_use]
fn memory_limit(self, limit: usize) -> Self;
#[must_use]
fn time_limit(self, limit: Duration) -> Self;
#[must_use]
fn strict_errors(self) -> Self;
#[must_use]
fn terminate_for_timeout(self) -> Self;
#[must_use]
fn stdout_filter<T>(self, listener: T) -> Self
where
Self: Control<Result = Output>,
T: PipeFilter;
#[must_use]
fn stderr_filter<T>(self, listener: T) -> Self
where
Self: Control<Result = Output>,
T: PipeFilter;
fn wait(self) -> WaitResult<Self::Result>;
}
pub trait ChildExt: private::Sealed {
#[must_use]
fn controlled(&mut self) -> impl Control<Result = ExitStatus> + Debug;
#[must_use]
fn controlled_with_output(self) -> impl Control<Result = Output> + Debug;
}
impl ChildExt for Child {
#[inline]
fn controlled(&mut self) -> impl Control<Result = ExitStatus> + Debug {
control::Buffer::new(self)
}
#[inline]
fn controlled_with_output(self) -> impl Control<Result = Output> + Debug {
control::Buffer::new(self)
}
}
mod private {
use std::process::Child;
use super::control;
pub trait Sealed {}
impl Sealed for Child {}
impl<P> Sealed for control::Buffer<P> where P: control::Process {}
}