- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 clap 编写 CLI 程序解析我的论点。我想为选项提供默认值,但如果有配置文件,配置文件应该胜过默认值。
设置命令行参数的优先级高于默认值很容易,但我希望优先级顺序为:
如果配置文件不是由命令行选项设置的,设置它也很容易,只需在运行 parse_args
之前解析配置文件,并提供解析后的配置值文件到 default_value
。问题是,如果您在命令行中指定配置文件,则只有在解析之后才能更改默认值。
我能想到的唯一方法是不设置 default_value
,然后手动匹配 value_of
中的 ""
。问题是在那种情况下,clap 将无法构建有用的 --help
。
有没有办法让 clap 读取配置文件本身?
最佳答案
对于将受益于派生宏的 clap v3 或 clap v4 的用户,我解决了这个问题,制作了两个结构:一个是目标结构,另一个是相同的但所有字段都是可选的。我用 serde 从配置文件和命令行用 clap 解析第二个结构,然后这些结构可以合并到第一个结构中:配置/命令行参数中不存在 None 的元素。
为此,我创建了一个派生宏 ( ClapSerde),它会自动执行以下操作:
// Priority:
// 1. command line arguments (clap)
// 2. config file (serde)
// 3. defaults
Args::from(serde_parsed)
.merge_clap();
例子:
use clap_serde_derive::{
clap::{self, ArgAction},
serde::Serialize,
ClapSerde,
};
#[derive(ClapSerde, Serialize)]
#[derive(Debug)]
#[command(author, version, about)]
pub struct Args {
/// Input files
pub input: Vec<std::path::PathBuf>,
/// String argument
#[arg(short, long)]
name: String,
/// Skip serde deserialize
#[default(13)]
#[serde(skip_deserializing)]
#[arg(long = "num")]
pub clap_num: u32,
/// Skip clap
#[serde(rename = "number")]
#[arg(skip)]
pub serde_num: u32,
/// Recursive fields
#[clap_serde]
#[command(flatten)]
pub suboptions: SubConfig,
}
#[derive(ClapSerde, Serialize)]
#[derive(Debug)]
pub struct SubConfig {
#[default(true)]
#[arg(long = "no-flag", action = ArgAction::SetFalse)]
pub flag: bool,
}
fn main() {
let args = Args::from(serde_yaml::from_str::<<Args as ClapSerde>::Opt>("number: 12").unwrap())
.merge_clap();
println!("{:?}", args);
}
请注意,以上内容需要在 Cargo.toml
中包含以下内容:
[dependencies]
clap = "*"
serde = "*"
serde_yaml = "*"
clap-serde-derive = "*"
cargo 上已经有许多箱子旨在实现类似的结果(例如 viperus、twelf、layeredconf),但它们使用没有派生的旧版本的 clap 和/或没有办法为 clap 和 serde 定义唯一的默认值.
我希望这个派生宏会有用。
更新
您可以通过这种方式轻松地从命令行获取配置文件路径。
use std::{fs::File, io::BufReader};
use clap_serde_derive::{
clap::{self, Parser},
ClapSerde,
};
#[derive(Parser)]
#[clap(author, version, about)]
struct Args {
/// Input files
input: Vec<std::path::PathBuf>,
/// Config file
#[clap(short, long = "config", default_value = "config.yml")]
config_path: std::path::PathBuf,
/// Rest of arguments
#[clap(flatten)]
pub config: <Config as ClapSerde>::Opt,
}
#[derive(ClapSerde)]
struct Config {
/// String argument
#[clap(short, long)]
name: String,
}
fn main() {
// Parse whole args with clap
let mut args = Args::parse();
// Get config file
let config = if let Ok(f) = File::open(&args.config_path) {
// Parse config with serde
match serde_yaml::from_reader::<_, <Config as ClapSerde>::Opt>(BufReader::new(f)) {
// merge config already parsed from clap
Ok(config) => Config::from(config).merge(&mut args.config),
Err(err) => panic!("Error in configuration file:\n{}", err),
}
} else {
// If there is not config file return only config parsed from clap
Config::from(&mut args.config)
};
}
关于rust - 有没有办法让拍手使用文件中的默认值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55133351/
我是一名优秀的程序员,十分优秀!