rustcommand-line-interfaceclap

CLAP- make an arg conflicting with a group of arg


Hello i'm using clap to build a cli

What I want to achieve is simple: I have 2 groups of args fruits and vegetables I have a subcommand create to create a new fruit or vegetable

I could have 2 sepetate commands to create fruit and vegetables but I prefer to have only one command and pass an arg to create a fruit (--fruit) or a vegetable (--vegetable)

my_cli create --fruit // will create a fruit
my_cli create --vegetable // will create a vegetable

there are required args to create a fruit --weight --country there are required args to create a vegetable --type --destination

If I run my_cli create --fruit --type "root vegetable" it should pop an error because --fruit will conflicts with all args from vegetable group, same for --vegetable with args from fruit group

There is my code

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Commands,
    /// Apply command on fruit
    #[arg(
        short,
        long,
        global = true,
        conflicts_with = "vegetable",
        default_value = "true",
        default_value_if("vegetable", "true", "false"),
        display_order = 0
    )]
    pub fruit: bool,
    /// Apply command on vegetable
    #[arg(
        short,
        long,
        global = true,
        conflicts_with = "fruit",
        default_value = "false",
        default_value_if("fruit", "true", "false"),
        display_order = 0
    )]
    pub vegetable: bool,
}


#[derive(Subcommand)]
pub enum Commands {
    /// Create a new fruit or vegetable
    Create(CreateArgs),
}


#[derive(Args, Debug)]
pub struct CreateArgs {
    /// Type
    #[arg(long, display_order = 1, group = "vegetable_group")]
    pub type: Option<String>,
    /// Destination
    #[arg(long, display_order = 1, group = "vegetable_group")]
    pub destination: Option<String>,
    /// Weight
    #[arg(long, display_order = 1, group = "fruit_group")]
    pub weight: Option<String>,
    /// Country
    #[arg(long, display_order = 1, group = "fruit_group")]
    pub country: Option<String>,
}

How to make a group of args conflicts with an other?

I could do something like this for all args but with a lot of args this is not relevant

/// Destination
#[arg(long, display_order = 1, group = "vegetable_group", conflicts_with_all(["weight", "country"])]
pub destination: Option<String>,

Solution

  • Clap subcommands names can contain dashes, so the solution is simple (if tricky):

    use clap::{Parser, Subcommand};
    
    #[derive(Parser)]
    struct Cli {
        #[command(subcommand)]
        command: CreateCommand,
    }
    
    #[derive(Subcommand)]
    enum CreateCommand {
        Create {
            #[command(subcommand)]
            fruit: FruitCommands,
        },
    }
    
    #[derive(Subcommand)]
    enum FruitCommands {
        #[command(name = "--fruit")]
        Fruit {
            #[arg(long)]
            weight: u32,
            #[arg(long)]
            country: String,
        },
        #[command(name = "--vegetable")]
        Vegetable {
            #[arg(long)]
            r#type: String,
            #[arg(long)]
            destination: String,
        },
    }