#[cfg(feature = "serde_json")]
use crate::{CliValueExtractor, OrthoMergeExt, OrthoResult, load_config_file, sanitized_provider};
#[cfg(feature = "serde_json")]
use clap::{ArgMatches, CommandFactory};
#[cfg(feature = "serde_json")]
use figment::providers::Serialized;
#[cfg(feature = "serde_json")]
use figment::{Figment, providers::Env};
#[cfg(feature = "serde_json")]
use serde::de::DeserializeOwned;
#[cfg(feature = "serde_json")]
use std::path::PathBuf;
#[cfg(feature = "serde_json")]
use uncased::Uncased;
mod paths;
#[cfg(feature = "serde_json")]
mod selected;
mod types;
#[cfg(feature = "serde_json")]
use paths::candidate_paths;
pub use paths::push_stem_candidates;
#[cfg(feature = "serde_json")]
pub use selected::{
LoadGlobalsAndSelectedSubcommandError, SelectedSubcommandMerge, SelectedSubcommandMergeError,
load_globals_and_merge_selected_subcommand,
};
pub use types::{CmdName, Prefix};
#[cfg(feature = "serde_json")]
fn load_from_files(paths: &[PathBuf], name: &CmdName) -> OrthoResult<Figment> {
let mut fig = Figment::new();
for p in paths {
if let Some(file_fig) = load_config_file(p)? {
fig = fig.merge(file_fig.focus(&format!("cmds.{}", name.as_str())));
}
}
Ok(fig)
}
#[cfg(feature = "serde_json")]
fn load_file_and_env_defaults<T>(prefix: &Prefix) -> OrthoResult<Figment>
where
T: CommandFactory,
{
let name = CmdName::new(T::command().get_name());
let paths = candidate_paths(prefix);
let mut fig = load_from_files(&paths, &name)?;
let env_name = name.env_key();
let raw = prefix.raw();
let raw_without_suffix = raw.strip_suffix('_').unwrap_or(raw);
let env_prefix = format!("{raw_without_suffix}_CMDS_{env_name}_");
let env_provider = Env::prefixed(&env_prefix)
.map(|k| Uncased::from(k))
.split("__");
fig = fig.merge(env_provider);
Ok(fig)
}
#[cfg(feature = "serde_json")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde_json")))]
pub fn load_and_merge_subcommand<T>(prefix: &Prefix, cli: &T) -> OrthoResult<T>
where
T: serde::Serialize + DeserializeOwned + Default + CommandFactory,
{
let fig = load_file_and_env_defaults::<T>(prefix)?;
fig.merge(sanitized_provider(cli)?)
.extract()
.into_ortho_merge()
}
#[cfg(feature = "serde_json")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde_json")))]
pub fn load_and_merge_subcommand_for<T>(cli: &T) -> OrthoResult<T>
where
T: crate::OrthoConfig + serde::Serialize + Default + CommandFactory,
{
load_and_merge_subcommand(&Prefix::new(T::prefix()), cli)
}
#[cfg(feature = "serde_json")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde_json")))]
pub fn load_and_merge_subcommand_with_matches<T>(
prefix: &Prefix,
cli: &T,
matches: &ArgMatches,
) -> OrthoResult<T>
where
T: serde::Serialize + DeserializeOwned + Default + CommandFactory + CliValueExtractor,
{
let fig = Figment::from(Serialized::defaults(T::default()))
.merge(load_file_and_env_defaults::<T>(prefix)?);
let cli_value = cli.extract_user_provided(matches)?;
fig.merge(Serialized::defaults(cli_value))
.extract()
.into_ortho_merge()
}
#[cfg(feature = "serde_json")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde_json")))]
pub fn load_and_merge_subcommand_for_with_matches<T>(
cli: &T,
matches: &ArgMatches,
) -> OrthoResult<T>
where
T: crate::OrthoConfig + serde::Serialize + Default + CommandFactory + CliValueExtractor,
{
load_and_merge_subcommand_with_matches(&Prefix::new(T::prefix()), cli, matches)
}
#[cfg(feature = "serde_json")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde_json")))]
pub trait SubcmdConfigMerge: crate::OrthoConfig + CommandFactory + Sized {
fn load_and_merge(&self) -> OrthoResult<Self>
where
Self: serde::Serialize + Default,
{
load_and_merge_subcommand_for(self)
}
fn load_and_merge_with_matches(&self, matches: &ArgMatches) -> OrthoResult<Self>
where
Self: serde::Serialize + Default + CliValueExtractor,
{
load_and_merge_subcommand_for_with_matches(self, matches)
}
}
#[cfg(feature = "serde_json")]
impl<T> SubcmdConfigMerge for T where
T: crate::OrthoConfig + serde::Serialize + Default + CommandFactory
{
}