Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically detect allow_missing_positional #4837

Open
2 tasks done
joshtriplett opened this issue Apr 17, 2023 · 4 comments
Open
2 tasks done

Automatically detect allow_missing_positional #4837

joshtriplett opened this issue Apr 17, 2023 · 4 comments
Labels
A-derive Area: #[derive]` macro API C-enhancement Category: Raise on the bar on expectations S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing

Comments

@joshtriplett
Copy link
Contributor

Please complete the following tasks

Clap Version

4.2.2

Describe your use case

I'd like to make a CLI that accepts either "arg1 arg2" or just "arg2" (the first argument is optional).

I wrote this:

#[derive(clap::Args, Debug)]
#[command(arg_required_else_help = true)]
pub struct MySubcommand {
    /// ...
    pub name: Option<String>,

    /// ...
    pub dir: PathBuf,

    /// ...
}

This did cause clap to realize that name should be optional, based on the generated help:

subcommand description

Usage: cmd subcmd [OPTIONS] [NAME] <DIR>

Arguments:
  [NAME]  ...
  <DIR>   ...

Notice the [NAME] vs <DIR>.

However, if I actually run the command with one argument:

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

Usage: cmd subcmd <NAME> <DIR>

I had to supply #[command(allow_missing_positional = true)] to make this work as expected.

Describe the solution you'd like

I'd like clap to automatically assume that if it has a single positional argument specified with Option, it should assume allow_missing_positional = true.

Alternatives, if applicable

If there's a strong reason not to autodetect this, then instead, I'd suggest producing an error on an Option positional argument with allow_missing_positional = false.

However, if at all possible I'd suggest making this work automatically.

Additional Context

No response

@joshtriplett joshtriplett added the C-enhancement Category: Raise on the bar on expectations label Apr 17, 2023
@epage epage added A-derive Area: #[derive]` macro API S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing labels Apr 17, 2023
@epage
Copy link
Member

epage commented Apr 17, 2023

The challenges this would run into

  • (either solution) Technically you could flatten in positionals from different structs, making this impossible to do with flatten and I've been loathe to offer additional features only for the non-flatten cases (see also Duplicate short option (and other misconfigurations) gives runtime error, wouldn't it be cool, if it was compile time error #3133)
  • (unlikely but ...) a developer could set index to an expression and we would be in a similar opaque situation as flatten
  • (unlikely but ....) a developer could set index to a literal, requiring us to start tracking it so we can detect either case. The more we add to the derive, the slower it is to compile and run, so we need to be judicious in what we add
  • There are times when a user might use Option but set #[arg(required = true)] (e.g. a conflict with all positionals). While Support for using ArgGroup as Enum with derive #2621 will improve this, people might still want to do the current workaround. This ends up being another case of index of dealing with expressions and literals
  • (a bit hand waive-y) allow_missing_positional is not very polished at this time (seems like people are regularly running into limitations) and the more we streamline it without polish, the more people are likely to use it and get frustrated at the limitations

None of this is to say "no". This case is a bit more peculiar than something like short, making it less likely to run into problems but I currently lean towards "no" for a simpler, more predictable experience.

@epage
Copy link
Member

epage commented Apr 17, 2023

This did cause clap to realize that name should be optional, based on the generated help:

This should have caused a debug assert to fire

#!/usr/bin/env cargo-eval

//! ```cargo
//! [dependencies]
//! clap = { version = "4", features = ["derive"] }
//! ```

use clap::{Args, Parser, Subcommand};
use std::path::PathBuf;

#[derive(Parser, Debug)]
#[command(arg_required_else_help = true)]
pub struct Cli {
    /// ...
    pub name: Option<String>,

    /// ...
    pub dir: PathBuf,
}

fn main() {
    let cli = Cli::parse();
    dbg!(cli);
}
thread 'main' panicked at 'Found non-required positional argument with a lower index than a required positional argument: "name" index Some(1)', /home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/clap_builder-4.2.2/src/builder/debug_asserts.rs:639:17

I could see us suggesting last or allow_misisng_positional in the error message.

@nyurik
Copy link
Contributor

nyurik commented Oct 24, 2023

@epage I ran into the same issue when trying to implement cp shell command-like interface (any number of sources followed by a single target - cp src1 src2 src3 destination). Are there any suggested ways to do this, or is the best to simply implement a single vector of PathBufs, and explain in the help message what those should be? Thx!

@epage
Copy link
Member

epage commented Oct 25, 2023

atm a single vector of PathBuf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-derive Area: #[derive]` macro API C-enhancement Category: Raise on the bar on expectations S-waiting-on-design Status: Waiting on user-facing design to be resolved before implementing
Projects
None yet
Development

No branches or pull requests

3 participants