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

No way to accept unbounded positional arguments with subcommand #3889

Closed
2 tasks done
nissaofthesea opened this issue Jun 30, 2022 · 4 comments
Closed
2 tasks done

No way to accept unbounded positional arguments with subcommand #3889

nissaofthesea opened this issue Jun 30, 2022 · 4 comments
Labels
C-bug Category: Updating dependencies

Comments

@nissaofthesea
Copy link

nissaofthesea commented Jun 30, 2022

Please complete the following tasks

Rust Version

rustc 1.61.0 (fe5b13d68 2022-05-18)

Clap Version

3.2.8

Minimal reproducible code

use clap::{Parser, Subcommand};

#[derive(Parser, Debug)]
#[clap(subcommand_required = true)]
struct Args {
    #[clap()]
    names: Vec<String>,

    #[clap(subcommand)]
    action: Action,
}

#[derive(Subcommand, Debug)]
enum Action {
    Greet,
}

fn main() {
    for name in Args::parse().names {
        println!("hey {}", name);
    }
}

Steps to reproduce the bug with the above code

  1. See help message
$ cargo r -- -h
cli

USAGE:
    cli [NAMES]... <SUBCOMMAND>

ARGS:
    <NAMES>...    

OPTIONS:
    -h, --help    Print help information

SUBCOMMANDS:
    greet    
    help     Print this message or the help of the given subcommand(s)
  1. Run program according to help message
$ cargo r -- name1 greet

Actual Behaviour

When I run the program according to the help message usage, it returns an error

$ cargo r -- name1 greet
error: 'cli' requires a subcommand but one was not provided

USAGE:
    cli [NAMES]... <SUBCOMMAND>

For more information try --help

Expected Behaviour

The syntax given by the help message should work, or the help message should give a valid syntax.

$ cargo r -- name1 greet
hey name1

Or, there should be an assertion somewhere about this specific configuration, since it also doesn't work to pass the names after the subcommand (there is no way to pass names).

Additional Context

Related: #3721

I'm able to pass names when names is global, but like is mentioned in the related issue, the help message shows the order differently than is valid.

Apologies if this is the same issue as #3721.

Debug Output

[      clap::builder::command] 	Command::_do_parse
[      clap::builder::command] 	Command::_build: name="cli"
[      clap::builder::command] 	Command::_propagate:cli
[      clap::builder::command] 	Command::_check_help_and_version: cli
[      clap::builder::command] 	Command::_check_help_and_version: Removing generated version
[      clap::builder::command] 	Command::_check_help_and_version: Building help subcommand
[      clap::builder::command] 	Command::_propagate_global_args:cli
[      clap::builder::command] 	Command::_propagate removing greet's help
[      clap::builder::command] 	Command::_propagate pushing help to greet
[      clap::builder::command] 	Command::_propagate pushing names to greet
[      clap::builder::command] 	Command::_propagate removing help's help
[      clap::builder::command] 	Command::_propagate pushing help to help
[      clap::builder::command] 	Command::_propagate pushing names to help
[      clap::builder::command] 	Command::_derive_display_order:cli
[      clap::builder::command] 	Command::_derive_display_order:greet
[      clap::builder::command] 	Command::_derive_display_order:help
[clap::builder::debug_asserts] 	Command::_debug_asserts
[clap::builder::debug_asserts] 	Arg::_debug_asserts:help
[clap::builder::debug_asserts] 	Arg::_debug_asserts:names
[clap::builder::debug_asserts] 	Command::_verify_positionals
[        clap::parser::parser] 	Parser::get_matches_with
[        clap::parser::parser] 	Parser::get_matches_with: Begin parsing 'RawOsStr("name1")' ([110, 97, 109, 101, 49])
[        clap::parser::parser] 	Parser::get_matches_with: Positional counter...1
[        clap::parser::parser] 	Parser::get_matches_with: Low index multiples...false
[        clap::parser::parser] 	Parser::possible_subcommand: arg=Ok("name1")
[        clap::parser::parser] 	Parser::get_matches_with: sc=None
[        clap::parser::parser] 	Parser::split_arg_values; arg=names, val=RawOsStr("name1")
[        clap::parser::parser] 	Parser::split_arg_values; trailing_values=false, DontDelimTrailingVals=false
[        clap::parser::parser] 	Parser::get_matches_with: Begin parsing 'RawOsStr("greet")' ([103, 114, 101, 101, 116])
[        clap::parser::parser] 	Parser::get_matches_with: Positional counter...1
[        clap::parser::parser] 	Parser::get_matches_with: Low index multiples...false
[        clap::parser::parser] 	Parser::resolve_pending: id=names
[        clap::parser::parser] 	Parser::react action=StoreValue, identifier=Some(Index), source=CommandLine
[        clap::parser::parser] 	Parser::remove_overrides: id=names
[   clap::parser::arg_matcher] 	ArgMatcher::start_occurrence_of_arg: id=names
[      clap::builder::command] 	Command::groups_for_arg: id=names
[        clap::parser::parser] 	Parser::push_arg_values: ["name1"]
[        clap::parser::parser] 	Parser::add_single_val_to_arg: cur_idx:=1
[      clap::builder::command] 	Command::groups_for_arg: id=names
[   clap::parser::arg_matcher] 	ArgMatcher::needs_more_vals: o=names, resolved=1, pending=0
[        clap::parser::parser] 	Parser::split_arg_values; arg=names, val=RawOsStr("greet")
[        clap::parser::parser] 	Parser::split_arg_values; trailing_values=false, DontDelimTrailingVals=false
[        clap::parser::parser] 	Parser::resolve_pending: id=names
[        clap::parser::parser] 	Parser::react action=StoreValue, identifier=Some(Index), source=CommandLine
[        clap::parser::parser] 	Parser::remove_overrides: id=names
[   clap::parser::arg_matcher] 	ArgMatcher::start_occurrence_of_arg: id=names
[      clap::builder::command] 	Command::groups_for_arg: id=names
[        clap::parser::parser] 	Parser::push_arg_values: ["greet"]
[        clap::parser::parser] 	Parser::add_single_val_to_arg: cur_idx:=2
[      clap::builder::command] 	Command::groups_for_arg: id=names
[   clap::parser::arg_matcher] 	ArgMatcher::needs_more_vals: o=names, resolved=2, pending=0
[        clap::parser::parser] 	Parser::add_defaults
[        clap::parser::parser] 	Parser::add_defaults:iter:help:
[        clap::parser::parser] 	Parser::add_default_value:iter:help: doesn't have default missing vals
[        clap::parser::parser] 	Parser::add_default_value: doesn't have conditional defaults
[        clap::parser::parser] 	Parser::add_default_value:iter:help: doesn't have default vals
[        clap::parser::parser] 	Parser::add_defaults:iter:names:
[        clap::parser::parser] 	Parser::add_default_value:iter:names: doesn't have default missing vals
[        clap::parser::parser] 	Parser::add_default_value: doesn't have conditional defaults
[        clap::parser::parser] 	Parser::add_default_value:iter:names: doesn't have default vals
[     clap::parser::validator] 	Validator::validate
[         clap::output::usage] 	Usage::create_usage_with_title
[         clap::output::usage] 	Usage::create_usage_no_title
[         clap::output::usage] 	Usage::create_help_usage; incl_reqs=true
[         clap::output::usage] 	Usage::get_required_usage_from: incls=[], matcher=false, incl_last=false
[         clap::output::usage] 	Usage::get_required_usage_from: unrolled_reqs={}
[         clap::output::usage] 	Usage::get_required_usage_from: ret_val={}
[         clap::output::usage] 	Usage::needs_options_tag
[         clap::output::usage] 	Usage::needs_options_tag:iter: f=help
[         clap::output::usage] 	Usage::needs_options_tag:iter Option is built-in
[         clap::output::usage] 	Usage::needs_options_tag: [OPTIONS] not required
[         clap::output::usage] 	Usage::get_args_tag; incl_reqs = true
[         clap::output::usage] 	Usage::get_args_tag:iter:names
[      clap::builder::command] 	Command::groups_for_arg: id=names
[         clap::output::usage] 	Usage::get_args_tag:iter: 1 Args not required or hidden
[      clap::builder::command] 	Command::groups_for_arg: id=names
[         clap::output::usage] 	Usage::get_args_tag:iter: Exactly one, returning 'names'
[          clap::builder::arg] 	Arg::name_no_brackets:names
[          clap::builder::arg] 	Arg::name_no_brackets: val_names=[
    "NAMES",
]
[         clap::output::usage] 	Usage::create_help_usage: usage=cli [NAMES]... <SUBCOMMAND>
[      clap::builder::command] 	Command::color: Color setting...
[      clap::builder::command] 	Auto
@nissaofthesea nissaofthesea added the C-bug Category: Updating dependencies label Jun 30, 2022
@epage
Copy link
Member

epage commented Jul 2, 2022

The syntax given by the help message should work, or the help message should give a valid syntax.

This requires the user doing this intentionally and designing their CLI so that a valid positional value never conflicts with a subcommand and to never use external_subcommand

Or, there should be an assertion somewhere about this specific configuration, since it also doesn't work to pass the names after the subcommand (there is no way to pass names).

This is describing #3721

If you feel this is an acceptable resolution, I might close this as a duplicate of that.

@nissaofthesea
Copy link
Author

Mhm, having an assertion for this specific case would be a clear way to enforce that as a way not to design a cli.
With that/when assertion is added I think this issue is resolved.

#3721 is a little different because the positional argument there is global.
For that issue, I do think the help message should be different; I'm not sure I followed your comment there. Using the format given by the help message doesn't actually work, you have to put the positional argument at the end.

@epage
Copy link
Member

epage commented Jul 11, 2022

In #3721, the bug is that we allowed unbound positional arguments at the same level as subcommands; that is unambiguous and we can't really do much about it. This is why I was suggesting we prevent users from even doing this, like here. This is independent of global(true).

@epage
Copy link
Member

epage commented Sep 28, 2022

Closing this in favor of #3721

@epage epage closed this as completed Sep 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Updating dependencies
Projects
None yet
Development

No branches or pull requests

2 participants