[clap] Mutually exclusive options

I have an application with a struct containing:

  #[arg(short = 'p', long, conflicts_with = "publish_real")]
  pub(crate) publish_dry: bool,

  #[arg(short = 'P', long)]
  pub(crate) publish_real: bool,

The idea is to disallow --publish-real and --publish-dry at the same time. This works. However, I'm wondering if it's possible to express this as an enum instead? (The two booleans can never be true at the same time, so an Option<SomeEnum> would be more semantically correct). Something along the line of:

// ...
enum Publish {
  #[clap(long = "--publish-dry")]
  DryRun,

  #[clap(long = "--publish-real")]
  Real
}

// ...
struct Args {
  #[clap(something_magic)]
  publish: Option<Publish>
}

Is this possible? I did some quick google searches and found github issues which discusses this type of construction, but it's unclear if this was implemented.

1 Like

There is a #[clap(group)] attribute, but I am not entirely sure how to use it for mutually exclusive arguments.

1 Like

The best I could come up with is this:

use clap::{self,Parser,ValueEnum,ArgAction::SetTrue}; // 4.3.11

#[derive(ValueEnum,Copy,Clone,Debug)]
#[value(rename_all = "kebab-case")]
enum Publish {
    DryRun,
    Real
}


#[derive(clap::Args,Copy,Clone,Debug)]
#[group(multiple=false)]
struct PublishArgs {
  #[arg(short='p', action=SetTrue)]
  _dry_run: (),

  #[arg(short='P', action=SetTrue)]
  _real: (),
  
  #[arg(long = "publish", num_args = 1, require_equals=true, default_value_ifs=[
    ("_dry_run", "true", "dry-run"),
    ("_real", "true", "real")
  ])]
  pub(crate) publish: Option<Publish>
}


#[derive(Parser, Debug)]
#[command()]
struct Args {
  #[clap(flatten)]
  publish: PublishArgs,
}

Edit: A previous version of this post didn't support the short aliases -P/-p. That code can be found here:

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.