74 lines
1.9 KiB
Rust
74 lines
1.9 KiB
Rust
use std::{
|
|
ffi::OsStr,
|
|
process::Command,
|
|
process::{Child, ExitStatus, Output},
|
|
};
|
|
|
|
use thiserror::Error;
|
|
|
|
/// Errors that can be created when running the `PipeExecutor`
|
|
#[derive(Debug, Error)]
|
|
pub enum ExecutorError {
|
|
/// IO Error
|
|
#[error(transparent)]
|
|
Io(#[from] std::io::Error),
|
|
|
|
/// Failed Command Error
|
|
#[error("Failed to run command '{} {}', exit code {exit_code}",
|
|
.command
|
|
.get_program()
|
|
.to_string_lossy(),
|
|
.command
|
|
.get_args()
|
|
.map(OsStr::to_string_lossy)
|
|
.collect::<Vec<_>>()
|
|
.join(" "),
|
|
)]
|
|
FailedCommand { command: Command, exit_code: i32 },
|
|
|
|
/// No stdin Error
|
|
#[error("Unable to get mutable stdin")]
|
|
NoStdIn,
|
|
}
|
|
|
|
/// A lazy piped command executor.
|
|
pub struct Executor<'a, S>
|
|
where
|
|
S: AsRef<[u8]> + ?Sized,
|
|
{
|
|
stdin: Option<&'a S>,
|
|
piped_commands: Box<dyn FnMut(Option<&'a S>) -> Result<Child, ExecutorError> + 'a>,
|
|
}
|
|
|
|
impl<'a, S> Executor<'a, S>
|
|
where
|
|
S: AsRef<[u8]> + ?Sized,
|
|
{
|
|
/// Construct a `PipeExecutor`.
|
|
pub fn new(
|
|
stdin: Option<&'a S>,
|
|
piped_commands: impl FnMut(Option<&'a S>) -> Result<Child, ExecutorError> + 'a,
|
|
) -> Self {
|
|
Self {
|
|
stdin,
|
|
piped_commands: Box::new(piped_commands),
|
|
}
|
|
}
|
|
|
|
/// Retrieves the `ExitStatus` of the last command.
|
|
///
|
|
/// # Errors
|
|
/// Will error if the exit status of any chained commands fail.
|
|
pub fn status(&mut self) -> Result<ExitStatus, ExecutorError> {
|
|
Ok((self.piped_commands)(self.stdin)?.wait()?)
|
|
}
|
|
|
|
/// Retrieves the `Output` of the last command.
|
|
///
|
|
/// # Errors
|
|
/// Will error if the exit status of any chained commands fail.
|
|
pub fn output(&mut self) -> Result<Output, ExecutorError> {
|
|
Ok((self.piped_commands)(self.stdin)?.wait_with_output()?)
|
|
}
|
|
}
|