use super::{ChildStderr, ChildStdin, ChildStdout, Error};
use std::io;
use std::process::{ExitStatus, Output};
use tokio::io::AsyncReadExt;
use tokio::try_join;
#[derive(Debug)]
pub(crate) enum RemoteChildImp {
#[cfg(feature = "process-mux")]
ProcessImpl(super::process_impl::RemoteChild),
#[cfg(feature = "native-mux")]
NativeMuxImpl(super::native_mux_impl::RemoteChild),
}
#[cfg(feature = "process-mux")]
impl From<super::process_impl::RemoteChild> for RemoteChildImp {
fn from(imp: super::process_impl::RemoteChild) -> Self {
RemoteChildImp::ProcessImpl(imp)
}
}
#[cfg(feature = "native-mux")]
impl From<super::native_mux_impl::RemoteChild> for RemoteChildImp {
fn from(imp: super::native_mux_impl::RemoteChild) -> Self {
RemoteChildImp::NativeMuxImpl(imp)
}
}
macro_rules! delegate {
($impl:expr, $var:ident, $then:block) => {{
match $impl {
#[cfg(feature = "process-mux")]
RemoteChildImp::ProcessImpl($var) => $then,
#[cfg(feature = "native-mux")]
RemoteChildImp::NativeMuxImpl($var) => $then,
}
}};
}
#[derive(Debug)]
pub struct Child<S> {
session: S,
imp: RemoteChildImp,
stdin: Option<ChildStdin>,
stdout: Option<ChildStdout>,
stderr: Option<ChildStderr>,
}
impl<S> Child<S> {
pub(crate) fn new(
session: S,
(imp, stdin, stdout, stderr): (
RemoteChildImp,
Option<ChildStdin>,
Option<ChildStdout>,
Option<ChildStderr>,
),
) -> Self {
Self {
session,
stdin,
stdout,
stderr,
imp,
}
}
pub async fn disconnect(self) -> io::Result<()> {
delegate!(self.imp, imp, { imp.disconnect().await })
}
pub async fn wait(mut self) -> Result<ExitStatus, Error> {
self.stdin().take();
delegate!(self.imp, imp, { imp.wait().await })
}
pub async fn wait_with_output(mut self) -> Result<Output, Error> {
self.stdin().take();
let child_stdout = self.stdout.take();
let stdout_read = async move {
let mut stdout = Vec::new();
if let Some(mut child_stdout) = child_stdout {
child_stdout
.read_to_end(&mut stdout)
.await
.map_err(Error::ChildIo)?;
}
Ok::<_, Error>(stdout)
};
let child_stderr = self.stderr.take();
let stderr_read = async move {
let mut stderr = Vec::new();
if let Some(mut child_stderr) = child_stderr {
child_stderr
.read_to_end(&mut stderr)
.await
.map_err(Error::ChildIo)?;
}
Ok::<_, Error>(stderr)
};
let (stdout, stderr) = try_join!(stdout_read, stderr_read)?;
Ok(Output {
status: self.wait().await?,
stdout,
stderr,
})
}
pub fn stdin(&mut self) -> &mut Option<ChildStdin> {
&mut self.stdin
}
pub fn stdout(&mut self) -> &mut Option<ChildStdout> {
&mut self.stdout
}
pub fn stderr(&mut self) -> &mut Option<ChildStderr> {
&mut self.stderr
}
}
impl<S: Clone> Child<S> {
pub fn session(&self) -> S {
self.session.clone()
}
}