use std::fmt;
use std::io;
use std::process::{Child, Output};
#[cfg(feature = "timeout")]
use std::time::Duration;
#[cfg(feature = "timeout")]
use wait_timeout::ChildExt;
#[derive(Clone, Copy)]
pub struct ExitStatusWrapper(ExitStatusEnum);
#[derive(Debug, Clone, Copy)]
enum ExitStatusEnum {
Std(::std::process::ExitStatus),
}
impl ExitStatusWrapper {
fn std(es: ::std::process::ExitStatus) -> Self {
ExitStatusWrapper(ExitStatusEnum::Std(es))
}
pub fn success(&self) -> bool {
match self.0 {
ExitStatusEnum::Std(es) => es.success(),
}
}
pub fn code(&self) -> Option<i32> {
match self.0 {
ExitStatusEnum::Std(es) => es.code(),
}
}
#[cfg(not(target_os = "windows"))]
pub fn unix_signal(&self) -> Option<i32> {
use std::os::unix::process::ExitStatusExt;
match self.0 {
ExitStatusEnum::Std(es) => es.signal(),
}
}
#[cfg(target_os = "windows")]
pub fn unix_signal(&self) -> Option<i32> {
None
}
}
impl fmt::Debug for ExitStatusWrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
ExitStatusEnum::Std(ref es) => fmt::Debug::fmt(es, f),
}
}
}
impl fmt::Display for ExitStatusWrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
ExitStatusEnum::Std(ref es) => fmt::Display::fmt(es, f),
}
}
}
#[derive(Debug)]
pub struct ChildWrapper {
child: Child,
exit_status: Option<ExitStatusWrapper>,
}
impl ChildWrapper {
pub(crate) fn new(child: Child) -> Self {
ChildWrapper { child, exit_status: None }
}
pub fn inner(&self) -> &Child {
&self.child
}
pub fn inner_mut(&mut self) -> &mut Child {
&mut self.child
}
pub fn kill(&mut self) -> io::Result<()> {
if self.exit_status.is_none() {
self.child.kill()
} else {
Err(io::Error::new(io::ErrorKind::NotFound, "Process already reaped"))
}
}
pub fn id(&self) -> u32 {
self.child.id()
}
pub fn wait(&mut self) -> io::Result<ExitStatusWrapper> {
if let Some(status) = self.exit_status {
Ok(status)
} else {
let status = ExitStatusWrapper::std(self.child.wait()?);
self.exit_status = Some(status);
Ok(status)
}
}
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatusWrapper>> {
if let Some(status) = self.exit_status {
Ok(Some(status))
} else {
let status = self.child.try_wait()?.map(ExitStatusWrapper::std);
self.exit_status = status;
Ok(status)
}
}
pub fn wait_with_output(self) -> io::Result<Output> {
if self.exit_status.is_some() {
return Err(io::Error::new(
io::ErrorKind::NotFound, "Process already reaped"));
}
self.child.wait_with_output()
}
#[cfg(feature = "timeout")]
pub fn wait_timeout(&mut self, dur: Duration)
-> io::Result<Option<ExitStatusWrapper>> {
if let Some(status) = self.exit_status {
Ok(Some(status))
} else {
let status = self.child.wait_timeout(dur)?.map(ExitStatusWrapper::std);
self.exit_status = status;
Ok(status)
}
}
}