From a3249350055e0a5085b0b73bc11e6c2905cc2dc9 Mon Sep 17 00:00:00 2001 From: Steppy Date: Fri, 7 Feb 2025 14:05:20 +0100 Subject: [PATCH] Rework how EnvCommand and ShellInterface interact with each other --- src/environment.rs | 35 +++++++++++++-- src/integration_test.rs | 52 ++++++++++++++++++++++ src/main.rs | 4 +- src/shell_interface.rs | 99 ++++++++++++++++++----------------------- 4 files changed, 131 insertions(+), 59 deletions(-) create mode 100644 src/integration_test.rs diff --git a/src/environment.rs b/src/environment.rs index 454e09f..6955767 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -1,3 +1,7 @@ +use crate::shell_interface::{ + build_command_from_shell_command, CommandOutput, CommandResult, ExitStatus, ShellCommand, + ShellInterface, StartError, +}; use std::env::VarError; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; @@ -32,9 +36,9 @@ impl Environment for Prod { } fn set_var(&mut self, key: K, value: V) - where - K: AsRef, - V: AsRef + where + K: AsRef, + V: AsRef, { env::set_var(key, value); } @@ -49,3 +53,28 @@ impl Environment for Prod { Ok(buffer.trim().to_string()) } } + +impl ShellInterface for Prod { + fn run_command(&mut self, command: ShellCommand) -> CommandResult { + CommandResult { + result: build_command_from_shell_command(&command) + .status() + .map(ExitStatus::from) + .map_err(StartError::from), + command, + } + } + + fn collect_command_output( + &mut self, + command: ShellCommand, + ) -> CommandResult { + CommandResult { + result: build_command_from_shell_command(&command) + .output() + .map(CommandOutput::from) + .map_err(StartError::from), + command, + } + } +} diff --git a/src/integration_test.rs b/src/integration_test.rs new file mode 100644 index 0000000..160349e --- /dev/null +++ b/src/integration_test.rs @@ -0,0 +1,52 @@ +use crate::environment::Environment; +use crate::shell_interface::{ + CommandOutput, CommandResult, ExitStatus, ShellCommand, ShellInterface, StartError, +}; +use std::env::VarError; +use std::ffi::{OsStr, OsString}; +use std::io::Error; +use std::path::PathBuf; + +struct TestEnvironment {} + +impl Environment for TestEnvironment { + fn args_os(&self) -> Vec { + todo!() + } + + fn var(&self, key: K) -> Result + where + K: AsRef, + { + todo!() + } + + fn set_var(&mut self, key: K, value: V) + where + K: AsRef, + V: AsRef, + { + todo!() + } + + fn get_home_directory(&self) -> Option { + todo!() + } + + fn read_line(&mut self) -> Result { + todo!() + } +} + +impl ShellInterface for TestEnvironment { + fn run_command(&mut self, command: ShellCommand) -> CommandResult { + todo!() + } + + fn collect_command_output( + &mut self, + command: ShellCommand, + ) -> CommandResult { + todo!() + } +} diff --git a/src/main.rs b/src/main.rs index 534acdf..05e0ba2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ mod os_str_extension; mod os_string_builder; mod server; mod shell_interface; +#[cfg(test)] +mod integration_test; use crate::action::{Action, FileAction, ServerActions}; use crate::environment::{Environment, Prod}; @@ -143,7 +145,7 @@ pub struct Application { impl Application where - E: Environment, + E: Environment + ShellInterface, { pub fn run(&mut self) -> Result<(), String> { let args = Args::try_parse_from(self.environment.args_os()).map_err(|e| e.to_string())?; diff --git a/src/shell_interface.rs b/src/shell_interface.rs index 75f0063..1bf318d 100644 --- a/src/shell_interface.rs +++ b/src/shell_interface.rs @@ -5,7 +5,6 @@ use std::error::Error; use std::ffi::OsString; use std::fmt::{Debug, Display, Formatter}; use std::iter::once; -use std::marker::PhantomData; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; use std::{io, process}; @@ -13,11 +12,44 @@ use std::{io, process}; #[derive(Debug)] pub struct EnvCommand<'a, E> { command: ShellCommand, - phantom_data: PhantomData<&'a E>, - #[cfg(test)] environment: &'a mut E, } +impl EnvCommand<'_, E> +where + E: ShellInterface, +{ + pub fn run(self) -> CommandResult { + self.environment.run_command(self.command) + } + + pub fn output(self) -> CommandResult { + self.environment.collect_command_output(self.command) + } + + pub fn run_logged(self, logger: &Logger) -> CommandResult + where + Self: Sized, + { + match logger.level { + LogLevel::Debug | LogLevel::Info => { + let res = self.run(); + CommandResult { + result: res.result.map(LoggedRunOutput::from), + command: res.command, + } + } + LogLevel::Error => { + let res = self.output(); + CommandResult { + result: res.result.map(LoggedRunOutput::from), + command: res.command, + } + } + } + } +} + #[derive(Debug, Clone)] pub enum ShellCommand { Ssh { @@ -38,12 +70,10 @@ pub enum ShellCommand { } impl ShellCommand { - pub fn in_env(self, _environment: &mut E) -> EnvCommand { + pub fn in_env(self, environment: &mut E) -> EnvCommand { EnvCommand { command: self, - phantom_data: Default::default(), - #[cfg(test)] - environment: _environment, + environment, } } } @@ -120,8 +150,7 @@ impl Display for ShellCommand { } } -//TODO remove crate visibility once it isn't needed anymore -pub(crate) fn command_to_string(command: &Command) -> String { +fn command_to_string(command: &Command) -> String { once(command.get_program().to_string_lossy().to_string()) .chain(command.get_args().map(|arg| { let arg_str = arg.to_string_lossy(); @@ -144,7 +173,7 @@ macro_rules! cmd { }}; } -fn build_command_from_shell_command(shell_command: &ShellCommand) -> Command { +pub fn build_command_from_shell_command(shell_command: &ShellCommand) -> Command { match shell_command { ShellCommand::Ssh { address, @@ -191,51 +220,11 @@ fn build_command_from_shell_command(shell_command: &ShellCommand) -> Command { } pub trait ShellInterface { - fn run(self) -> CommandResult; - fn output(self) -> CommandResult; - fn run_logged(self, logger: &Logger) -> CommandResult - where - Self: Sized, - { - match logger.level { - LogLevel::Debug | LogLevel::Info => { - let res = self.run(); - CommandResult { - result: res.result.map(LoggedRunOutput::from), - command: res.command, - } - } - LogLevel::Error => { - let res = self.output(); - CommandResult { - result: res.result.map(LoggedRunOutput::from), - command: res.command, - } - } - } - } -} - -impl ShellInterface for EnvCommand<'_, E> { - fn run(self) -> CommandResult { - CommandResult { - result: build_command_from_shell_command(&self.command) - .status() - .map(ExitStatus::from) - .map_err(StartError), - command: self.command, - } - } - - fn output(self) -> CommandResult { - CommandResult { - result: build_command_from_shell_command(&self.command) - .output() - .map(CommandOutput::from) - .map_err(StartError), - command: self.command, - } - } + fn run_command(&mut self, command: ShellCommand) -> CommandResult; + fn collect_command_output( + &mut self, + command: ShellCommand, + ) -> CommandResult; } pub trait MaybeCast {