[英]How to create a custom derive macro for clap structs in rust?
我正在嘗試在 rust 中創建某種簡單的 CLI 計費應用程序以供練習。 它是一種數據庫應用程序。 我有許多操作,我需要用戶能夠根據將對其進行操作的列的值進行過濾。
對於下面的示例,有刪除和顯示命令:
#[derive(Debug, Args)]
struct DeleteCommand {
/// The ID of the bill
#[clap(short, long, value_parser)]
id: Option<String>,
/// The name of the bill
#[clap(short, long, value_parser)]
name: Option<String>,
/// The value of the bill
#[clap(short, long, value_parser)]
value: Option<String>,
/// The amount of the billB
#[clap(short, long, value_parser)]
amount: Option<String>,
/// Datetime
#[clap(short, long, value_parser)]
datetime: Option<String>,
/// Currency
#[clap(short, long, value_parser)]
currency: Option<String>,
/// Recipient
#[clap(short, long, value_parser)]
recipient: Option<String>,
/// Situation
#[clap(short, long, value_parser)]
situation: Option<String>,
/// Hard
#[clap(short = 'H', long)]
hard: bool,
}
#[derive(Debug, Args)]
struct ShowCommand {
#[clap(subcommand)]
subcommand: ShowSubcommand,
/// The ID of the bill
#[clap(short, long, value_parser)]
id: Option<String>,
/// The name of the bill
#[clap(short, long, value_parser)]
name: Option<String>,
/// The value of the bill
#[clap(short, long, value_parser)]
value: Option<String>,
/// The amount of the bill
#[clap(short, long, value_parser)]
amount: Option<String>,
/// Datetime
#[clap(short, long, value_parser)]
datetime: Option<String>,
/// Currency
#[clap(short, long, value_parser)]
currency: Option<String>,
/// Datetime
#[clap(short, long, value_parser)]
recipient: Option<String>,
/// Datetime
#[clap(short, long, value_parser)]
situation: Option<String>,
/// Head
#[clap(long, value_parser)]
head: Option<u32>,
/// Tail
#[clap(long, value_parser)]
tail: Option<u32>,
/// Order by
#[clap(short, long, value_parser)]
orderby: Option<String>,
}
如您所見,兩個結構之間有許多公共字段,因為兩者都需要過濾數據庫中的項目。 我只想創建一個派生自定義宏,讓我可以簡單地在結構中重復這些行,而無需一次又一次地編寫它:
/// The ID of the bill
#[clap(short, long, value_parser)]
id: Option<String>,
/// The name of the bill
#[clap(short, long, value_parser)]
name: Option<String>,
/// The value of the bill
#[clap(short, long, value_parser)]
value: Option<String>,
/// The amount of the bill
#[clap(short, long, value_parser)]
amount: Option<String>,
/// Datetime
#[clap(short, long, value_parser)]
datetime: Option<String>,
/// Currency
#[clap(short, long, value_parser)]
currency: Option<String>,
/// Recipient
#[clap(short, long, value_parser)]
recipient: Option<String>,
/// Situation
#[clap(short, long, value_parser)]
situation: Option<String>,
我還需要重復 /// 行,因為它將成為 CLI 中每個參數的描述。
最小可重現示例:
這就是我所擁有的
use clap::{Parser, Subcommand, Args};
#[derive(Debug, Parser)]
pub struct UserInput {
#[clap(subcommand)]
command: Command,
}
#[derive(Debug, Subcommand)]
enum Command {
/// A command description
A(CommandA),
/// B command description
B(CommandB),
}
#[derive(Debug, Args)]
struct CommandA {
/// The value of column x to be filtered in the database
#[clap(short, long, value_parser)]
x_value: Option<String>,
/// The value of column y to be filtered in the database
#[clap(short, long, value_parser)]
y_value: Option<String>,
/// Some particular parameter of this command
#[clap(short, long, value_parser)]
particular: Option<String>,
}
#[derive(Debug, Args)]
struct CommandB {
/// The value of x to be filtered in the database
#[clap(short, long, value_parser)]
x_value: Option<String>,
/// The value of y to be filtered in the database
#[clap(short, long, value_parser)]
y_value: Option<String>,
/// Some particular parameter of this command
#[clap(short, long, value_parser)]
particular: Option<String>,
}
fn main() {
let user_input: UserInput = UserInput::parse();
print!("{:#?}", user_input)
}
這就是我要的:
use clap::{Parser, Subcommand, Args};
#[derive(Debug, Parser)]
pub struct UserInput {
#[clap(subcommand)]
command: Command,
}
#[derive(Debug, Subcommand)]
enum Command {
/// A command description
A(CommandA),
/// B command description
B(CommandB),
}
#[derive(Debug, Args, Filter)]
struct CommandA {
/// Some particular parameter command A
#[clap(short, long, value_parser)]
particular: Option<String>,
}
#[derive(Debug, Args, Filter)]
struct CommandB {
/// Some particular parameter command B
#[clap(short, long, value_parser)]
particular: Option<String>,
}
fn main() {
let user_input: UserInput = UserInput::parse();
print!("{:#?}", user_input)
}
等待行為:
[user@host appname]$ appname a --help
p1-a
A command description
USAGE:
p1 a [OPTIONS]
OPTIONS:
-x, --x-value <X_VALUE> The value of column X to be filtered in the database
-y, --y-value <Y_VALUE> The value of column Y to be filtered in the database
-h, --help Print help information
-p, --particular <PARTICULAR> Some particular parameter of command A
編輯:我正在使用 clap 3.2.22,但我會嘗試將我的代碼移動到最新版本。
您可以像這樣使用組合和#[clap(flatten)]
來實現您想要的效果:
use clap::{Args, Parser, Subcommand};
#[derive(Debug, Args)]
struct CommonArgs {
/// The value of column x to be filtered in the database
#[clap(short, long, value_parser)]
x_value: Option<String>,
/// The value of column y to be filtered in the database
#[clap(short, long, value_parser)]
y_value: Option<String>,
}
#[derive(Debug, Args)]
struct ArgsA {
#[clap(flatten)]
common_args: CommonArgs,
/// Hard
#[clap(short = 'H', long)]
particular_a: bool,
}
#[derive(Debug, Args)]
struct ArgsB {
// #[clap(subcommand)]
// subcommand: ShowSubcommand,
#[clap(flatten)]
commmon_args: CommonArgs,
/// Head
#[clap(long)]
particular_b: Option<u32>,
}
#[derive(Debug, Parser)]
pub struct UserInput {
#[clap(subcommand)]
command: MyCommand,
}
#[derive(Debug, Subcommand)]
enum MyCommand {
/// A command description
A(ArgsA),
/// B command description
B(ArgsB),
}
fn main() {
dbg!(UserInput::parse_from("playground b --help".split(' ')));
}
唯一的區別是您可以在訪問時多一級間接訪問。
這種“缺點”帶來的好處是您可以更輕松地傳遞 arguments 的部分內容,例如為ArgsA
和ArgsB
實施AsRef<CommonArgs>
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.