clap icon indicating copy to clipboard operation
clap copied to clipboard

#[command(flatten)] on optional field makes it required

Open smessmer opened this issue 2 years ago • 5 comments

Please complete the following tasks

Rust Version

1.72

Clap Version

4.3.4

Minimal reproducible code

use clap::{Args, Parser};

fn main() {
    MainArgs::parse();
}

#[derive(Parser, Debug)]
pub struct MainArgs {
    #[arg(short = 'V', long)]
    pub version: bool,

    #[command(flatten)]
    pub specific_args: Option<SpecificArgs>,
}

#[derive(Args, Debug)]
pub struct SpecificArgs {
    pub positional: String,
}

Steps to reproduce the bug with the above code

cargo r -- --version

Actual Behaviour

error: the following required arguments were not provided:
  <POSITIONAL>

Usage: test-executable --version <POSITIONAL>

For more information, try '--help'.

Expected Behaviour

No error because the SpecificArgs is optional.

Additional Context

I want to use this to have a top level Parser with a few global args (e.g. --version but also some others), and I want to reuse this across different CLI tools, each with their own ConcreteArgs. The ConcreteArgs are usually written as mandatory in their corresponding #[derive(Args)] parser, but they should only be mandatory if none of the global flags are present. To do this, I made SpecificArgs optional in the #[command(flatten)] but that doesn't seem to have the intended effect.

Similar discussion: https://github.com/clap-rs/clap/discussions/4954

Debug Output

No response

smessmer avatar Aug 27 '23 19:08 smessmer

We have a discrepancy in how we treat Option between Arg (required, no required) and ArgGroup (no-op, track whether present).

A lot of this depends on how people are using an ArgGroup

  • Composing structs: lack of Option shouldn't mean anything
  • Require or don't require an ArgGroup
  • Override required Args within an ArgGroup (what this issue wants

Can we pick a one-size-fits all option? If not, how do we allow variability of this?

epage avatar Aug 28 '23 13:08 epage

Is there a workaround?

jannschu avatar Sep 02 '23 20:09 jannschu

The way to workaround this heavily depends on your use case. If you want one argument to be present when the group is, you can use ArgGroup::requires.

As an example, let's take the reproduction case and change it to use that:

use clap::{Args, Parser};

fn main() {
    MainArgs::parse();
}

#[derive(Parser, Debug)]
pub struct MainArgs {
    #[arg(short = 'V', long)]
    pub version: bool,

    #[command(flatten)]
    pub specific_args: Option<SpecificArgs>,
}

#[derive(Args, Debug)]
#[group(requires = "positional")]
pub struct SpecificArgs {
    #[arg(required = false)]
    pub positional: String,
}

SpecificArgs will only be instantiated if the group is present and if the group is present then positional must be present as well, so you can skip using Option for it and just override the default required = true with required = false.

epage avatar Sep 03 '23 01:09 epage