之前学习了Rust中的Clap crate, 前几天又接触了Rust实现的一个命令行记录工具Atuin, 他的命令行参数就是使用Clap实现, 并且参数众多, 做了不同的分层. 今天就扒一扒Atuin的实现方式, 加深一下对Clap的理解.
下图是整理的Atuin部分命令配置:
首先观察紫色的这条线部分, 并结合前部在AtuinCmd和client::Cmd添加的subcommand和flatten可以知道, history应该是最上层的命令, 这从帮助信息中可以看到. 再观察紫色框中的内容, 可以知道history会有 start, end, list, last这些命令, 同时需要输入的参数是, command, id, cwd, session等.

再观察酒红色这一部分, 这里使用的是Parser派生. 同时命令参数的类型是enum, 没有附加数据. 这些命令每次只能指定一个, 并且不带有参数.

接下来看绿色方框的部分, 这里使用Commands类型又多做了一层封装. 如果换成红色方框的结构, 也可以编译通过. 在Commands中的类型是命令, 而在比如login::Cmd中的类型就是参数, 使用的是Parser派生.

总结:
在Atuin的代码中, 可以看到它是使用#[command(flatten)] #[command(subcomannd)] #[derive(Subcommand)] 来完成层级结构的规划的.
- 其中
#[command(flatten)]是将字命令提升一级, 相当于用子命令替换了#[command(flatten)]所在的参数. #[command(subcomannd)]和#[derive(Subcommand)]组合完成子命令的创建.
同时可以看到的是 #[derive(Parser)]和#[derive(Args)]的使用.
在只有具体参数选项的结构中, 使用的是#[derive(Parser)], 比如login::Cmd和import::Cmd. Clap crate的文档中对Parser的解释就是
Parse command-line arguments into
Self.
将参数解析到Self(我理解可以是enum或者struct)当中. 也就是最后需要完成参数解析了.
对Args的解释是
Parse a set of arguments into a user-defined container.
Implementing this trait lets a parent container delegate argument parsing behavior to
Self. with:
#[command(flatten)] args: ChildArgs: Attribute can only be used with struct fields that implArgs.Variant(ChildArgs): No attribute is used with enum variants that implArgs.(适用当前account::Cmd的情况)
对Subcommand的解释是
Parse a sub-command into a user-defined enum.
Implementing this trait lets a parent container delegate subcommand behavior to
Self. with:
#[command(subcommand)] field: SubCmd: Attribute can be used with either struct fields or enum variants that implSubcommand.#[command(flatten)] Variant(SubCmd): Attribute can only be used with enum variants that implSubcommand.
从两个官方给出的解释可以看到, Args是解析参数集合到用户定义的容器中(只要求容器类型), Subcommand是解析子命令到用户定义的枚举中(只能是枚举). 还有的不同是(这块还没理解到, 后面补充)
rust
#[command(flatten)]
args: ChildArgs --> ChildArgs必须是实现了Args的struct
#[command(flatten)]
Variant(SubCmd) --> SubCmd必须是实现了Subcommand的enum