by @geoffjay
Configuration management patterns including file formats, precedence, environment variables, and XDG directories. Use when implementing configuration systems for CLI applications.
Patterns and best practices for managing configuration in command-line applications.
The standard precedence order (lowest to highest priority):
use config::{Config as ConfigBuilder, Environment, File};
pub fn load_config(cli: &Cli) -> Result<Config> {
let mut builder = ConfigBuilder::builder()
// 1. Defaults
.set_default("port", 8080)?
.set_default("host", "localhost")?
.set_default("log_level", "info")?;
// 2. System config (if exists)
builder = builder
.add_source(File::with_name("/etc/myapp/config").required(false));
// 3. User config (if exists)
if let Some(config_dir) = dirs::config_dir() {
builder = builder.add_source(
File::from(config_dir.join("myapp/config.toml")).required(false)
);
}
// 4. Project config (if exists)
builder = builder
.add_source(File::with_name("myapp").required(false))
.add_source(File::with_name(".myapp").required(false));
// 5. CLI-specified config (if provided)
if let Some(config_path) = &cli.config {
builder = builder.add_source(File::from(config_path.as_ref()));
}
// 6. Environment variables
builder = builder.add_source(
Environment::with_prefix("MYAPP")
.separator("_")
.try_parsing(true)
);
// 7. CLI arguments (highest priority)
if let Some(port) = cli.port {
builder = builder.set_override("port", port)?;
}
Ok(builder.build()?.try_deserialize()?)
}
Clear, human-readable, good error messages.
# con...