Merge pull request 'Add proper logging' (#9) from logging into master

Reviewed-on: https://stupstech.de/dev/Mr_Steppy/multi-ssh/pulls/9
This commit is contained in:
Leonard Steppy 2024-12-17 06:03:26 +01:00
commit bdd1652595
2 changed files with 100 additions and 16 deletions

56
src/logger.rs Normal file
View File

@ -0,0 +1,56 @@
use clap::ValueEnum;
#[derive(Debug, Default)]
pub struct Logger {
pub level: LogLevel,
}
macro_rules! define_log_function {
($name:ident, $level:ident) => {
pub fn $name<S>(&self, message: S) where S: ToString {
self.log(LogLevel::$level, message.to_string());
}
};
}
impl Logger {
pub fn log<S>(&self, level: LogLevel, message: S) where S: ToString {
if level >= self.level {
println!("{}", message.to_string());
}
}
define_log_function!(info, Info);
define_log_function!(debug, Debug);
define_log_function!(error, Error);
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, ValueEnum)]
pub enum LogLevel {
Debug,
#[default]
Info,
Error,
}
#[macro_export]
macro_rules! log {
($logger:expr, $level:ident, $($args:tt)*) => {
$logger.$level(format!($($args)*));
};
($logger:expr, $($args:tt)*) => {
log!($logger, info, $($args)*);
}
}
#[cfg(test)]
mod test {
use crate::logger::Logger;
#[test]
#[ignore]
fn syntax_test() {
let logger = Logger::default();
log!(logger, "Foo {}", "bar");
}
}

View File

@ -1,10 +1,12 @@
mod action;
mod file;
mod logger;
mod os_string_builder;
mod server;
use crate::action::{Action, FileAction, ServerActions};
use crate::file::{FileMatcher, FileNameInfo};
use crate::logger::{LogLevel, Logger};
use crate::os_string_builder::ReplaceWithOsStr;
use clap::{Parser, Subcommand, ValueEnum};
use lazy_regex::{lazy_regex, Lazy, Regex};
@ -41,6 +43,15 @@ struct Args {
/// The ssh names and optionally home directories of the servers to perform the action on
#[arg(num_args = 1.., value_parser = ServerReference::from_str)]
servers: Vec<ServerReference>,
/// How verbose logging output should be
#[arg(long, default_value = "info", conflicts_with_all = ["quiet", "info"])]
log_level: LogLevel,
/// Only log errors
#[arg(short, long, default_value = "false", conflicts_with_all = ["info"])]
quiet: bool,
/// Log additional debugging info
#[arg(short = 'v', long, default_value = "false")]
info: bool,
}
#[derive(Subcommand, Debug)]
@ -110,6 +121,17 @@ enum OldVersionPolicy {
fn main() -> Result<(), String> {
let args = Args::parse();
let logger = Logger {
//all the below options are conflicting with each other so an if else is fine
level: if args.quiet {
LogLevel::Error
} else if args.info {
LogLevel::Debug
} else {
args.log_level
},
};
let mut configured_servers = LazyCell::new(parse_server_configuration_from_env);
let servers = args
.servers
@ -136,11 +158,7 @@ fn main() -> Result<(), String> {
no_confirm,
file_name,
} => {
if servers.is_empty() {
println!("Please provide some servers to upload to. See --help");
return Ok(());
}
require_non_empty_servers(&servers)?;
start_ssh_agent()?;
let file_name_info =
@ -226,13 +244,13 @@ fn main() -> Result<(), String> {
})
.collect::<Result<Vec<_>, String>>()?;
println!("The following actions will be performed:");
log!(logger, "The following actions will be performed: ");
for server_actions in &actions {
println!("{server_actions}");
log!(logger, "{server_actions}");
}
if !no_confirm {
print!("Continue? [Y|n] ");
log!(logger, "Continue? [Y|n] ");
std::io::stdout().flush().expect("failed to flush stdout");
let mut buffer = String::new();
std::io::stdin()
@ -240,7 +258,7 @@ fn main() -> Result<(), String> {
.expect("failed to read stdin");
match buffer.to_lowercase().trim() {
"n" | "no" => {
println!("Aborting...");
log!(logger, "Aborting...");
return Ok(());
}
_ => {}
@ -249,7 +267,7 @@ fn main() -> Result<(), String> {
for server_actions in actions {
let server = server_actions.server;
println!("Performing actions on {}...", server.ssh_name);
log!(logger, "Performing actions on {}...", server.ssh_name);
for file_action in server_actions.actions {
match file_action.kind {
Action::Add | Action::Replace => {
@ -290,12 +308,13 @@ fn main() -> Result<(), String> {
}
}
println!("Done!");
log!(logger, "Done!");
}
Command::Command { command } => {
start_ssh_agent()?;
require_non_empty_servers(&servers)?;
for server in servers {
println!("Running command on '{}'...", server.ssh_name);
log!(logger, "Running command on '{}'...", server.ssh_name);
ShellCmd::new("ssh")
.arg(server.ssh_name)
.arg(osf!("cd ") + server.server_directory_path + "; " + &command)
@ -304,7 +323,7 @@ fn main() -> Result<(), String> {
.wait()
.map_err(|e| format!("failed to wait for ssh command completion: {e}"))?;
}
println!("Done!");
log!(logger, "Done!");
}
Command::Editor {
file,
@ -331,16 +350,17 @@ fn main() -> Result<(), String> {
.any(|entry| entry.file_name() == file_name)
{
return Err(format!(
"A file with the name {} already exists in {}. You can override it with --override",
"A file with the name {} already exists in {}. You can override it with --override or -f",
file_name.to_string_lossy(),
working_directory.to_string_lossy()
));
}
require_non_empty_servers(&servers)?;
start_ssh_agent()?;
for server in servers {
println!("Downloading file from {}...", server.ssh_name);
log!(logger, "Downloading file from {}...", server.ssh_name);
ShellCmd::new("scp")
.arg(osf!(&server.ssh_name) + ":" + server.server_directory_path.join(&file))
.arg(&working_directory)
@ -368,13 +388,21 @@ fn main() -> Result<(), String> {
.map_err(|e| format!("failed to upload file again: {e}"))?;
}
println!("Done!");
log!(logger, "Done!");
}
}
Ok(())
}
fn require_non_empty_servers(servers: &[Server]) -> Result<(), String> {
if servers.is_empty() {
Err("You did not provide any servers for this operation. Please see --help".to_string())
} else {
Ok(())
}
}
fn start_ssh_agent() -> Result<(), String> {
//start the ssh agent
let agent_output = ShellCmd::new("ssh-agent")