clap icon indicating copy to clipboard operation
clap copied to clipboard

Support multiple arguments writing to the same destination

Open epage opened this issue 2 years ago • 6 comments

Please complete the following tasks

  • [X] I have searched the discussions
  • [X] I have searched the existing issues

Clap Version

3.0.0-rc.0

Describe your use case

Related issues

  • #1820
  • #815

Describe the solution you'd like

In Python's argparse, you can assign the dest (for us, name) for an argument in the Namespace (for us, ArgMatches). This gives a lot of power. One example is #1820 with migrating flags. The thing I used this most frequently for was negations (#815).

The code would look something like

parser.add_argument("--flag", dest="flag", action="store_true")
parser.add_argument("--no-flag", dest="flag", action="store_false")
... some line about setting the default

This makes it easy to define and handle and gives good semantics (last writer wins)

For now, I'm doing stuff like

fn resolve_bool_arg(yes: bool, no: bool) -> Option<bool> {
    match (yes, no) {
        (true, false) => Some(true),
        (false, true) => Some(false),
        (false, false) => None,
        (_, _) => unreachable!("StructOpt should make this impossible"),
    }
}

https://github.com/epage/git-stack/blob/main/src/bin/git-stack/args.rs#L105

and haven't played with overrides_with to see what a last-writer-wins would look like with clap.

Alternatives, if applicable

No response

Additional Context

See #1820 which has several concerns over doing this. I especially expect problems with

  • Interaction with global(true)
  • Validation rules (they have to be aware of and operate on the dest)
  • Derives reading the right dest
  • Derives dealing with multiple "arguments" pointing to the same dest

See #3119 for more use cases

epage avatar Dec 10 '21 21:12 epage

Is there a good workaround for this? I have a lot of options that are negatable (mostly using no- prefixed options), and it would be really nice to avoid having duplicate boolean fields for all of them that I have to check both of.

tmccombs avatar Jun 27 '22 08:06 tmccombs

@tmccombs would a workaround like this work for your use case?

ducaale avatar Jun 27 '22 08:06 ducaale

Maybe. In my case I only want these options for a few of the options, and in some cases I the man option starts with --no- and the negating option doesn't have the prefix (or in other words, the option without the --no prefix is the default).

I'm also not really sure what the best way to do that when using the derive api would be.

tmccombs avatar Jun 28 '22 06:06 tmccombs

@tmccombs if your interest is in negation flags, be sure to watch the more specific #815. In it, I do bring up a more unusual solution using ArgAction so there is room for solutions outside of this issue.

epage avatar Jun 28 '22 12:06 epage

Some other use cases for this:

  • having an option that is a shortcut for passing a specific value to another option. For example -1 as an alias for --max-results=1
  • Allowing an argument to be passed either as a positional argument or an option
  • Options that take the same value, but with different meanings. for example --include and --exclude might feed into a single list of filters, where relative order matters, and the difference is just which variant of an enum is used (or maybe one type is prefixed with some delimiter like "!").

tmccombs avatar Jul 01 '22 07:07 tmccombs

and haven't played with overrides_with to see what a last-writer-wins would look like with clap.

https://docs.rs/bpaf/0.5.6/bpaf/batteries/fn.toggle_flag.html - looks like this in bpaf, values can be the same or different. As long as types align you just compose them as alternatives, consume all and get the last value construct!([a, b, c, d]).many().map(|xs| xs.into_iter().last()).

pacak avatar Sep 04 '22 07:09 pacak