use core::pin::Pin;
use core::task::Context;
use core::task::Poll;
use std::io;
use std::os::fd::OwnedFd;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::IntoRawFd;
use syscalls::Errno;
use tokio::io::AsyncRead;
use tokio::io::AsyncWrite;
use tokio::io::ReadBuf;
use super::fd::AsyncFd;
use super::fd::Fd;
use super::fd::pipe;
#[derive(Debug)]
pub struct Stdio(InnerStdio);
#[derive(Debug)]
pub struct ChildStdin(AsyncFd);
#[derive(Debug)]
pub struct ChildStdout(AsyncFd);
#[derive(Debug)]
pub struct ChildStderr(AsyncFd);
#[derive(Debug)]
enum InnerStdio {
Inherit,
Null,
Piped,
File(Fd),
}
impl Default for Stdio {
fn default() -> Self {
Self(InnerStdio::Inherit)
}
}
impl Stdio {
pub fn piped() -> Self {
Self(InnerStdio::Piped)
}
pub fn inherit() -> Self {
Self(InnerStdio::Inherit)
}
pub fn null() -> Self {
Self(InnerStdio::Null)
}
pub(super) fn pipes(&self, readable: bool) -> Result<(Option<Fd>, Option<Fd>), Errno> {
match &self.0 {
InnerStdio::Inherit => Ok((None, None)),
InnerStdio::Null => Ok((None, Some(Fd::null(readable)?))),
InnerStdio::Piped => {
let (reader, writer) = pipe()?;
let (parent, child) = if readable {
(writer, reader)
} else {
(reader, writer)
};
Ok((Some(parent), Some(child)))
}
InnerStdio::File(file) => Ok((None, Some(file.dup()?))),
}
}
}
impl<T: IntoRawFd> From<T> for Stdio {
fn from(f: T) -> Self {
Self(InnerStdio::File(Fd::new(f.into_raw_fd())))
}
}
impl From<Stdio> for std::process::Stdio {
fn from(stdio: Stdio) -> Self {
match stdio.0 {
InnerStdio::Inherit => Self::inherit(),
InnerStdio::Null => Self::null(),
InnerStdio::Piped => Self::piped(),
InnerStdio::File(fd) => Self::from(std::fs::File::from(fd)),
}
}
}
impl ChildStdin {
pub(super) fn new(fd: Fd) -> Result<Self, Errno> {
AsyncFd::writable(fd).map(Self)
}
}
impl ChildStdout {
pub(super) fn new(fd: Fd) -> Result<Self, Errno> {
AsyncFd::readable(fd).map(Self)
}
}
impl ChildStderr {
pub(super) fn new(fd: Fd) -> Result<Self, Errno> {
AsyncFd::readable(fd).map(Self)
}
}
impl AsyncWrite for ChildStdin {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<tokio::io::Result<usize>> {
Pin::new(&mut self.0).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_shutdown(cx)
}
}
impl AsyncRead for ChildStdout {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut ReadBuf,
) -> Poll<tokio::io::Result<()>> {
Pin::new(&mut self.0).poll_read(cx, buf)
}
}
impl AsyncRead for ChildStderr {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut ReadBuf,
) -> Poll<tokio::io::Result<()>> {
Pin::new(&mut self.0).poll_read(cx, buf)
}
}
impl FromRawFd for ChildStdin {
unsafe fn from_raw_fd(fd: i32) -> Self {
Self::new(Fd::new(fd)).unwrap()
}
}
impl From<OwnedFd> for ChildStdin {
fn from(fd: OwnedFd) -> Self {
Self::new(fd.into()).unwrap()
}
}
impl FromRawFd for ChildStdout {
unsafe fn from_raw_fd(fd: i32) -> Self {
Self::new(Fd::new(fd)).unwrap()
}
}
impl From<OwnedFd> for ChildStdout {
fn from(fd: OwnedFd) -> Self {
Self::new(fd.into()).unwrap()
}
}
impl FromRawFd for ChildStderr {
unsafe fn from_raw_fd(fd: i32) -> Self {
Self::new(Fd::new(fd)).unwrap()
}
}
impl From<OwnedFd> for ChildStderr {
fn from(fd: OwnedFd) -> Self {
Self::new(fd.into()).unwrap()
}
}
impl From<tokio::process::ChildStdin> for ChildStdin {
fn from(io: tokio::process::ChildStdin) -> Self {
let fd = io.as_raw_fd();
let fd = unsafe { libc::dup(fd) };
drop(io);
unsafe { Self::from_raw_fd(fd) }
}
}
impl From<tokio::process::ChildStdout> for ChildStdout {
fn from(io: tokio::process::ChildStdout) -> Self {
let fd = io.as_raw_fd();
let fd = unsafe { libc::dup(fd) };
drop(io);
unsafe { Self::from_raw_fd(fd) }
}
}
impl From<tokio::process::ChildStderr> for ChildStderr {
fn from(io: tokio::process::ChildStderr) -> Self {
let fd = io.as_raw_fd();
let fd = unsafe { libc::dup(fd) };
drop(io);
unsafe { Self::from_raw_fd(fd) }
}
}