Rework how EnvCommand and ShellInterface interact with each other
This commit is contained in:
parent
d963d4ff88
commit
a324935005
@ -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<K, V>(&mut self, key: K, value: V)
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
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<ExitStatus, StartError> {
|
||||
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<CommandOutput, StartError> {
|
||||
CommandResult {
|
||||
result: build_command_from_shell_command(&command)
|
||||
.output()
|
||||
.map(CommandOutput::from)
|
||||
.map_err(StartError::from),
|
||||
command,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
52
src/integration_test.rs
Normal file
52
src/integration_test.rs
Normal file
@ -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<OsString> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn var<K>(&self, key: K) -> Result<String, VarError>
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_var<K, V>(&mut self, key: K, value: V)
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_home_directory(&self) -> Option<PathBuf> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn read_line(&mut self) -> Result<String, Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ShellInterface for TestEnvironment {
|
||||
fn run_command(&mut self, command: ShellCommand) -> CommandResult<ExitStatus, StartError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn collect_command_output(
|
||||
&mut self,
|
||||
command: ShellCommand,
|
||||
) -> CommandResult<CommandOutput, StartError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@ -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<E> {
|
||||
|
||||
impl<E> Application<E>
|
||||
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())?;
|
||||
|
||||
@ -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<E> EnvCommand<'_, E>
|
||||
where
|
||||
E: ShellInterface,
|
||||
{
|
||||
pub fn run(self) -> CommandResult<ExitStatus, StartError> {
|
||||
self.environment.run_command(self.command)
|
||||
}
|
||||
|
||||
pub fn output(self) -> CommandResult<CommandOutput, StartError> {
|
||||
self.environment.collect_command_output(self.command)
|
||||
}
|
||||
|
||||
pub fn run_logged(self, logger: &Logger) -> CommandResult<LoggedRunOutput, StartError>
|
||||
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<E>(self, _environment: &mut E) -> EnvCommand<E> {
|
||||
pub fn in_env<E>(self, environment: &mut E) -> EnvCommand<E> {
|
||||
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<ExitStatus, StartError>;
|
||||
fn output(self) -> CommandResult<CommandOutput, StartError>;
|
||||
fn run_logged(self, logger: &Logger) -> CommandResult<LoggedRunOutput, StartError>
|
||||
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<E> ShellInterface for EnvCommand<'_, E> {
|
||||
fn run(self) -> CommandResult<ExitStatus, StartError> {
|
||||
CommandResult {
|
||||
result: build_command_from_shell_command(&self.command)
|
||||
.status()
|
||||
.map(ExitStatus::from)
|
||||
.map_err(StartError),
|
||||
command: self.command,
|
||||
}
|
||||
}
|
||||
|
||||
fn output(self) -> CommandResult<CommandOutput, StartError> {
|
||||
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<ExitStatus, StartError>;
|
||||
fn collect_command_output(
|
||||
&mut self,
|
||||
command: ShellCommand,
|
||||
) -> CommandResult<CommandOutput, StartError>;
|
||||
}
|
||||
|
||||
pub trait MaybeCast<T> {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user