Replies: 15 comments 39 replies
-
#2759's approach
|
Beta Was this translation helpful? Give feedback.
-
At one point, I would use a
|
Beta Was this translation helpful? Give feedback.
-
Recently, I've been using a
|
Beta Was this translation helpful? Give feedback.
-
There is also the popular config crate which is designed around layered config |
Beta Was this translation helpful? Give feedback.
-
Maintainer's notes:
Note: There's a lot of previous discussion on #748. Let's continue this discussion there? |
Beta Was this translation helpful? Give feedback.
-
For completeness, I'm going to mention argfile support. The formats currently supported by the argfile crate do not support comments and you can only specify it on the command line, you can't load it yourself to extend argv. This does have the benefit of allowing the user to control where the args are inserted. |
Beta Was this translation helpful? Give feedback.
-
Instead of a solution, this thread will be exploring requirements and their prioritization Various requirements include:
What additional requirements are there? Is there a viable subset that covers enough people for a shared solution? What building blocks can be made to help with all of this? |
Beta Was this translation helpful? Give feedback.
-
Problems with early error reporting:
|
Beta Was this translation helpful? Give feedback.
-
Apparently there are a few more options, but neither support Derive:
|
Beta Was this translation helpful? Give feedback.
-
Looking for feedback on the upcoming clap API design, in particular from a layered config perspective but also if people have thoughts in general See #3792 |
Beta Was this translation helpful? Give feedback.
-
After a 4-day diversion into a config system that involved becoming very familiar with the code for the twelf crate (some might call it a yak-shave), I have arrived at a fairly clear vision for the design of a a config layering feature that I personally would like to use. First a few observations:
Optional arguments aren’t enough to accomplish this however, because the user still needs to see the error if no layer was ultimately able to supply the argument value.
Most serde format crates have one or two line samples that provide an interface like this.
There are pros and cons to each. On the one hand, having to re-specify so many field attributes for Clap and Serde is a pain, and there is no struct if the Command builder API was used instead. But on the other hand, allowing the user to derive Deserialize independently gives a lot more control / flexibility. It sounds like pain either way. A third solution may be to side-step this concern altogether by providing a way to Deserialize directly to ArgMatches. So, for an API, I think an elegant design might be:
Finally, it could all be wrapped up in a convenience API that presents a nice “Layer” API. I am offering to prototype this if it sounds like a good design / direction to everyone. Although It will probably take me a few weeks / months to get back to this as I already lost too much time to config management and I’m behind on other work. |
Beta Was this translation helpful? Give feedback.
-
I think that if update_from_arg_matches() used use clap::{CommandFactory, FromArgMatches, Parser};
use serde::Deserialize;
#[derive(Debug, Parser, Deserialize)]
struct Opts {
#[clap(long)]
a: Option<String>,
#[serde(default)]
#[clap(long, default_value = "def")]
b: String,
#[clap(long)]
c: bool,
}
const CONFIG: &str = "
a: one
b: two
c: false
";
fn main() {
let matches = <Opts as CommandFactory>::command().get_matches();
let mut opts: Opts = serde_yaml::from_str(CONFIG).unwrap();
opts.update_from_arg_matches(&matches).unwrap();
dbg!(&opts);
} Basically creating an ArgMatches from the command-line args, parsing the config file into the Opts struct, and then updating it with the values from the command line if set. You'd probably have to keep the serde default value and the clap default value in sync though. |
Beta Was this translation helpful? Give feedback.
-
There is also the clap_serde_derive crate, which seems to offer a nice solution by creating a struct for your config that has everything wrapped in an I haven't used it or looked at the source due to the AGPL license, but if it handles subcommands properly it seems like a neat solution. |
Beta Was this translation helpful? Give feedback.
-
I want to express such a use case: use std::path::PathBuf;
use clap::Parser;
use figment::{
providers::{Env, Format, Serialized, Json},
Figment,
};
use serde::{Deserialize, Serialize};
fn conf_default_name() -> String {
"world".to_string()
}
#[derive(Parser, Debug, Serialize, Deserialize)]
struct Config {
/// A file to override default config.
#[clap(short, long, value_parser)]
config: Option<PathBuf>,
/// Name of the person to greet.
#[clap(short, long, value_parser, default_value_t = conf_default_name())]
#[serde(default = "conf_default_name")]
name: String,
}
fn main() {
// Create config builder.
let mut config: Figment = Figment::new();
// Parse CLI arguments.
let cli_args = Config::parse();
// Override with config file.
if let Some(config_path) = cli_args.config.clone() {
config = config.merge(Json::file(config_path));
}
// Override with `APP_`-prefixed environment variables and CLI args.
let config: Config = config
.merge(Env::prefixed("APP_"))
.merge(Serialized::defaults(cli_args))
.extract()
.unwrap();
println!("{:?}", config);
} In this case, I expect the override priority to be: cargo run -- --config config.json Some people may say that setting the |
Beta Was this translation helpful? Give feedback.
-
@luketpeterson did you ever start a prototype for this? If so I'd love to take a look. |
Beta Was this translation helpful? Give feedback.
-
This is inspired by #2759 in which there is a proposal for one approach for layering. Thought the actual layering best practices would best be split out in a discussion
Beta Was this translation helpful? Give feedback.
All reactions