mod exit_status;
mod streams;
pub use self::{
exit_status::ExitStatus,
streams::{OutputStream, Stderr, Stdout},
};
use crate::error::{
FrameworkError,
FrameworkErrorKind::{ProcessError, TimeoutError},
};
use std::{
io::{self, Write},
process::{Child, ChildStdin},
sync::MutexGuard,
time::Duration,
};
use wait_timeout::ChildExt;
pub(super) type Guard<'cmd> = MutexGuard<'cmd, ()>;
#[derive(Debug)]
pub struct Process<'cmd> {
child: Child,
timeout: Duration,
stdout: Option<Stdout>,
stderr: Option<Stderr>,
stdin: ChildStdin,
guard: Option<Guard<'cmd>>,
}
impl<'cmd> Process<'cmd> {
pub(super) fn new(mut child: Child, timeout: Duration, guard: Option<Guard<'cmd>>) -> Self {
let stdout = child.stdout.take().map(Stdout::new);
let stderr = child.stderr.take().map(Stderr::new);
let stdin = child.stdin.take().unwrap();
Self {
child,
timeout,
stdout,
stderr,
stdin,
guard,
}
}
pub fn stdout(&mut self) -> &mut Stdout {
self.stdout
.as_mut()
.expect("child stdout not captured (use 'capture_stdout' method)")
}
pub fn stderr(&mut self) -> &mut Stderr {
self.stderr
.as_mut()
.expect("child stderr not captured (use 'capture_stderr' method)")
}
pub fn wait(mut self) -> Result<ExitStatus<'cmd>, FrameworkError> {
match self.child.wait_timeout(self.timeout)? {
Some(status) => {
let code = status.code().ok_or_else(|| {
err!(ProcessError, "no exit status returned from subprocess!")
})?;
Ok(ExitStatus::new(code, self.guard))
}
None => fail!(
TimeoutError,
"operation timed out after {} seconds",
self.timeout.as_secs()
),
}
}
}
impl<'cmd> Write for Process<'cmd> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.stdin.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.stdin.flush()
}
}