From ffd8f71f8b531418306e24dcac948d948bcba7c0 Mon Sep 17 00:00:00 2001 From: Steppy Date: Mon, 3 Feb 2025 12:33:15 +0100 Subject: [PATCH] Add collect_full_output method to commands --- src/command.rs | 92 ++++++++++++++++++++++++++++++++++++-------------- src/main.rs | 21 +++++------- 2 files changed, 75 insertions(+), 38 deletions(-) diff --git a/src/command.rs b/src/command.rs index 6474703..5636d99 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,27 +1,34 @@ use crate::log; use crate::logger::{LogLevel, Logger}; use std::error::Error; -use std::fmt::{Display, Formatter}; +use std::fmt::{Debug, Display, Formatter}; use std::io; use std::iter::once; use std::process::{Command, ExitStatus, Output}; pub trait LogRunnable { - fn run(&mut self, logger: &Logger) -> Result<(), SpecificExecutionError>; - fn collect_output(&mut self) -> Result; - //TODO collect_full_output + fn run(&mut self, logger: &Logger) -> Result<(), CommandSpecificError>; + fn collect_output(&mut self) -> Result>; + fn collect_full_output(&mut self) -> Result>; } impl LogRunnable for Command { - fn run(&mut self, logger: &Logger) -> Result<(), SpecificExecutionError> { - run(self, logger).map_err(|error| SpecificExecutionError { + fn run(&mut self, logger: &Logger) -> Result<(), CommandSpecificError> { + run(self, logger).map_err(|error| CommandSpecificError { command: self, error, }) } - fn collect_output(&mut self) -> Result { - collect_output(self, None).map_err(|error| SpecificExecutionError { + fn collect_output(&mut self) -> Result> { + collect_output(self, None).map_err(|error| CommandSpecificError { + command: self, + error, + }) + } + + fn collect_full_output(&mut self) -> Result> { + collect_full_output(self).map_err(|error| CommandSpecificError { command: self, error, }) @@ -47,7 +54,7 @@ fn collect_output( command: &mut Command, logger: Option<&Logger>, ) -> Result { - let output = command.output()?; //pipes stdout and stderr automatically + let output = collect_full_output(command)?; if !output.status.success() { if let Some(logger) = logger { log!(logger, error, "{}", String::from_utf8_lossy(&output.stdout)); @@ -58,13 +65,20 @@ fn collect_output( Ok(output) } -#[derive(Debug)] -pub struct SpecificExecutionError<'a> { - pub command: &'a Command, - pub error: ExecutionError, +fn collect_full_output(command: &mut Command) -> Result { + Ok(command.output()?) } -impl Display for SpecificExecutionError<'_> { +#[derive(Debug)] +pub struct CommandSpecificError<'a, E> { + pub command: &'a Command, + pub error: E, +} + +impl Display for CommandSpecificError<'_, E> +where + E: Display, +{ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, @@ -76,22 +90,52 @@ impl Display for SpecificExecutionError<'_> { } fn command_to_string(command: &Command) -> String { - once(command.get_program().to_string_lossy()) - .chain(command.get_args().map(|arg| arg.to_string_lossy())) + once(command.get_program().to_string_lossy().to_string()) + .chain(command.get_args().map(|arg| { + let arg_str = arg.to_string_lossy(); + if arg_str.contains(' ') { + format!("\"{arg_str}\"") + } else { + arg_str.to_string() + } + })) .collect::>() .join(" ") } -impl Error for SpecificExecutionError<'_> {} +impl Error for CommandSpecificError<'_, E> where E: Debug + Display {} + +#[derive(Debug)] +pub struct StartError(io::Error); + +impl From for StartError { + fn from(value: io::Error) -> Self { + StartError(value) + } +} + +impl Display for StartError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "Failed to start command: {}", self.0) + } +} + +impl Error for StartError {} #[derive(Debug)] pub enum ExecutionError { - StartError(io::Error), + StartError(StartError), BadExitStatus(ExitStatus), } impl From for ExecutionError { fn from(value: io::Error) -> Self { + Self::StartError(StartError(value)) + } +} + +impl From for ExecutionError { + fn from(value: StartError) -> Self { Self::StartError(value) } } @@ -105,10 +149,8 @@ impl From for ExecutionError { 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 {}", status) - } + ExecutionError::StartError(e) => Display::fmt(e, f), + ExecutionError::BadExitStatus(status) => write!(f, "Command failed with {}", status), } } } @@ -117,7 +159,7 @@ impl Error for ExecutionError {} #[cfg(test)] mod test { - use crate::command::{ExecutionError, LogRunnable, SpecificExecutionError}; + use crate::command::{CommandSpecificError, ExecutionError, LogRunnable}; use crate::logger::Logger; use std::path::PathBuf; use std::process::Command; @@ -126,7 +168,7 @@ mod test { fn test_unknown_command() { let mut command = Command::new("python7"); let Err( - e @ SpecificExecutionError { + e @ CommandSpecificError { error: ExecutionError::StartError(_), .. }, @@ -143,7 +185,7 @@ mod test { fn test_error() { let mut command = Command::new("python3"); let Err( - e @ SpecificExecutionError { + e @ CommandSpecificError { error: ExecutionError::BadExitStatus(_), .. }, diff --git a/src/main.rs b/src/main.rs index 10c6fdf..bc62143 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ mod os_string_builder; mod server; use crate::action::{Action, FileAction, ServerActions}; -use crate::command::{ExecutionError, LogRunnable, SpecificExecutionError}; +use crate::command::{CommandSpecificError, ExecutionError, LogRunnable}; use crate::file::{FileMatcher, FileNameInfo}; use crate::logger::{LogLevel, Logger}; use crate::os_string_builder::ReplaceWithOsStr; @@ -216,7 +216,7 @@ fn main() -> Result<(), String> { .collect::>() .join(&OsString::from(",")) + "}"; - + //TODO handle bad exit status let realpath_output = ShellCmd::new("ssh") .arg(ssh_address) @@ -234,17 +234,12 @@ fn main() -> Result<(), String> { check_file_exists_on_server(file, ssh_address, &file_server.server_directory_path)?; } } - ServerAddress::Localhost => { - for file in &files { - check_local_file_exists(file_server.server_directory_path.join(file))?; - } - } + ServerAddress::Localhost => files + .iter() + .map(|file| file_server.server_directory_path.join(file)) + .try_for_each(check_local_file_exists)?, }, - None => { - for file in &files { - check_local_file_exists(file)?; - } - } + None => files.iter().try_for_each(check_local_file_exists)?, } let file_details = files @@ -587,7 +582,7 @@ where .collect_output() { Ok(_) => Ok(()), //file exists on file server - Err(SpecificExecutionError { + Err(CommandSpecificError { error: ExecutionError::BadExitStatus(_), //test failed .. }) => Err(format!(