Skip to content

dependent optional arguments #6258

@TheJJ

Description

@TheJJ

Please complete the following tasks

Clap Version

master

Describe your use case

i have a building tool with several build backends.
depending on the used backend, it can have different other arguments.

example:

  • select the build backend with --driver docker, this unlocks other options like --dockerfile <name>.
  • when we instead use --driver lxd, we have other options such as --lxc-raw "lxc.init.cmd = /bin/bash"

Describe the solution you'd like

this can be done very similar to Subcommands, but without enforcing the positional order.

this could be Subargs or something. definition wise, you could define them just like subcommands, in the above case

#[derive(Parser, Debug)]
#[command(name = "debmagic")]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand, Debug)]
pub enum Commands {
    Build(BuildSubcommandArgs),
    ...
}

#[derive(Args, Debug)]
pub struct BuildSubcommandArgs {
    #[arg(short, long)]
    pub driver: Option<BuildDriverType>,

    #[arg(short, long)]
    pub source_dir: Option<PathBuf>,
}

#[derive(Subargs, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
pub enum BuildDriverType {
    Docker(DockerDriverArgs),
    Lxd(LxdDriverArgs),
}

#[derive(Args, Debug)]
pub struct DockerDriverSubargs {
    #[arg(long)]
    pub dockerfile: Option<String>,
    ...
}

#[derive(Args, Debug)]
pub struct LxdDriverSubargs {
    #[arg(long)]
    pub lxc_raw: Option<String>,
    ...
}

Alternatives, if applicable

currently, you would

  • either make the driver a subcommand, which doesn't fit its optional nature and position independence.
  • or add the arguments at the same level as the driver selection, and then use ArgGroups. this doesn't model the variable relations and encapsulation properly, though.
#[derive(clap::Subcommand, Debug)]
enum Commands {
    #[command(
        group(
            ArgGroup::new("docker_args")
                .args(["docker_image"])
                .conflicts_with("lxd_args")
        ),
        group(
            ArgGroup::new("lxd_args")
                .args(["lxc_options"])
        )
    )]
    Pack {
        #[arg(long, value_enum)]
        driver: Option<Driver>,

        /// docker options
        #[arg(long, group = "docker_args")]
        docker_image: Option<String>,
        ...

        /// lxd options
        #[arg(long, group = "lxd_args")]
        lxc_options: Option<String>,
        ....
    },
}

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-deriveArea: #[derive]` macro APIA-validatorsArea: ArgMatches validation logiC-enhancementCategory: Raise on the bar on expectationsS-triageStatus: New; needs maintainer attention.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions