|
1 | | -use std::collections::BTreeMap; |
2 | 1 | use std::path::PathBuf; |
3 | 2 |
|
4 | 3 | use clap::{value_parser, Arg, ArgMatches, Command}; |
5 | | -use serde_json::{json, Value}; |
6 | 4 |
|
7 | | -use crate::loading::update_config_map; |
8 | | -use crate::{ |
9 | | - ConfigError, |
10 | | - ConfigFormat, |
11 | | - ParamPath, |
12 | | - SerializationType, |
13 | | - SerializedParam, |
14 | | - CONFIG_FILE_ARG_NAME, |
15 | | - CONFIG_FILE_SHORT_ARG_NAME, |
16 | | - CONFIG_FORMAT_ARG_NAME, |
17 | | -}; |
| 5 | +use crate::{ConfigError, CONFIG_FILE_ARG_NAME, CONFIG_FILE_SHORT_ARG_NAME}; |
18 | 6 |
|
19 | 7 | pub(crate) fn get_command_matches( |
20 | | - config_map: &BTreeMap<ParamPath, SerializedParam>, |
21 | 8 | command: Command, |
22 | 9 | command_input: Vec<String>, |
23 | 10 | ) -> Result<ArgMatches, ConfigError> { |
24 | | - Ok(command.args(build_args_parser(config_map)).try_get_matches_from(command_input)?) |
25 | | -} |
26 | | - |
27 | | -// Takes matched arguments from the command line interface and env variables and updates the config |
28 | | -// map. |
29 | | -// Supports f64, u64, i64, bool and String. |
30 | | -pub(crate) fn update_config_map_by_command_args( |
31 | | - config_map: &mut BTreeMap<ParamPath, Value>, |
32 | | - types_map: &BTreeMap<ParamPath, SerializationType>, |
33 | | - arg_match: &ArgMatches, |
34 | | -) -> Result<(), ConfigError> { |
35 | | - for param_path_id in arg_match.ids() { |
36 | | - let param_path = param_path_id.as_str(); |
37 | | - let new_value = get_arg_by_type(types_map, arg_match, param_path)?; |
38 | | - update_config_map(config_map, types_map, param_path, new_value)?; |
39 | | - } |
40 | | - Ok(()) |
41 | | -} |
42 | | - |
43 | | -// Builds the parser for the command line flags and env variables according to the types of the |
44 | | -// values in the config map. |
45 | | -fn build_args_parser(config_map: &BTreeMap<ParamPath, SerializedParam>) -> Vec<Arg> { |
46 | | - let mut args_parser = vec![ |
47 | | - // Custom_config_file_path. |
48 | | - Arg::new(CONFIG_FILE_ARG_NAME) |
49 | | - .long(CONFIG_FILE_ARG_NAME) |
50 | | - .short(CONFIG_FILE_SHORT_ARG_NAME) |
51 | | - .value_delimiter(',') |
52 | | - .help("Optionally sets a config file to use") |
53 | | - .value_parser(value_parser!(PathBuf)) |
54 | | - .num_args(1..) // Allow multiple values |
55 | | - .action(clap::ArgAction::Append), // Collect multiple occurrences |
56 | | - // How the --config_file arguments are interpreted. Absent => ConfigFormat::Preset. |
57 | | - Arg::new(CONFIG_FORMAT_ARG_NAME) |
58 | | - .long(CONFIG_FORMAT_ARG_NAME) |
59 | | - .num_args(1) |
60 | | - .value_parser(value_parser!(ConfigFormat)) |
61 | | - .help( |
62 | | - "How the --config_file arguments are interpreted: 'preset' (flat dotted-key, \ |
63 | | - layered) or 'native' (the first file is nested serde, later files are flat \ |
64 | | - secret overrides)", |
65 | | - ), |
66 | | - ]; |
67 | | - |
68 | | - for (param_path, serialized_param) in config_map.iter() { |
69 | | - let Some(serialization_type) = serialized_param.content.get_serialization_type() else { |
70 | | - continue; // Pointer target |
71 | | - }; |
72 | | - let clap_parser = match serialization_type { |
73 | | - SerializationType::Boolean => clap::value_parser!(bool), |
74 | | - SerializationType::Float => clap::value_parser!(f64).into(), |
75 | | - SerializationType::NegativeInteger => clap::value_parser!(i64).into(), |
76 | | - SerializationType::PositiveInteger => clap::value_parser!(u64).into(), |
77 | | - SerializationType::String => clap::value_parser!(String), |
78 | | - }; |
79 | | - |
80 | | - let arg = Arg::new(param_path) |
81 | | - .long(param_path) |
82 | | - .env(to_env_var_name(param_path)) |
83 | | - .help(&serialized_param.description) |
84 | | - .value_parser(clap_parser) |
85 | | - .allow_negative_numbers(true); |
86 | | - |
87 | | - args_parser.push(arg); |
88 | | - } |
89 | | - args_parser |
90 | | -} |
91 | | - |
92 | | -// Converts clap arg_matches into json values. |
93 | | -fn get_arg_by_type( |
94 | | - types_map: &BTreeMap<ParamPath, SerializationType>, |
95 | | - arg_match: &ArgMatches, |
96 | | - param_path: &str, |
97 | | -) -> Result<Value, ConfigError> { |
98 | | - let serialization_type = types_map.get(param_path).expect("missing type"); |
99 | | - match serialization_type { |
100 | | - SerializationType::Boolean => Ok(json!(arg_match.try_get_one::<bool>(param_path)?)), |
101 | | - SerializationType::Float => Ok(json!(arg_match.try_get_one::<f64>(param_path)?)), |
102 | | - SerializationType::NegativeInteger => Ok(json!(arg_match.try_get_one::<i64>(param_path)?)), |
103 | | - SerializationType::PositiveInteger => Ok(json!(arg_match.try_get_one::<u64>(param_path)?)), |
104 | | - SerializationType::String => Ok(json!(arg_match.try_get_one::<String>(param_path)?)), |
105 | | - } |
106 | | -} |
107 | | - |
108 | | -fn to_env_var_name(param_path: &str) -> String { |
109 | | - param_path.replace("#is_none", "__is_none__").to_uppercase().replace('.', "__") |
| 11 | + // The config file flag is the only argument; the config files themselves carry every value. |
| 12 | + let config_file_arg = Arg::new(CONFIG_FILE_ARG_NAME) |
| 13 | + .long(CONFIG_FILE_ARG_NAME) |
| 14 | + .short(CONFIG_FILE_SHORT_ARG_NAME) |
| 15 | + .value_delimiter(',') |
| 16 | + .help("Sets the config files to use") |
| 17 | + .value_parser(value_parser!(PathBuf)) |
| 18 | + .num_args(1..) // Allow multiple values |
| 19 | + .action(clap::ArgAction::Append); // Collect multiple occurrences |
| 20 | + Ok(command.args([config_file_arg]).try_get_matches_from(command_input)?) |
110 | 21 | } |
0 commit comments