gpt4 book ai didi

rust - 有没有办法让拍手使用文件中的默认值?

转载 作者:行者123 更新时间:2023-11-29 08:15:35 26 4
gpt4 key购买 nike

我正在使用 clap 编写 CLI 程序解析我的论点。我想为选项提供默认值,但如果有配置文件,配置文件应该胜过默认值。

设置命令行参数的优先级高于默认值很容易,但我希望优先级顺序为:

  1. 命令行参数
  2. 配置文件
  3. 默认值

如果配置文件不是由命令行选项设置的,设置它也很容易,只需在运行 parse_args 之前解析配置文件,并提供解析后的配置值文件到 default_value。问题是,如果您在命令行中指定配置文件,则只有在解析之后才能更改默认值。

我能想到的唯一方法是不设置 default_value,然后手动匹配 value_of 中的 ""。问题是在那种情况下,clap 将无法构建有用的 --help

有没有办法让 clap 读取配置文件本身?

最佳答案

对于将受益于派生宏的 clap v3 或 clap v4 的用户,我解决了这个问题,制作了两个结构:一个是目标结构,另一个是相同的但所有字段都是可选的。我用 serde 从配置文件和命令行用 clap 解析第二个结构,然后这些结构可以合并到第一个结构中:配置/命令行参数中不存在 None 的元素。

为此,我创建了一个派生宏 ( ClapSerde),它会自动执行以下操作:

  • 创建带有可选字段的结构
  • 派生 clap Parser 并在其上进行 serde Deserialize
  • 提供了将带有可选字段的 clap 和反序列化(使用 serde)结构合并到目标结构中的方法;这可用于创建分层配置解析器,即用于请求的情况
// 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com