use std::process::Output;
#[derive(Debug)]
pub enum Error {
Raw(RawCommandOutput),
FailedToExecute(std::io::Error),
}
#[derive(Debug)]
pub struct CommandOutput<T> {
pub raw: RawCommandOutput,
pub interpreted_to: T,
}
#[derive(Debug)]
pub struct RawCommandOutput {
pub status: i32,
pub stdout: String,
pub stderr: String,
}
impl RawCommandOutput {
pub fn success(&self) -> bool {
self.status == 0
}
}
impl From<std::process::Output> for RawCommandOutput {
fn from(output: Output) -> Self {
let status = output.status.code().unwrap_or(-1);
let stdout = String::from_utf8(output.stdout).unwrap_or_default();
let stderr = String::from_utf8(output.stderr).unwrap_or_default();
RawCommandOutput {
status,
stdout,
stderr,
}
}
}
impl TryFrom<RawCommandOutput> for CommandOutput<()> {
type Error = Error;
fn try_from(raw: RawCommandOutput) -> Result<Self, Self::Error> {
match raw.success() {
true => raw.interpret_to(()),
false => Err(Error::Raw(raw)),
}
}
}
impl TryFrom<Result<std::process::Output, std::io::Error>> for RawCommandOutput {
type Error = Error;
fn try_from(result: Result<std::process::Output, std::io::Error>) -> Result<Self, Self::Error> {
match result {
Ok(output) => Ok(RawCommandOutput::from(output)),
Err(err) => Err(Error::FailedToExecute(err)),
}
}
}
impl RawCommandOutput {
pub fn interpret_to<T>(self, item: T) -> Result<CommandOutput<T>, Error> {
Ok(CommandOutput {
raw: self,
interpreted_to: item,
})
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Raw(raw) => write!(f, "{}", raw.stderr),
Error::FailedToExecute(err) => write!(f, "Failed to execute command: {err}"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::process::Command;
#[test]
fn test_command_wrapper() {
let output = Command::new("ls").arg("hello").output();
let raw = RawCommandOutput::try_from(output);
assert!(raw.is_ok());
let raw = raw.unwrap();
assert!(!raw.success());
let output = Command::new("nothing").arg("hello").output();
let raw = RawCommandOutput::try_from(output);
assert!(raw.is_err());
let err = raw.err().unwrap();
assert_eq!(
err.to_string(),
"Failed to execute command: No such file or directory (os error 2)"
);
}
}