use std::{
io::{self, Write},
process::{Command, ExitStatus, Stdio},
};
pub(super) fn command_available(command: &str) -> bool {
crate::executable::find_on_path(command).is_some()
}
pub(super) fn command_output(command: &str, args: &[&str]) -> Option<Vec<u8>> {
let output = Command::new(command)
.args(args)
.stdin(Stdio::null())
.stderr(Stdio::null())
.output()
.ok()?;
output.status.success().then_some(output.stdout)
}
pub(super) fn write_command_stdin(program: &str, args: &[&str], bytes: &[u8]) -> io::Result<()> {
let mut child = Command::new(program)
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.map_err(|error| io::Error::new(error.kind(), format!("spawn {program}: {error}")))?;
let write_result: io::Result<()> = (|| {
let mut stdin = child.stdin.take().ok_or_else(|| {
io::Error::new(io::ErrorKind::BrokenPipe, format!("{program} stdin closed"))
})?;
stdin.write_all(bytes)?;
stdin.flush()?;
Ok(())
})();
let status = child.wait()?;
resolve_command_write(program, status, write_result)
}
fn resolve_command_write(
program: &str,
status: ExitStatus,
write_result: io::Result<()>,
) -> io::Result<()> {
if !status.success() {
return Err(io::Error::other(format!("{program} exited with {status}")));
}
write_result
}
#[cfg(test)]
#[path = "process_tests.rs"]
mod tests;