use std::{
any::Any,
future::Future,
io::Result,
pin::Pin,
process::{ExitStatus, Output},
};
#[cfg(windows)]
use std::os::windows::io::BorrowedHandle;
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>;
#[cfg(windows)]
fn process_handle(&self) -> Option<BorrowedHandle<'_>> {
None
}
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> {
self
}
#[cfg(windows)]
fn process_handle(&self) -> Option<BorrowedHandle<'_>> {
let handle = self.raw_handle()?;
Some(unsafe { BorrowedHandle::borrow_raw(handle) })
}
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(())
}
}
}
fn same_child(left: &dyn ChildWrapper, right: &dyn ChildWrapper) -> bool {
std::ptr::addr_eq(left, right) && left.type_id() == right.type_id()
}
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 try_inner_child(&self) -> Option<&Child> {
let mut inner = self;
loop {
if let Some(child) = inner.downcast_ref::<Child>() {
return Some(child);
}
let next = inner.inner();
if same_child(inner, next) {
return None;
}
inner = next;
}
}
pub unsafe fn try_inner_child_mut(&mut self) -> Option<&mut Child> {
let mut inner = self;
loop {
if inner.is_raw_child() {
return (inner as &mut dyn Any).downcast_mut();
}
let inner_type = (&*inner as &dyn Any).type_id();
let inner_ptr = std::ptr::from_mut(inner);
let next = inner.inner_mut();
if std::ptr::addr_eq(inner_ptr, std::ptr::from_mut(next))
&& inner_type == (&*next as &dyn Any).type_id()
{
return None;
}
inner = next;
}
}
pub unsafe fn try_into_inner_child(self: Box<Self>) -> std::result::Result<Child, Box<Self>> {
let mut inner = self;
loop {
if inner.is_raw_child() {
return match (inner as Box<dyn Any>).downcast::<Child>() {
Ok(child) => Ok(*child),
Err(_) => unreachable!("native child type was checked before downcasting"),
};
}
let terminal = {
let next = inner.inner();
same_child(inner.as_ref(), next)
};
if terminal {
return Err(inner);
}
inner = inner.into_inner();
}
}
}
const _: () = {
const fn assert_sync<T: ?Sized + Sync>() {}
assert_sync::<dyn ChildWrapper>();
};