Clap, ignore global argument in sub-command

Hey,

I currently have the following code to represent my clap options:

# Cargo.toml

[dependencies.clap]
version = "4.4.7"
features = ["derive"]
// main.rs

use clap::{Parser, Subcommand};

#[derive(Parser)]
struct Cli {
    #[arg(short, long)]
    foo: String,
    #[command(subcommand)]
    command: Option<Command>,
}

#[derive(Subcommand)]
enum Command {
    Bar { baz: String },
}

fn main() {
    let _cli = Cli::parse();
}

Currently, when I try to run my application with the bar command cargo r -- bar "example" I get the following error message the following required arguments were not provided: --foo <FOO> I would like to be able to call the bar command without providing the foo argument, unfortunately I couldn't find any solution to this problem in the clap documentation, am I missing something from the documentation that would solve this issue?

Thanks!

AFAIK you can change the type of foo to Option<String> to make it an optional argument.

Let's strip out decorations.

Can you create a valid Cli without providing a value for foo by hands?

Yep, that's why the current default implementation doesn't work, which is why I am asking if there's something within clap that allows me to specify that an argument is optional if a certain sub-command is called. Of course, I could go ahead and specify that foo is an optional value as suggested in the previous comment, but that would require me to write a manual check to see if foo is Some or None when not calling the sub-command which I am going to do if there's no other solution that I am missing.

In similar situation I would move foo inside those subcommands that need it and optionally change Cli into enum if there's no arguments left on a top level, but I'm not using clap so I'm not sure how to do it with it.

Yeah, similarly to how I did it with baz inside bar unfortunately that doesn't work with foo since it's an argument that is required when calling the application without any command thus being in the root

That's extra motivation to make Cli into enum with "no command" being one of the branches - that's what I'd do.

I might have found the solution whilst scrolling through the documentation of the individual functions.

The trick is to set the foo argument as an optional value in order to construct the Cli struct without specifying a specific value for foo. By adding the required attribute to the arg proc-macro the caller will still be required to provide the foo argument when calling the application, but by adding the top level command proc-macro with the attribute subcommand_negates_reqs we can then ignore all the required arguments from the parent when calling a sub-command.

use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(subcommand_negates_reqs = true)]
struct Cli {
    #[arg(short, long, required = true)]
    foo: Option<String>,
    #[command(subcommand)]
    command: Option<Command>,
}

#[derive(Subcommand)]
enum Command {
    Bar { baz: String },
}
1 Like