use std::{
fmt,
future::poll_fn,
io::{self, IoSlice, IoSliceMut},
pin::Pin,
process::Stdio,
task::{Context, Poll, ready},
};
#[cfg(unix)]
use std::{
io::{PipeReader, PipeWriter, Read, Write},
os::fd::{AsFd, OwnedFd},
};
#[cfg(windows)]
use std::{
io::{Read, Write},
marker::PhantomData,
};
use futures_io::{AsyncRead, AsyncWrite};
#[cfg(unix)]
use crate::AsyncIo;
#[cfg(windows)]
use crate::Unblock;
use crate::{Local, Mode, Runtime};
pub struct ChildStdin<M = Local>
where
M: Mode,
{
pipe: Option<Pipe<StdinPipe, M>>,
}
impl<M> ChildStdin<M>
where
M: Mode,
{
pub(super) fn new(runtime: &Runtime<M>, pipe: std::process::ChildStdin) -> io::Result<Self> {
Ok(Self {
pipe: Some(Pipe::new(runtime, pipe)?),
})
}
pub async fn into_stdio(self) -> io::Result<Stdio> {
let Some(mut pipe) = self.pipe else {
return Err(closed());
};
poll_fn(|cx| Pin::new(&mut pipe).poll_flush(cx)).await?;
pipe.into_stdio().await
}
fn pipe(&mut self) -> io::Result<&mut Pipe<StdinPipe, M>> {
self.pipe.as_mut().ok_or_else(closed)
}
}
impl<M> AsyncWrite for ChildStdin<M>
where
M: Mode,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let pipe = match self.get_mut().pipe() {
Ok(pipe) => pipe,
Err(e) => return Poll::Ready(Err(e)),
};
Pin::new(pipe).poll_write(cx, buf)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<io::Result<usize>> {
let pipe = match self.get_mut().pipe() {
Ok(pipe) => pipe,
Err(e) => return Poll::Ready(Err(e)),
};
Pin::new(pipe).poll_write_vectored(cx, bufs)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let Some(pipe) = &mut self.get_mut().pipe else {
return Poll::Ready(Ok(()));
};
Pin::new(pipe).poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
let Some(pipe) = &mut this.pipe else {
return Poll::Ready(Ok(()));
};
let flushed = ready!(Pin::new(pipe).poll_flush(cx));
this.pipe = None;
Poll::Ready(flushed)
}
}
impl<M> fmt::Debug for ChildStdin<M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChildStdin").finish_non_exhaustive()
}
}
pub struct ChildStdout<M = Local>
where
M: Mode,
{
pipe: Pipe<StdoutPipe, M>,
}
impl<M> ChildStdout<M>
where
M: Mode,
{
pub(super) fn new(runtime: &Runtime<M>, pipe: std::process::ChildStdout) -> io::Result<Self> {
Ok(Self {
pipe: Pipe::new(runtime, pipe)?,
})
}
pub async fn into_stdio(self) -> io::Result<Stdio> {
self.pipe.into_stdio().await
}
}
impl<M> AsyncRead for ChildStdout<M>
where
M: Mode,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.get_mut().pipe).poll_read(cx, buf)
}
fn poll_read_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.get_mut().pipe).poll_read_vectored(cx, bufs)
}
}
impl<M> fmt::Debug for ChildStdout<M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChildStdout").finish_non_exhaustive()
}
}
pub struct ChildStderr<M = Local>
where
M: Mode,
{
pipe: Pipe<StderrPipe, M>,
}
impl<M> ChildStderr<M>
where
M: Mode,
{
pub(super) fn new(runtime: &Runtime<M>, pipe: std::process::ChildStderr) -> io::Result<Self> {
Ok(Self {
pipe: Pipe::new(runtime, pipe)?,
})
}
pub async fn into_stdio(self) -> io::Result<Stdio> {
self.pipe.into_stdio().await
}
}
impl<M> AsyncRead for ChildStderr<M>
where
M: Mode,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.get_mut().pipe).poll_read(cx, buf)
}
fn poll_read_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.get_mut().pipe).poll_read_vectored(cx, bufs)
}
}
impl<M> fmt::Debug for ChildStderr<M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChildStderr").finish_non_exhaustive()
}
}
#[cfg(unix)]
type StdinPipe = PipeWriter;
#[cfg(unix)]
type StdoutPipe = PipeReader;
#[cfg(unix)]
type StderrPipe = PipeReader;
#[cfg(windows)]
type StdinPipe = std::process::ChildStdin;
#[cfg(windows)]
type StdoutPipe = std::process::ChildStdout;
#[cfg(windows)]
type StderrPipe = std::process::ChildStderr;
#[cfg(unix)]
struct Pipe<T, M>(AsyncIo<T, M>)
where
M: Mode;
#[cfg(windows)]
struct Pipe<T, M>(Unblock<T>, PhantomData<M::Ptr<()>>)
where
M: Mode;
#[cfg(unix)]
impl<T, M> Pipe<T, M>
where
M: Mode,
T: AsFd + From<OwnedFd> + Send + Sync + 'static,
{
fn new<S>(runtime: &Runtime<M>, pipe: S) -> io::Result<Self>
where
S: Into<OwnedFd>,
{
let pipe = T::from(pipe.into());
rustix::io::ioctl_fionbio(&pipe, true)?;
Ok(Self(AsyncIo::from_nonblocking(runtime, pipe)?))
}
}
#[cfg(windows)]
impl<T, M> Pipe<T, M>
where
M: Mode,
{
fn new(_runtime: &Runtime<M>, pipe: T) -> io::Result<Self> {
Ok(Self(Unblock::new(pipe), PhantomData))
}
}
#[cfg(unix)]
impl<T, M> Pipe<T, M>
where
M: Mode,
T: AsFd + Into<Stdio>,
{
async fn into_stdio(self) -> io::Result<Stdio> {
let pipe = self.0.into_inner();
rustix::io::ioctl_fionbio(&pipe, false)?;
Ok(pipe.into())
}
}
#[cfg(windows)]
impl<T, M> Pipe<T, M>
where
M: Mode,
T: Into<Stdio> + Send + 'static,
{
async fn into_stdio(self) -> io::Result<Stdio> {
Ok(self.0.into_inner().await.into())
}
}
#[cfg(unix)]
impl<T, M> AsyncRead for Pipe<T, M>
where
M: Mode,
for<'a> &'a T: Read,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut &self.0).poll_read(cx, buf)
}
fn poll_read_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut &self.0).poll_read_vectored(cx, bufs)
}
}
#[cfg(windows)]
impl<T, M> AsyncRead for Pipe<T, M>
where
M: Mode,
T: Read + Send + 'static,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.get_mut().0).poll_read(cx, buf)
}
fn poll_read_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.get_mut().0).poll_read_vectored(cx, bufs)
}
}
#[cfg(unix)]
impl<T, M> AsyncWrite for Pipe<T, M>
where
M: Mode,
for<'a> &'a T: Write,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut &self.0).poll_write(cx, buf)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut &self.0).poll_write_vectored(cx, bufs)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut &self.0).poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut &self.0).poll_close(cx)
}
}
#[cfg(windows)]
impl<T, M> AsyncWrite for Pipe<T, M>
where
M: Mode,
T: Write + Send + 'static,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.get_mut().0).poll_write(cx, buf)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.get_mut().0).poll_write_vectored(cx, bufs)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().0).poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().0).poll_close(cx)
}
}
fn closed() -> io::Error {
io::Error::new(
io::ErrorKind::BrokenPipe,
"the pipe to the child's standard input is closed",
)
}