use std::{
any::Any,
future::Future,
io::Result,
pin::Pin,
process::{ExitStatus, Output},
};
use futures::future::try_join3;
#[cfg(unix)]
use nix::{
sys::signal::{Signal, kill},
unistd::Pid,
};
use tokio::{
io::{AsyncRead, AsyncReadExt},
process::{Child, ChildStderr, ChildStdin, ChildStdout, Command},
};
crate::generic_wrap::Wrap!(Command, Child, ChildWrapper, |child| child);
pub trait ChildWrapper: Any + std::fmt::Debug + Send + Sync {
fn inner(&self) -> &dyn ChildWrapper;
fn inner_mut(&mut self) -> &mut dyn ChildWrapper;
fn into_inner(self: Box<Self>) -> Box<dyn ChildWrapper>;
fn try_clone(&self) -> Option<Box<dyn ChildWrapper>> {
None
}
fn stdin(&mut self) -> &mut Option<ChildStdin> {
self.inner_mut().stdin()
}
fn stdout(&mut self) -> &mut Option<ChildStdout> {
self.inner_mut().stdout()
}
fn stderr(&mut self) -> &mut Option<ChildStderr> {
self.inner_mut().stderr()
}
fn id(&self) -> Option<u32> {
self.inner().id()
}
fn kill(&mut self) -> Box<dyn Future<Output = Result<()>> + Send + '_> {
Box::new(async {
self.start_kill()?;
self.wait().await?;
Ok(())
})
}
fn start_kill(&mut self) -> Result<()> {
self.inner_mut().start_kill()
}
fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
self.inner_mut().try_wait()
}
fn wait(&mut self) -> Pin<Box<dyn Future<Output = Result<ExitStatus>> + Send + '_>> {
Box::pin(self.inner_mut().wait())
}
fn wait_with_output(mut self: Box<Self>) -> Box<dyn Future<Output = Result<Output>> + Send>
where
Self: 'static,
{
Box::new(async move {
async fn read_to_end<A: AsyncRead + Unpin>(io: &mut Option<A>) -> Result<Vec<u8>> {
let mut vec = Vec::new();
if let Some(io) = io.as_mut() {
io.read_to_end(&mut vec).await?;
}
Ok(vec)
}
let mut stdout_pipe = self.stdout().take();
let mut stderr_pipe = self.stderr().take();
let stdout_fut = read_to_end(&mut stdout_pipe);
let stderr_fut = read_to_end(&mut stderr_pipe);
let (status, stdout, stderr) = try_join3(self.wait(), stdout_fut, stderr_fut).await?;
drop(stdout_pipe);
drop(stderr_pipe);
Ok(Output {
status,
stdout,
stderr,
})
})
}
#[cfg(unix)]
fn signal(&self, sig: i32) -> Result<()> {
self.inner().signal(sig)
}
}
impl ChildWrapper for Child {
fn inner(&self) -> &dyn ChildWrapper {
self
}
fn inner_mut(&mut self) -> &mut dyn ChildWrapper {
self
}
fn into_inner(self: Box<Self>) -> Box<dyn ChildWrapper> {
Box::new(*self)
}
fn stdin(&mut self) -> &mut Option<ChildStdin> {
&mut self.stdin
}
fn stdout(&mut self) -> &mut Option<ChildStdout> {
&mut self.stdout
}
fn stderr(&mut self) -> &mut Option<ChildStderr> {
&mut self.stderr
}
fn id(&self) -> Option<u32> {
Child::id(self)
}
fn start_kill(&mut self) -> Result<()> {
Child::start_kill(self)
}
fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
Child::try_wait(self)
}
fn wait(&mut self) -> Pin<Box<dyn Future<Output = Result<ExitStatus>> + Send + '_>> {
Box::pin(Child::wait(self))
}
#[cfg(unix)]
fn signal(&self, sig: i32) -> Result<()> {
if let Some(id) = self.id() {
kill(
Pid::from_raw(i32::try_from(id).map_err(std::io::Error::other)?),
Signal::try_from(sig)?,
)
.map_err(std::io::Error::from)
} else {
Ok(())
}
}
}
impl dyn ChildWrapper {
fn downcast_ref<T: 'static>(&self) -> Option<&T> {
(self as &dyn Any).downcast_ref()
}
fn is_raw_child(&self) -> bool {
self.downcast_ref::<Child>().is_some()
}
pub fn inner_child(&self) -> &Child {
let mut inner = self;
while !inner.is_raw_child() {
inner = inner.inner();
}
inner.downcast_ref().unwrap()
}
pub unsafe fn inner_child_mut(&mut self) -> &mut Child {
let mut inner = self;
while !inner.is_raw_child() {
inner = inner.inner_mut();
}
(inner as &mut dyn Any).downcast_mut().unwrap()
}
pub unsafe fn into_inner_child(self: Box<Self>) -> Child {
let mut inner = self;
while !inner.is_raw_child() {
inner = inner.into_inner();
}
*(inner as Box<dyn Any>).downcast().unwrap()
}
}
const _: () = {
const fn assert_sync<T: ?Sized + Sync>() {}
assert_sync::<dyn ChildWrapper>();
};