Clap Subcommand Config File Loading
Define a clap CLI with typed subcommands, layered defaults, optional config file overrides, and environment variable fallbacks.
Code
use clap::{Arg, ArgAction, Command};
use std::collections::HashMap;
#[derive(Debug)]
struct SyncOptions {
workers: u32,
region: String,
dry_run: bool,
}
fn build_cli() -> Command {
Command::new("deployctl")
.about("deployment control tool")
.subcommand(
Command::new("sync")
.about("sync catalog to a region")
.arg(
Arg::new("workers")
.short('w')
.long("workers")
.env("DEPLOY_WORKERS")
.default_value("4")
.value_parser(clap::value_parser!(u32).range(1..=64)),
)
.arg(
Arg::new("region")
.short('r')
.long("region")
.env("DEPLOY_REGION")
.default_value("eu"),
)
.arg(
Arg::new("dry-run")
.long("dry-run")
.action(ArgAction::SetTrue),
),
)
.subcommand(
Command::new("status").about("show sync status"),
)
}
fn load_file_overrides(path: &str) -> HashMap<String, String> {
let mut map = HashMap::new();
if let Ok(content) = std::fs::read_to_string(path) {
for line in content.lines() {
if let Some((key, value)) = line.split_once('=') {
map.insert(key.trim().to_string(), value.trim().to_string());
}
}
}
map
}
fn resolve(matches: &clap::ArgMatches, file: &HashMap<String, String>) -> SyncOptions {
let flag_workers: u32 = *matches.get_one("workers").unwrap_or(&4);
// 显式传参 > 配置文件 > clap 默认值(含环境变量)
let workers = if matches.value_source("workers")
== Some(clap::builder::ValueSource::CommandLine)
{
flag_workers
} else if let Some(from_file) = file.get("workers").and_then(|v| v.parse().ok()) {
from_file
} else {
flag_workers
};
let region = matches
.get_one::<String>("region")
.cloned()
.unwrap_or_else(|| "eu".into());
SyncOptions {
workers,
region,
dry_run: matches.get_flag("dry-run"),
}
}
fn main() {
let cli = build_cli();
let matches = cli.get_matches();
match matches.subcommand() {
Some(("sync", sub)) => {
let file = load_file_overrides("deployctl.conf");
let options = resolve(sub, &file);
println!("{options:?}");
}
Some(("status", _)) => println!("status: idle"),
_ => unreachable!("clap 保证子命令必选"),
}
}Report a problem with this exampleOpens a pre-filled issue on GitHub.