diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..89226b6 --- /dev/null +++ b/src/logger.rs @@ -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(&self, message: S) where S: ToString { + self.log(LogLevel::$level, message.to_string()); + } + }; +} + +impl Logger { + pub fn log(&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"); + } +} diff --git a/src/main.rs b/src/main.rs index 87f3bd0..b9ec631 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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, + /// 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::, 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")