use std::{
fmt,
io::Result,
process::{ExitStatus, Output},
};
use tokio::{io::AsyncReadExt, process::Child};
#[cfg(unix)]
pub(self) use unix::ChildImp;
#[cfg(windows)]
pub(self) use windows::ChildImp;
#[cfg(unix)]
use nix::sys::signal::Signal;
#[cfg(windows)]
use winapi::um::winnt::HANDLE;
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
pub struct AsyncGroupChild {
imp: ChildImp,
exitstatus: Option<ExitStatus>,
}
impl fmt::Debug for AsyncGroupChild {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("AsyncGroupChild").finish()
}
}
impl AsyncGroupChild {
#[cfg(unix)]
pub(crate) fn new(inner: Child) -> Self {
Self {
imp: ChildImp::new(inner),
exitstatus: None,
}
}
#[cfg(windows)]
pub(crate) fn new(inner: Child, j: HANDLE, c: HANDLE) -> Self {
Self {
imp: ChildImp::new(inner, j, c),
exitstatus: None,
}
}
pub fn inner(&mut self) -> &mut Child {
self.imp.inner()
}
#[cfg_attr(
windows,
doc = "On Windows, this unnavoidably leaves a handle unclosed. Prefer [`inner()`](Self::inner)."
)]
pub fn into_inner(self) -> Child {
self.imp.into_inner()
}
pub async fn kill(&mut self) -> Result<()> {
self.start_kill()?;
self.wait().await?;
Ok(())
}
pub fn start_kill(&mut self) -> Result<()> {
self.imp.start_kill()
}
pub fn id(&self) -> Option<u32> {
self.imp.id()
}
pub async fn wait(&mut self) -> Result<ExitStatus> {
if let Some(es) = self.exitstatus {
return Ok(es);
}
drop(self.imp.take_stdin());
let status = self.imp.wait().await?;
self.exitstatus = Some(status);
Ok(status)
}
pub fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
if self.exitstatus.is_some() {
return Ok(self.exitstatus);
}
match self.imp.try_wait()? {
Some(es) => {
self.exitstatus = Some(es);
Ok(Some(es))
}
None => Ok(None),
}
}
pub async fn wait_with_output(mut self) -> Result<Output> {
drop(self.imp.take_stdin());
let (mut stdout, mut stderr) = (Vec::new(), Vec::new());
match (self.imp.take_stdout(), self.imp.take_stderr()) {
(None, None) => {}
(Some(mut out), None) => {
out.read_to_end(&mut stdout).await?;
}
(None, Some(mut err)) => {
err.read_to_end(&mut stderr).await?;
}
(Some(mut out), Some(mut err)) => {
tokio::try_join!(out.read_to_end(&mut stdout), err.read_to_end(&mut stderr),)?;
}
}
let status = self.imp.wait().await?;
Ok(Output {
status,
stdout,
stderr,
})
}
}
#[cfg(unix)]
impl crate::UnixChildExt for AsyncGroupChild {
fn signal(&self, sig: Signal) -> Result<()> {
self.imp.signal_imp(sig)
}
}