From f3e965394ed305698129ec5e419404384c2ea8de Mon Sep 17 00:00:00 2001 From: Leonard Steppy Date: Tue, 17 Dec 2024 10:31:02 +0100 Subject: [PATCH] Implement command module --- src/command.rs | 78 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 8 deletions(-) diff --git a/src/command.rs b/src/command.rs index acb3c27..de280eb 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,31 +1,93 @@ -use crate::logger::Logger; +use crate::log; +use crate::logger::{LogLevel, Logger}; +use std::error::Error; use std::fmt::{Display, Formatter}; use std::io; -use std::process::{Command, ExitStatus}; +use std::process::{Command, ExitStatus, Stdio}; pub trait LogRunnable { fn run(&mut self, logger: &Logger) -> Result<(), ExecutionError>; } impl LogRunnable for Command { - //TODO use specific execution error as error type - fn run(&mut self, logger: &Logger) -> Result<(), ExecutionError> { - todo!("depending on log level, pipe output and only print with logger on error") + fn run(&mut self, logger: &Logger) -> Result<(), SpecificExecutionError> { + //TODO I'm not happy yet with the implementation of this method + match logger.level { + LogLevel::Debug | LogLevel::Info => self + .status() + .map_err(ExecutionError::StartError) + .and_then(|status| { + if status.success() { + Ok(()) + } else { + Err(ExecutionError::BadExitStatus(status)) + } + }), + LogLevel::Error => self + .stdout(Stdio::piped()) + .output() + .map_err(ExecutionError::StartError) + .and_then(|output| { + if output.status.success() { + Ok(()) + } else { + log!(logger, error, "{}", String::from_utf8_lossy(&output.stderr)); + Err(ExecutionError::BadExitStatus(output.status)) + } + }), + } + .map_err(|error| SpecificExecutionError { + command: self, + error, + }) } } -//TODO show command in specific execution error +#[derive(Debug)] +pub struct SpecificExecutionError<'a> { + pub command: &'a Command, + pub error: ExecutionError, +} + +impl Display for SpecificExecutionError<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Failed to execute command {}: {}", + self.command, self.error + ) + } +} + +impl Error for SpecificExecutionError<'_> {} + #[derive(Debug)] pub enum ExecutionError { StartError(io::Error), BadExitStatus(ExitStatus), } +impl From for ExecutionError { + fn from(value: io::Error) -> Self { + Self::StartError(value) + } +} + +impl From for ExecutionError { + fn from(value: ExitStatus) -> Self { + Self::BadExitStatus(value) + } +} + impl Display for ExecutionError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { ExecutionError::StartError(e) => write!(f, "failed to start command: {}", e), - ExecutionError::BadExitStatus(status) => write!(f, "command failed with exit status: {}", status), + ExecutionError::BadExitStatus(status) => { + write!(f, "command failed with exit status: {}", status) + } } } -} \ No newline at end of file +} + +impl Error for ExecutionError {}