Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
pvshvp-oss committed May 10, 2024
1 parent 298cba2 commit d19f754
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 127 deletions.
134 changes: 84 additions & 50 deletions paxy/src/app/config.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
lazy_static! {
pub static ref CONFIG_FILE_EXTENSIONS: [&'static str] = ["toml", "json", "yaml", "yml"];
}

/// Initializes a layered configuration deriving values from the app-wide
/// defaults, overridden by values from global paths, overridden by values from
/// local paths. overridden by environment variables starting with `PAXY_`,
/// overridden by the configuration file specified by the commandline.
/// Values from only files with supported file extensions would be merged.
pub fn init_config<G>(cli_global_arguments: G) -> Result<(Config, Vec<PathBuf>), Error>
pub fn init_config<G>(cli_global_arguments: G) -> Result<(ConfigTemplate, Vec<PathBuf>), Error>
where
G: ui::GlobalArguments,
<G as ui::GlobalArguments>::L: clap_verbosity_flag::LogLevel,
{
let mut candidate_config_filepaths: Vec<PathBuf> = Vec::new();

Expand All @@ -29,47 +32,32 @@ where
.iter_mut()
.for_each(|f| f.push(*app::APP_NAME));

let lowercase_config_file_extensions = *config::CONFIG_FILE_EXTENSIONS.iter();
let uppercase_config_file_extensions = lowercase_config_file_extensions.map(str::to_uppercase);

candidate_config_filepaths = candidate_config_filepaths
.iter()
.cartesian_product(["toml", "TOML", "json", "JSON", "yaml", "YAML", "yml", "YML"]);
.cartesian_product(
lowercase_config_file_extensions.chain(uppercase_config_file_extensions),
);

// Initialize configuration with app-wide defaults
let mut config = Config::new();

// Merge configuration values from global and local filepaths
config = config.with_overriding_files(candidate_config_filepaths);
config.with_overriding_files(candidate_config_filepaths);

// Merge configuration values from environment variables
config = config.with_overriding_env(&format!("{}_", *app::APP_NAME));

// Merge configuration values from additional config filepaths (usually
// specified through CLI)
if let Some(additional_config_filepath) = preferred_config_filepath {
if let Some(parent) = additional_config_filepath.parent() {
if let Some(stem) = additional_config_filepath.file_stem() {
let mut stub = PathBuf::from(parent);
stub.push(stem);
figment = admerge_from_stub(&stub, figment);
candidate_config_filepath_stubs.push(stub);
}
}
}
config.with_overriding_env(&format!("{}_", *app::APP_NAME));

// Merge configuration values from the CLI
// These are not set to be optional, so only action-required states are
// merged with the configuration
if cli_global_arguments.is_uncolored() {
figment = figment.admerge(("no_color", true));
}
if let Some(log_level_filter) = cli_global_arguments.verbosity_filter() {
figment = figment.admerge(("log_level_filter", log_level_filter));
}
config.with_overriding_args(cli_global_arguments);

Ok((
figment
.extract()
config
.object()
.context(ExtractConfigSnafu {})?,
candidate_config_filepath_stubs,
candidate_config_filepaths,
))
}

Expand All @@ -93,15 +81,15 @@ fn admerge_from_stub(candidate_config_filepath_stub: &PathBuf, mut figment: Figm
pub struct ConfigTemplate {
pub config_filepaths: Vec<PathBuf>,
pub log_filepath_stub: PathBuf,
pub console_output_format: ConsoleOutputFormat,
pub console_output_format: ui::ConsoleOutputFormat,
}

impl Default for ConfigTemplate {
fn default() -> Self {
Self {
config_filepaths: Vec::new(),
log_filepath_stub: PathBuf::default(),
console_output_format: ConsoleOutputFormat::default(),
console_output_format: ui::ConsoleOutputFormat::default(),
}
}
}
Expand Down Expand Up @@ -145,13 +133,6 @@ impl Config {
self
}

pub fn with_overriding_filepath_stub<P: Into<PathBuf>>(&mut self, filepath_stub: P) -> &mut Self {
let filepath_stub: PathBuf = filepath_stub.into();


self
}

pub fn with_overriding_files<P, I>(&mut self, filepaths: I) -> &mut Self
where
P: AsRef<Path>,
Expand All @@ -162,6 +143,49 @@ impl Config {
self
}

pub fn with_overriding_filepath_stubs<I1, I2, P>(
&mut self,
file_extensions: I1,
filepath_stubs: I2,
) -> &mut Self
where
I1: IntoIterator<Item = S>,
S: AsRef<str>,
I2: IntoIterator<Item = P>,
P: Into<PathBuf>,
{
let filepath_stubs: Iterator<Item = PathBuf> = filepath_stubs
.into_iter()
.map(Into::into);
file_extensions
.into_iter()
.map(AsRef::as_ref)
.cartesian_product(filepath_stubs)
.map(|(file_extension, filepath_stub)| {
let filepath = filepath_stub;
filepath.set_extension(file_extension);

self.with_overriding_file(filepath);
});

self
}

pub fn with_overriding_filepath_stub<I, P>(
&mut self,
file_extensions: I,
filepath_stub: P,
) -> &mut Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
P: Into<PathBuf>,
{
self.with_overriding_filepath_stubs(file_extensions, iter::once(filepath_stub));

self
}

pub fn with_overriding_env<S: AsRef<str>>(&mut self, prefix: S) -> &mut Self {
let prefix = prefix.as_ref();
self.figment = self
Expand All @@ -171,14 +195,17 @@ impl Config {
self
}

pub fn with_overriding_args<A: ui::GlobalArguments>(&mut self, cli_arguments: A) -> &mut Self {
if let Some(path) = cli_arguments.config_filepath() {
/// Merge configuration values from the CLI
/// These are not set to be optional, so only action-required states are
/// merged with the configuration
pub fn with_overriding_args<A: ui::GlobalArguments>(&mut self, console_arguments: A) -> &mut Self {
if let Some(path) = console_arguments.config_filepath() {
self.figment = self
.figment
.admerge(("config_filepaths", path));
}

let console_output_mode = cli_arguments.console_output_mode();
let console_output_mode = console_arguments.console_output_mode();
if console_output_mode != ConsoleOutputMode::Regular {
self.figment = self
.figment
Expand All @@ -188,9 +215,9 @@ impl Config {
let current_max_verbosity = self
.figment
.extract_inner::<log::LevelFilter>("console_output_format.max_verbosity");
let requested_max_verbosity = cli_arguments.max_output_verbosity();
let requested_max_verbosity = console_arguments.max_output_verbosity();
if let Ok(current_max_verbosity) = current_max_verbosity {
if cli_requested_max_verbosity > current_max_verbosity {
if requested_max_verbosity > current_max_verbosity {
self.figment = self
.figment
.admerge((
Expand All @@ -200,36 +227,42 @@ impl Config {
}
}

let requested_no_color =
console_arguments.is_no_color() || console_arguments.is_plain() || console_arguments.is_json();
let current_no_color = self
.figment
.extract_inner::<log::LevelFilter>("console_output_format.no_color");
let requested_no_color =
cli_arguments.is_no_color() || cli_arguments.is_plain() || cli_arguments.is_json();
.extract_inner::<bool>("console_output_format.no_color");
let env_no_color = env::var("NO_COLOR").is_ok()
|| env::var(format!(
"{}_NO_COLOR",
String::from(*app::APP_NAME).to_uppercase()
))
.is_ok()
|| env::var("TERM").is_ok_and(|env_term_value| env_term_value.to_lowercase == "dumb");
if !current_no_color && (requested_no_color || env_no_color) {
if (requested_no_color || env_no_color) && !current_no_color.unwrap_or(false) {
self.figment = self
.figment
.admerge(("console_output_format.no_color", true));
}

self
}

pub fn object(&self) -> Result<ConfigTemplate, Error> {
self.figment
let config_object = self.figment
.extract()
.context(ExtractConfigSnafu {})?
.context(ExtractConfigSnafu {})?;

let current_max_verbosity = config_object


config_object
}
}

// region: IMPORTS

use std::iter;
use std::path::PathBuf;

use figment::{
Expand All @@ -241,6 +274,7 @@ use figment::{
Provider,
};
use itertools::Itertools;
use lazy_static::lazy_static;
use log::LevelFilter;
use serde::{Deserialize, Serialize};
use snafu::{OptionExt, ResultExt, Snafu};
Expand Down
64 changes: 63 additions & 1 deletion paxy/src/app/logging.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pub fn init_log(max_output_verbosity: log::LevelFilter) -> Result<(Handle, PathBuf), Error> {
pub fn init_log(
console_output_format: &ui::ConsoleOutputFormat,
) -> Result<(Handle, PathBuf), Error> {
let log_filename = format!("{}.log", *app::APP_NAME);
let log_dirpath = obtain_log_dirpath(preferred_log_dirpath)?;
let log_file_appender =
Expand Down Expand Up @@ -152,6 +154,65 @@ pub fn init_log(max_output_verbosity: log::LevelFilter) -> Result<(Handle, PathB
))
}

fn resolve_max_output_verbosity(
console_output_format: &ui::ConsoleOutputFormat,
) -> log::LevelFilter {
let verbosity_flag_filter = console_output_format.max_verbosity;

if matches!(
console_output_format.mode,
&ui::ConsoleOutputMode::Plain | &ui::ConsoleOutputMode::Json
) {
return Some(log::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();
}
}

fn adjust_output_formatting(
cli_output_format: &CliOutputFormat,
mut logging_handle: &logging::Handle,
) {
// Turn off colors if requested
if matches!(
cli_output_format.mode,
CliOutputMode::Plain | CliOutputMode::Json
) || cli_output_format.no_color
|| is_env_variable_set("NO_COLOR")
|| is_env_variable_set(format!(
"{}_NO_COLOR",
String::from(*app::APP_NAME).to_uppercase()
))
{
anstream::ColorChoice::Never.write_global();
owo_colors::set_override(false);
}

// Change output mode if requested
match cli_output_format.mode {
CliOutputMode::Plain => logging_handle
.switch_to_plain()
.context(app::LoggingSnafu {})
.context(crate::AppSnafu {})?,
CliOutputMode::Json => logging_handle
.switch_to_json()
.context(app::LoggingSnafu {})
.context(crate::AppSnafu {})?,
CliOutputMode::Test => logging_handle
.switch_to_test()
.context(app::LoggingSnafu {})
.context(crate::AppSnafu {})?,
_ => {}
}
}

fn obtain_log_dirpath(preferred_log_dirpath: Option<PathBuf>) -> Result<PathBuf, Error> {
let obtain_fallback_log_dirpath = || {
let xdg_app_dirs =
Expand Down Expand Up @@ -322,6 +383,7 @@ use tracing_subscriber::{
Layer,
};

use super::ui;
use crate::app;

// endregion: IMPORTS
Loading

0 comments on commit d19f754

Please sign in to comment.