use crate::{config::Config, context::Context};
use std::{
io::{self, Read, Write},
process::{ChildStderr, ChildStdin, ChildStdout},
thread::{self, JoinHandle},
};
#[derive(Debug)]
pub(crate) struct Waiter {
stdin: Option<JoinHandle<io::Result<()>>>,
stdout: JoinHandle<io::Result<Option<Vec<u8>>>>,
stderr: JoinHandle<io::Result<Option<Vec<u8>>>>,
}
impl Waiter {
fn spawn_standard_stream_handler(
capture_stream: bool,
mut source: impl Read + Send + 'static,
mut relay_sink: impl Write + Send + 'static,
) -> JoinHandle<io::Result<Option<Vec<u8>>>> {
thread::spawn(move || -> io::Result<Option<Vec<u8>>> {
let mut collected = if capture_stream {
Some(Vec::new())
} else {
None
};
let buffer = &mut [0; 256];
loop {
let length = source.read(buffer)?;
if (length) == 0 {
break;
}
if let Some(collected) = &mut collected {
collected.extend(&buffer[..length]);
}
if !capture_stream {
relay_sink.write_all(&buffer[..length])?;
}
}
Ok(collected)
})
}
pub(crate) fn spawn_standard_stream_relaying<Stdout, Stderr>(
context: &Context<Stdout, Stderr>,
config: &Config,
mut child_stdin: ChildStdin,
child_stdout: ChildStdout,
child_stderr: ChildStderr,
) -> Self
where
Stdout: Write + Send + Clone + 'static,
Stderr: Write + Send + Clone + 'static,
{
let stdin_join_handle = config.stdin.clone().map(|config_stdin| {
thread::spawn(move || -> io::Result<()> {
child_stdin.write_all(&config_stdin)?;
Ok(())
})
});
let stdout_join_handle = Self::spawn_standard_stream_handler(
config.capture_stdout,
child_stdout,
context.stdout.clone(),
);
let stderr_join_handle = Self::spawn_standard_stream_handler(
config.capture_stderr,
child_stderr,
context.stderr.clone(),
);
Waiter {
stdin: stdin_join_handle,
stdout: stdout_join_handle,
stderr: stderr_join_handle,
}
}
pub(crate) fn join(self) -> io::Result<CollectedOutput> {
if let Some(stdin) = self.stdin {
stdin.join().expect("stdout relaying thread panicked")?;
}
Ok(CollectedOutput {
stdout: self
.stdout
.join()
.expect("stdout relaying thread panicked")?,
stderr: self
.stderr
.join()
.expect("stderr relaying thread panicked")?,
})
}
}
#[derive(Debug)]
pub(crate) struct CollectedOutput {
pub(crate) stdout: Option<Vec<u8>>,
pub(crate) stderr: Option<Vec<u8>>,
}