Compare commits

...

2 Commits

4 changed files with 132 additions and 60 deletions

View File

@ -1,9 +1,13 @@
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;
use std::{env, io};
pub trait Environment {
pub trait Environment: ShellInterface {
fn args_os(&self) -> Vec<OsString>;
fn var<K>(&self, key: K) -> Result<String, VarError>
where
@ -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
View 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!()
}
}

View File

@ -1,6 +1,8 @@
mod action;
mod environment;
mod file;
#[cfg(test)]
mod integration_test;
mod logger;
mod os_str_extension;
mod os_string_builder;
@ -14,7 +16,7 @@ use crate::logger::{LogLevel, Logger};
use crate::os_str_extension::OsStrExtension;
use crate::os_string_builder::ReplaceWithOsStr;
use crate::server::{RelativeLocalPathAnker, ServerAddress};
use crate::shell_interface::{ScpParam, ServerCommand, ShellCommand, ShellInterface};
use crate::shell_interface::{ScpParam, ServerCommand, ShellCommand};
use clap::{Parser, Subcommand, ValueEnum};
use lazy_regex::{lazy_regex, Lazy, Regex};
use server::{Server, ServerReference};

View File

@ -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> {