#![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;
macro_rules! r#impl {
( $short_name:ident , $long_cfg:expr , ) => {
const _: () = assert!(
cfg!($short_name) == $long_cfg,
concat!(
"The configuration option '",
stringify!($short_name),
"' is private.",
),
);
};
}
r#impl!(
process_control_memory_limit,
cfg!(any(
target_os = "android",
all(
target_os = "linux",
any(target_env = "gnu", target_env = "musl"),
),
windows,
)),
);
r#impl!(
process_control_unix_waitid,
cfg!(not(any(
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "tvos",
target_os = "vxworks",
))),
);
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.0.$method()
}
};
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[must_use]
pub struct ExitStatus(imp::ExitStatus);
impl ExitStatus {
#[inline]
#[must_use]
pub fn success(self) -> bool {
self.0.success()
}
#[inline]
#[must_use]
pub fn code(self) -> Option<i64> {
self.0.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>);
}
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.0, f)
}
}
impl From<process::ExitStatus> for ExitStatus {
#[inline]
fn from(value: process::ExitStatus) -> Self {
Self(value.into())
}
}
#[derive(Clone, Eq, PartialEq)]
#[must_use]
pub struct Output {
pub status: ExitStatus,
pub stdout: Vec<u8>,
pub stderr: Vec<u8>,
}
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
}
}
#[must_use]
pub trait Control: private::Sealed {
type Result;
#[cfg(any(doc, process_control_memory_limit))]
#[cfg_attr(
process_control_docs_rs,
doc(cfg(any(
target_os = "android",
all(
target_os = "linux",
any(target_env = "gnu", target_env = "musl"),
),
windows,
)))
)]
#[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;
fn wait(self) -> WaitResult<Self::Result>;
}
pub trait ChildExt<'a>: private::Sealed {
type ExitStatusControl: 'a + Control<Result = ExitStatus> + Debug;
type OutputControl: Control<Result = Output> + Debug;
#[deprecated(since = "4.1.0", note = "use `Child::kill` instead")]
fn terminate_if_running(&mut self) -> io::Result<()>;
#[must_use]
fn controlled(&'a mut self) -> Self::ExitStatusControl;
#[must_use]
fn controlled_with_output(self) -> Self::OutputControl;
}
impl<'a> ChildExt<'a> for Child {
type ExitStatusControl = control::Buffer<&'a mut Self>;
type OutputControl = control::Buffer<Self>;
#[inline]
fn terminate_if_running(&mut self) -> io::Result<()> {
self.kill()
}
#[inline]
fn controlled(&'a mut self) -> Self::ExitStatusControl {
Self::ExitStatusControl::new(self)
}
#[inline]
fn controlled_with_output(self) -> Self::OutputControl {
Self::OutputControl::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 {}
}