簡體   English   中英

如何為 rust 中的 clap 結構創建自定義派生宏?

[英]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 的部分內容,例如為ArgsAArgsB實施AsRef<CommonArgs>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM