16 个版本
0.4.0-pre.1 | 2024年4月29日 |
---|---|
0.3.2 | 2023年12月18日 |
0.3.1 | 2022年10月22日 |
0.2.4 | 2022年3月2日 |
0.1.4 | 2021年3月5日 |
#81 在 命令行界面
35,259 每月下载量
在 19 个 库中使用(17 个直接使用)
21KB
259 行
xflags
提供了解析命令行参数的过程宏。
它适用于开发工具,因此它强调快速的编译时间和便利性,而牺牲了一些功能。
选择参数解析库的粗略决策树
- 如果你需要所有功能并且不关心简约主义,请使用 clap
- 如果你想尽可能简约,只需要基本功能(例如,不需要帮助生成),并且想做到一丝不苟,请使用 lexopt
- 如果你想快速完成任务(例如,你想自动生成帮助,但不想因为等待 syn 编译而等待),请考虑这个库。
xflags 的秘密在于它是一个与 derive 宏相反的宏。它不是从 Rust 结构体生成命令行语法,而是根据输入语法生成 Rust 结构体。语法定义既简短又简单,编译时间也更短。
以下是 parse_or_exit!
宏的完整示例,该宏将参数解析到“匿名”结构体中
use std::path::PathBuf;
fn main() {
let flags = xflags::parse_or_exit! {
/// Remove directories and their contents recursively.
optional -r,--recursive
/// File or directory to remove
required path: PathBuf
};
println!(
"removing {}{}",
flags.path.display(),
if flags.recursive { "recursively" } else { "" },
)
}
上述程序在带有 --help
参数运行时,会生成以下帮助信息
Usage: <path> [-r] [-h]
Arguments:
<path> File or directory to remove
Options:
-r, --recursive Remove directories and their contents recursively.
-h, --help Prints help
Commands:
help Print this message or the help of the given subcommand(s)
对于较大的程序,通常希望使用 xflags!
宏,该宏为您生成 命名 结构体。与典型宏不同,xflags
将生成的代码写入源文件,以便您可以轻松了解 rust 类型。
mod flags {
use std::path::PathBuf;
xflags::xflags! {
src "./examples/basic.rs"
cmd my-command {
required path: PathBuf
optional -v, --verbose
}
}
// generated start
// The following code is generated by `xflags` macro.
// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
#[derive(Debug)]
pub struct MyCommand {
pub path: PathBuf,
pub verbose: bool,
}
impl MyCommand {
pub fn from_env_or_exit() -> Self {
Self::from_env_or_exit_()
}
pub fn from_env() -> xflags::Result<Self> {
Self::from_env_()
}
pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
Self::from_vec_(args)
}
}
// generated end
}
fn main() {
let flags = flags::MyCommand::from_env();
println!("{:#?}", flags);
}
如果您希望使用生成隐藏代码的典型 proc-macro,请省略 src 属性。
xflags 正确处理非 utf8 参数。
语法参考
xflags!
宏使用 cmd 关键字来引入接受位置参数和开关的命令或子命令。
xflags::xflags! {
cmd command-name { }
}
开关指定在花括号内。长名称(--switch
)是必须的,短名称(-s
)是可选的。每个开关可以是 可选的、必需的 或 重复的。开关名称中允许使用破折号。
xflags::xflags! {
cmd switches {
optional -q,--quiet
required --pass-me
repeated --verbose
}
}
开关还可以接受值。如果值类型是 OsString
或 PathBuf
,则直接从底层参数创建。否则,使用 FromStr
进行解析
use std::{path::PathBuf, ffi::OsString};
xflags::xflags! {
cmd switches-with-values {
optional --config path: PathBuf
repeated --data val: OsString
optional -j, --jobs n: u32
}
}
没有 --
的参数是位置参数。
use std::{path::PathBuf, ffi::OsString};
xflags::xflags! {
cmd positional-arguments {
required program: PathBuf
repeated args: OsString
}
}
如果需要,可以创建别名,这就像在 cmd
定义中添加额外的名称一样简单。在这种情况下,run
可以被称为 run
、r
和 exec
xflags::xflags! {
cmd run r exec {}
}
允许嵌套 cmd。 xflag
自动为子命令生成样板枚举
xflags::xflags! {
src "./examples/subcommands.rs"
cmd app {
repeated -v, --verbose
cmd foo { optional -s, --switch }
cmd bar {}
}
}
// generated start
// The following code is generated by `xflags` macro.
// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
#[derive(Debug)]
pub struct App {
pub verbose: u32,
pub subcommand: AppCmd,
}
#[derive(Debug)]
pub enum AppCmd {
Foo(Foo),
Bar(Bar),
}
#[derive(Debug)]
pub struct Foo {
pub switch: bool,
}
#[derive(Debug)]
pub struct Bar {
}
impl App {
pub fn from_env_or_exit() -> Self {
Self::from_env_or_exit_()
}
pub fn from_env() -> xflags::Result<Self> {
Self::from_env_()
}
pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
Self::from_vec_(args)
}
}
// generated end
开关总是“继承的”。无论是 app -v foo
还是 app foo -v
,结果都是一样的。
要使子命令名称可选,使用 default 关键字标记子命令,以选择是否未传递子命令名称。默认子命令的名称仅影响生成的 Rust 结构体的名称,不能在命令行上显式指定。
xflags::xflags! {
cmd app {
repeated -v, --verbose
default cmd foo { optional -s, --switch }
cmd bar {}
}
}
命令、参数和开关都可以进行文档说明。文档注释成为生成帮助的一部分
mod flags {
use std::path::PathBuf;
xflags::xflags! {
/// Run basic system diagnostics.
cmd healthck {
/// Optional configuration file.
optional config: PathBuf
/// Verbosity level, can be repeated multiple times.
repeated -v, --verbose
}
}
}
fn main() {
match flags::Healthck::from_env() {
Ok(flags) => {
run_checks(flags.config, flags.verbose);
}
Err(err) => err.exit()
}
}
关键字 src 控制代码生成的方式。如果它不存在,xflags
作为典型的过程宏,生成大量结构和实现。
如果存在 src 关键字,它应该指定包含 xflags!
调用的文件的路径。路径应该是相对于包含 Cargo.toml 的目录的相对路径。然后,宏将避免生成结构。相反,如果设置了 UPDATE_XFLAGS
环境变量,则宏将直接将其写入指定的文件。
按照惯例,xflag!
宏应从 flags
子模块调用。应使用 flags::
前缀来引用命令名称。额外的验证逻辑可以放在 flags
模块中
mod flags {
xflags::xflags! {
cmd my-command {
repeated -v, --verbose
optional -q, --quiet
}
}
impl MyCommand {
fn validate(&self) -> xflags::Result<()> {
if self.quiet && self.verbose > 0 {
return Err(xflags::Error::new(
"`-q` and `-v` can't be specified at the same time"
));
}
Ok(())
}
}
}
parse_or_exit!
宏是 xflags!
的语法糖,它立即解析参数,如果需要则退出进程。 parse_or_exit
只支持单个顶级命令,不需要 cmd
关键字。
限制
xflags
遵循 Fuchsia 命令行参数约定。不支持像分组短标志(-xyz
)或粘合短标志和值((-fVAL)
)这样的 GNU 约定。
xflags
要求命令行界面完全静态。无法在运行时包含额外的标志。
实现不是完全健壮的,边缘情况下可能存在一些残留错误。