use std::collections::BTreeMap;
use std::path::PathBuf;
use clap::{value_parser, Arg, ArgMatches, Command};
use serde_json::{json, Value};
use crate::loading::update_config_map;
use crate::{
ConfigError,
ParamPath,
SerializationType,
SerializedParam,
CONFIG_FILE_ARG_NAME,
CONFIG_FILE_SHORT_ARG_NAME,
};
pub(crate) fn get_command_matches(
config_map: &BTreeMap<ParamPath, SerializedParam>,
command: Command,
command_input: Vec<String>,
) -> Result<ArgMatches, ConfigError> {
Ok(command.args(build_args_parser(config_map)).try_get_matches_from(command_input)?)
}
pub(crate) fn update_config_map_by_command_args(
config_map: &mut BTreeMap<ParamPath, Value>,
types_map: &BTreeMap<ParamPath, SerializationType>,
arg_match: &ArgMatches,
) -> Result<(), ConfigError> {
for param_path_id in arg_match.ids() {
let param_path = param_path_id.as_str();
let new_value = get_arg_by_type(types_map, arg_match, param_path)?;
update_config_map(config_map, types_map, param_path, new_value)?;
}
Ok(())
}
fn build_args_parser(config_map: &BTreeMap<ParamPath, SerializedParam>) -> Vec<Arg> {
let mut args_parser = vec![
Arg::new(CONFIG_FILE_ARG_NAME)
.long(CONFIG_FILE_ARG_NAME)
.short(CONFIG_FILE_SHORT_ARG_NAME)
.value_delimiter(',')
.help("Optionally sets a config file to use")
.value_parser(value_parser!(PathBuf))
.num_args(1..) .action(clap::ArgAction::Append), ];
for (param_path, serialized_param) in config_map.iter() {
let Some(serialization_type) = serialized_param.content.get_serialization_type() else {
continue; };
let clap_parser = match serialization_type {
SerializationType::Boolean => clap::value_parser!(bool),
SerializationType::Float => clap::value_parser!(f64).into(),
SerializationType::NegativeInteger => clap::value_parser!(i64).into(),
SerializationType::PositiveInteger => clap::value_parser!(u64).into(),
SerializationType::String => clap::value_parser!(String),
};
let arg = Arg::new(param_path)
.long(param_path)
.env(to_env_var_name(param_path))
.help(&serialized_param.description)
.value_parser(clap_parser)
.allow_negative_numbers(true);
args_parser.push(arg);
}
args_parser
}
fn get_arg_by_type(
types_map: &BTreeMap<ParamPath, SerializationType>,
arg_match: &ArgMatches,
param_path: &str,
) -> Result<Value, ConfigError> {
let serialization_type = types_map.get(param_path).expect("missing type");
match serialization_type {
SerializationType::Boolean => Ok(json!(arg_match.try_get_one::<bool>(param_path)?)),
SerializationType::Float => Ok(json!(arg_match.try_get_one::<f64>(param_path)?)),
SerializationType::NegativeInteger => Ok(json!(arg_match.try_get_one::<i64>(param_path)?)),
SerializationType::PositiveInteger => Ok(json!(arg_match.try_get_one::<u64>(param_path)?)),
SerializationType::String => Ok(json!(arg_match.try_get_one::<String>(param_path)?)),
}
}
fn to_env_var_name(param_path: &str) -> String {
param_path.replace("#is_none", "__is_none__").to_uppercase().replace('.', "__")
}