From dcd64e0cb06c2608e45739108735145c09b5f1ed Mon Sep 17 00:00:00 2001 From: pvshvp-oss Date: Mon, 6 May 2024 16:09:42 -0500 Subject: [PATCH] WIP --- paxy-cli/src/lib.rs | 42 ++++++------ paxy-gui/src/lib.rs | 30 ++++----- paxy/src/app/config.rs | 106 +++++++++++++++++++++++------- paxy/src/app/ui.rs | 142 ++++++++++++++++++++++++++++------------- 4 files changed, 213 insertions(+), 107 deletions(-) diff --git a/paxy-cli/src/lib.rs b/paxy-cli/src/lib.rs index e81535e..3402db6 100644 --- a/paxy-cli/src/lib.rs +++ b/paxy-cli/src/lib.rs @@ -58,7 +58,7 @@ mod cli_template { )] pub struct CliTemplate { #[command(flatten)] - pub global_arguments: ui::cli_template::GlobalArgs, + pub global_args: ui::cli_template::GlobalArgs, #[command(subcommand)] pub entity: Option, @@ -67,43 +67,39 @@ mod cli_template { /// Implement a trait that can extract standard global arguments from our /// own CLI template impl ui::GlobalArguments for CliTemplate { - type L = clap_verbosity_flag::InfoLevel; - - fn config_file(&self) -> &Option { - &self - .global_arguments - .config_file + fn config_filepath(&self) -> &Option { + self.global_args + .config_filepath() } fn is_json(&self) -> bool { - self.global_arguments - .json_flag + self.global_args + .is_json() } fn is_plain(&self) -> bool { - self.global_arguments - .plain_flag + self.global_args + .is_plain() } fn is_debug(&self) -> bool { - self.global_arguments - .debug_flag + self.global_args + .is_debug() } - fn is_no_color(&self) -> bool { - self.global_arguments - .no_color_flag + fn is_test(&self) -> bool { + self.global_args + .is_test() } - fn is_test(&self) -> bool { - self.global_arguments - .test_flag + fn is_no_color(&self) -> bool { + self.global_args + .is_no_color() } - fn verbosity(&self) -> &clap_verbosity_flag::Verbosity { - &self - .global_arguments - .verbose + fn verbosity_filter(&self) -> &log::LevelFilter { + self.global_args + .verbosity_filter() } } diff --git a/paxy-gui/src/lib.rs b/paxy-gui/src/lib.rs index dd71737..b110471 100644 --- a/paxy-gui/src/lib.rs +++ b/paxy-gui/src/lib.rs @@ -50,43 +50,39 @@ mod gui_cli_template { /// Implement a trait that can extract standard global arguments from our /// own CLI template impl ui::GlobalArguments for CliTemplate { - type L = clap_verbosity_flag::InfoLevel; - - fn config_file(&self) -> &Option { - &self - .global_args - .config_file + fn config_filepath(&self) -> &Option { + self.global_args + .config_filepath() } fn is_json(&self) -> bool { self.global_args - .json_flag + .is_json() } fn is_plain(&self) -> bool { self.global_args - .plain_flag + .is_plain() } fn is_debug(&self) -> bool { self.global_args - .debug_flag + .is_debug() } - fn is_no_color(&self) -> bool { + fn is_test(&self) -> bool { self.global_args - .no_color_flag + .is_test() } - fn is_test(&self) -> bool { + fn is_no_color(&self) -> bool { self.global_args - .test_flag + .is_no_color() } - fn verbosity(&self) -> &clap_verbosity_flag::Verbosity { - &self - .global_args - .verbose + fn verbosity_filter(&self) -> &log::LevelFilter { + self.global_args + .verbosity_filter() } } diff --git a/paxy/src/app/config.rs b/paxy/src/app/config.rs index 1623935..3820579 100644 --- a/paxy/src/app/config.rs +++ b/paxy/src/app/config.rs @@ -89,43 +89,102 @@ fn admerge_from_stub(candidate_config_filepath_stub: &PathBuf, mut figment: Figm figment } -#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Config { - pub config_dirpaths: Vec, - - pub log_dirpath: PathBuf, - - pub cli_output_format: ui::CliOutputFormat, + pub figment: Figment, } impl Config { pub fn new() -> Self { - Self::default() + Self { + figment: Figment::from(ConfigTemplate::default()), + } } -} -impl Default for Config { - fn default() -> Self { - Self { - config_dirpaths: None, - log_dirpath: None, - log_directory: todo!(), - cli_output_format: todo!(), + pub fn with_overriding_file>(&mut self, filepath: P) -> &mut Self { + let filepath: &Path = filepath.as_ref(); + if let Some(file_extension) = filepath.extension() { + file_extension = file_extension + .to_string_lossy() + .to_lowercase(); + match (file_extension) { + "toml" => { + self.figment = self + .figment + .admerge(Toml::file(filepath)); + } + "json" => { + self.figment = self + .figment + .admerge(Json::file(filepath)); + } + "yaml" | "yml" => { + self.figment = self + .figment + .admerge(Yaml::file(filepath)); + } + } } + + self } -} -impl Provider for Config { - fn metadata(&self) -> Metadata { - Metadata::named("Library Config") + pub fn with_overriding_files(&mut self, filepaths: I) -> &mut Self + where + P: AsRef, + I: Iterator, + { + filepaths.for_each(|filepath| self.with_overriding_file(filepath)); + + self + } + + pub fn with_overriding_env>(prefix: S) -> &mut Self { + let prefix = prefix.as_ref(); + self.figment = self + .figment + .admerge(Env::prefixed(prefix)); + + self } - fn data(&self) -> Result, figment::Error> { - figment::providers::Serialized::defaults(Config::default()).data() + pub fn with_overriding_env_var>(env_var_name: S) -> &mut Self { + let env_var_name = env_var_name.as_ref(); + self.figment = self + .figment + .admerge(Env::raw().only(&[env_var_name])); + + self + } + + pub fn with_overriding_args(&mut self, arguments: A) -> &mut Self { + if let Some(path) = arguments.config_filepath() { + self.figment = self.figment.admerge(("config_filepaths", path)); + } + + self } - fn profile(&self) -> Option { - None + pub fn object(&self) -> Result { + self.figment + .extract() + .context(ExtractConfigSnafu {})? + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ConfigTemplate { + pub config_filepaths: Vec, + pub log_filepath_stub: PathBuf, + pub console_output_format: ConsoleOutputFormat, +} + +impl Default for ConfigTemplate { + fn default() -> Self { + Self { + config_filepaths: Vec::new(), + log_filepath_stub: PathBuf::default(), + console_output_format: ConsoleOutputFormat::default(), + } } } @@ -145,6 +204,7 @@ use log::LevelFilter; use serde::{Deserialize, Serialize}; use snafu::{OptionExt, ResultExt, Snafu}; +use super::ui::GlobalArguments; use crate::app; use crate::app::ui; diff --git a/paxy/src/app/ui.rs b/paxy/src/app/ui.rs index 199464c..00d1fad 100644 --- a/paxy/src/app/ui.rs +++ b/paxy/src/app/ui.rs @@ -13,9 +13,13 @@ where .context(crate::AppSnafu)?; // Begin logging - let (mut logging_handle, log_filepath) = logging::init_log(&config.) - .context(app::LoggingSnafu {}) - .context(crate::AppSnafu {})?; + let (mut logging_handle, log_filepath) = logging::init_log( + &config + .cli_output_format + .requested_verbosity, + ) + .context(app::LoggingSnafu {}) + .context(crate::AppSnafu {})?; // Adjust output formatting if requested adjust_output_formatting(&config.cli_output_format, &logging_handle); @@ -29,17 +33,19 @@ where Ok((cli_input, logging_handle.worker_guards)) } -fn resolve_max_output_verbosity(cli_output_format: &CliOutputFormat, cli_global_arguments: G) { - let verbosity_flag_filter = cli_output_format - .verbosity() - .log_level_filter(); +fn resolve_max_output_verbosity( + cli_output_format: &CliOutputFormat, + cli_global_arguments: G, +) -> log::LevelFilter { + let verbosity_flag_filter = cli_output_format.requested_verbosity; if matches!( cli_output_format.output_mode, CliOutputMode::Plain | CliOutputMode::Json - ){ + ) { return Some(LevelFilter::Info); - } else if verbosity_flag_filter < clap_verbosity_flag::LevelFilter::Debug && cli_global_arguments.is_debug() + } else if verbosity_flag_filter < clap_verbosity_flag::LevelFilter::Debug + && cli_global_arguments.is_debug() { return Some(LevelFilter::Debug); } else { @@ -188,56 +194,38 @@ fn emit_test_messages() { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CliOutputFormat { - pub output_mode: CliOutputMode, +pub struct ConsoleOutputFormat { + pub output_mode: ConsoleOutputMode, - pub requested_verbosity: log::LevelFilter, + pub max_verbosity: log::LevelFilter, pub no_color: bool, } -impl CliOutputFormat { - pub fn resolve_max_verbosity_level() -> LevelFilter { - match self.output_mode { - Some(CliOutputMode::Plain) | Some(CliOutputMode::Json) => { - return Some(LevelFilter::Info) - } - _ => return verbosity_level_filter, - } - } -} - -impl Default for CliOutputFormat { +impl Default for ConsoleOutputFormat { fn default() -> Self { Self { - output_mode: Default::default(), - requested_verbosity: Some(log::LevelFilter::Info), - is_colored: Some(true), + output_mode: ConsoleOutputMode::default(), + max_verbosity: log::LevelFilter::Info, + no_color: false, } } } #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum CliOutputMode { +pub enum ConsoleOutputMode { Regular, Plain, Json, Test, } -impl Default for CliOutputMode { +impl Default for ConsoleOutputMode { fn default() -> Self { CliOutputMode::Regular } } -impl CliModifier for T -where - T: GlobalArguments, - ::L: LogLevel, -{ -} - pub trait CliModifier: GlobalArguments where ::L: LogLevel, @@ -277,9 +265,7 @@ where } pub trait GlobalArguments { - type L; - - fn config_file(&self) -> &Option; + fn config_filepath(&self) -> &Option; fn is_json(&self) -> bool; @@ -291,9 +277,38 @@ pub trait GlobalArguments { fn is_test(&self) -> bool; - fn verbosity(&self) -> &clap_verbosity_flag::Verbosity - where - Self::L: LogLevel; + fn verbosity_filter(&self) -> &log::LevelFilter; + + fn console_output_mode(&self) -> ConsoleOutputMode { + if self.is_json() { + ConsoleOutputMode::Json + } else if self.is_plain() { + ConsoleOutputMode::Plain + } else if self.is_test() { + ConsoleOutputMode::Test + } else { + ConsoleOutputMode::Regular + } + } + + fn max_output_verbosity(&self) -> log::LevelFilter { + let verbosity_flag_filter = cli_output_format.requested_verbosity; + if matches!( + cli_output_format.output_mode, + CliOutputMode::Plain | CliOutputMode::Json + ) { + return Some(LevelFilter::Info); + } else if verbosity_flag_filter < clap_verbosity_flag::LevelFilter::Debug + && cli_global_arguments.is_debug() + { + return Some(LevelFilter::Debug); + } else { + return verbosity_flag_filter + .as_str() + .parse() + .ok(); + } + } } #[derive(Debug, Snafu)] @@ -309,7 +324,6 @@ pub enum Error { use core::fmt; use std::{env, path::PathBuf}; -use clap_verbosity_flag::LogLevel; use log::LevelFilter; use owo_colors::OwoColorize; use serde::{Deserialize, Serialize}; @@ -382,7 +396,45 @@ pub mod cli_template { pub test_flag: bool, #[command(flatten)] - pub verbose: clap_verbosity_flag::Verbosity, + pub verbosity: clap_verbosity_flag::Verbosity, + } + + impl GlobalArguments for GlobalArgs { + fn config_filepath(&self) -> &Option { + self.config_filepath + } + + fn is_json(&self) -> bool { + self.json_flag + } + + fn is_plain(&self) -> bool { + self.plain_flag + } + + fn is_debug(&self) -> bool { + self.debug_flag + } + + fn is_test(&self) -> bool { + self.test_flag + } + + fn is_no_color(&self) -> bool { + self.no_color_flag + } + + fn verbosity_filter(&self) -> &log::LevelFilter { + self.verbosity + .log_level_filter() + .and_then(|log_level_filter| { + log_level_filter + .as_str() + .parse() + .ok() + }) + .unwrap_or(log::LevelFilter::Info) + } } // region: IMPORTS @@ -391,6 +443,8 @@ pub mod cli_template { use clap::Args; + use super::GlobalArguments; + // endregion: IMPORTS }