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

Distinguish between multiple sets of values for a multiple, min_values arg #1026

Closed
soumya92 opened this issue Aug 14, 2017 · 12 comments · Fixed by #2297
Closed

Distinguish between multiple sets of values for a multiple, min_values arg #1026

soumya92 opened this issue Aug 14, 2017 · 12 comments · Fixed by #2297
Milestone

Comments

@soumya92
Copy link

soumya92 commented Aug 14, 2017

Rust Version

rustc 1.19.0 (0ade33941 2017-07-17)

Affected Version of clap

clap 2.25.0

Expected Behavior Summary

It would be nice to be able to distinguish between -a A B C D E and -a A B -a C D E, where -a is an arg with min_values(2) and multiple(true).

From clap::Arg::min_values

NOTE: This does not implicitly set Arg::multiple(true). This is because -o val -o val is multiple occurrences but a single value and -o val1 val2 is a single occurence with multiple values.

This seems to imply that such a distinction is already possible. But the test option_min_exact demonstrates exactly the opposite behaviour.

A toy example of where this might be useful is a backup program. It might allow users to backup files to multiple "targets", for example, with common settings specified once. An invocation might look like:

backup -s server -u user -target target1 file1 file2 file3 -target target2 file4 file5 file6 file7 -target target3 file8

Currently there seems to be no way to determine that target1, target2, and target3 appeared immediately after -target. All we know is that there are three targets (occurrences_of("target")), but values_of("target") will just iterate over target1 file1 file2 file3 target2 file4 file5 file6 file7 target3 file8

@kbknapp
Copy link
Member

kbknapp commented Oct 4, 2017

hmm, perhaps adding a new setting that allows grouping values? So when iterating these values it'd be:

["arget1 file1 file2 file3", "target2 file4 file5 file6 file7", "target3 file8"]

@soumya92
Copy link
Author

soumya92 commented Oct 4, 2017

I like the idea of adding an option to enable grouping.

[["target1", "file1", "file2", "file3"], ["target2", "file4", "file5", "file6", "file7"], ["target3", "file8"]] would be perfect, just because that would require less processing on the app side. Otherwise the app authors would have to shell split the strings to get individual arguments.

@kbknapp
Copy link
Member

kbknapp commented Oct 4, 2017

I don't think we could do a Vec<Vec<&str>> in a non-breaking way just do to the signature of ArgMatches::values_of, however in user code one could do:

// values are: ["arget1 file1 file2 file3", "target2 file4 file5 file6 file7", "target3 file8"]
let vals: Vec<Vec<_>> = m.values_of("arg").map(|v| v.split(" ").collect()).collect();
// Now: [["arget1", "file1",  "file2", file3"], ["target2", "file4", "file5", "file6", "file7"], ["target3", "file8"]]

Granted, I just typed that off the top of my head, so some tweaks may be required to make it compile

@soumya92
Copy link
Author

soumya92 commented Oct 4, 2017

I assumed you were talking about adding ArgMatches::grouped_values_of or something similar.
Splitting on " " is not ideal because it will then prevent user code from being able to handle arguments that themselves contain spaces, since that information will be lost. Or it will require some weird form of escaping to distinguish between spaces used to join arguments and spaces already present in the arguments.

@kbknapp
Copy link
Member

kbknapp commented Oct 4, 2017

Splitting on " " is just an example, and would be determined by the user. My point being, I don't think adding a ArgMatches::grouped_values_of would be easy because of the internal structure of matched values.

This isn't to say it's not possible, just that it'd be kind of a hacky solution (from the implementation point of view).

Personally I'd be more inclined to change the CLI slightly instead of using a single option for two purposes (targets and data).

I'll leave this open on the issue tracker for reference once I get a few more of the issues knocked out. This might also be easier to implement on the 3.x branch where the code is cleaner.

@tmccombs
Copy link
Contributor

I have a use case where I want an argument that can take either two or three arguments. Specifically, it must have a timeout (in seconds) and a command to run on the timeout and possibly another command to run when another event happens after the timeout. (and this can be provided multiple times)

I'm not necessarily opposed to using a different scheme for the options, but I'm not sure of a good way to do that without making the interface awkward. Solutions I have considered:

  1. Using a delimeter: since the arguments are shell commands, the arguments could contain basically any character, besides having to split the argument myself, I would have to implement some kind of escape mechanism.
  2. Use separate options for the first and second commands, but the option for the second command must appear immediately after the first option. However, it isn't clear to me how to implement this constraint with clap. I would probably need to do something with the indices of the arguments, which seems fairly complicated and brittle.
  3. Use a separate option for the second command that also requires a timeout that means "run this command if we got an event after the timeout triggered". This would be straightforward to implement, but IMO a confusing interface for the user.

foo-dogsquared added a commit to foo-dogsquared/lanoma that referenced this issue Nov 30, 2019
Since clap-rs doesn't have a way to group arguments (see clap-rs Issue #1026 at clap-rs/clap#1026), a revision of the interface has to be done. The most efficient way I can think of is introducing the units (i.e., subject, notes, and their IDs) as its own subcommand.

I think this is a good compromise overall. I don't usually input with multiple subjects, notes, and IDs at the same time anyway. It also avoids some chaotic usage with it. I'd say it's an improvement than a compromise.
@JMLX42
Copy link

JMLX42 commented Feb 25, 2020

I have the following use case: I need to support "localized" string values on the command line.

A LocalizedString can either be:

  • a single not-localized String value
  • a set of (language tag, localized string) values in a Map
pub enum LocalizedString {
    String(String),
    Map(Map<String, String>),
}

so the following are possible:

$ cli --label my-not-localized-label
$ cli --label 'fr_FR:défaut' 'en_US:defect'

It works fine. The problem arise when I want to set multiple: true to describe a collection of LocalizedString. This does not work as expected:

$ cli --option 'fr_FR:mon option 1' 'en_US:my option 1' \
    --option 'fr_FR:mon option 2' 'en_US:my option 2'

The goal would be to end up with this:

[
  {
    "fr_FR": "mon option 1",
    "en_US": "my option 1",
  },
  {
    "fr_FR": "mon option 2",
    "en_US": "my option 2",
  }
]

Instead, the option match using clap will be a single Vec containing:

[
  "fr_FR:mon option 1",
  "en_US:my option 1",
  "fr_FR:mon option 2",
  "en_US:my option 2"
]

And IMHO it's then pretty much impossible to handle validation and proper handling.

I would expect to be able to declare that multiple occurrences of a specific option would be differentiable after the match.

@sharkdp
Copy link

sharkdp commented Apr 13, 2020

In fd, we also have a use case for this: We have a --exec/-x option that takes a "command" in the form of multiple values, such that users do not have to add quotes and we do not have to shell-split it:

fd … --exec sed -i -e 's/foo/bar/g'

            \_____________________/

          four values passed to `--exec`

We would now like to allow users to execute several commands:

fd … --exec echo "Replacing foo with bar in {}" \
     --exec sed -i -e 's/foo/bar/g'

However, with the limitation described in this ticket, I don't see a way to split these six values into two groups (of two and four values).

@pksunkara
Copy link
Member

We will be definitely fixing this before 3.0 is out. This is one of the highest priority issue IMO.

@JMLX42
Copy link

JMLX42 commented Jun 9, 2020

@pksunkara any update on this? Thanks 3000!

@pksunkara
Copy link
Member

Nothing yet. Still working on some other stuff first.

@CreepySkeleton
Copy link
Contributor

I'm working on it, but I'm as well working on other stuff concurrently (clap book, sane global args representation, sane default values, some groups bugs) so don't expect it to be dealt with soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants