docopt.rs icon indicating copy to clipboard operation
docopt.rs copied to clipboard

More readable struct fields

Open keleshev opened this issue 11 years ago • 14 comments

I don't know Rust, but for the C implementation the strategy was similar—to generate a struct. However, the idea was to either generate struct under one "namespace":

-g       => args.g     # unless --group is a synonym
--group  => args.group
FILE     => args.file
<file>   => args.file
build    => args.build

Or have separate nested structs for options, arguments and commands:

-g       => args.options.g       # unless --group is a synonym
--group  => args.options.group
FILE     => args.positional.file
<file>   => args.positional.file
build    => args.commands.build

I would prefer any of the two better than the current scheme.

keleshev avatar Jul 29 '14 13:07 keleshev

The problem with the first approach is you can end up with collisions, although I suppose an error could be generated in that case.

@alexcrichton What do you think? I don't have any terribly strong opinions. The current approach was just something That Worked. I'm not attached to it.

BurntSushi avatar Jul 29 '14 14:07 BurntSushi

I'd avoid the first for the same reasons, and foo.options.bar does seem a little better than foo.flag_bar (foo.flags.bar?). I also don't have too too strong a preference!

alexcrichton avatar Jul 29 '14 14:07 alexcrichton

I think that a single namespace is still a viable option, because when was the last time anyone had a --file option and a <file> argument?!

keleshev avatar Jul 29 '14 16:07 keleshev

Right, wouldn't collisions represent symbol reuse within a single pattern? That seems inadvisable to begin with. Since the first, singly-namespaced approach better matches what other docopt implementations provide, I'd vote for it over struct nesting.

evnm avatar Jul 29 '14 17:07 evnm

Since the first, singly-namespaced approach better matches what other docopt implementations provide

The reference docopt implementation is namespaced.

If it's truly inadvisable to have a flag and, say, a positional argument with the same name, then perhaps Docopt itself should rule it out. But as of now, it allows it, so I don't really see a reason to arbitrarily disallow it.

BurntSushi avatar Jul 29 '14 17:07 BurntSushi

If it's truly inadvisable to have a flag and, say, a positional argument with the same name, then perhaps Docopt itself should rule it out. But as of now, it allows it, so I don't really see a reason to arbitrarily disallow it.

I would totally disallow it. It just happens that some major languages have fist-class support for json-like dictionaries, so the problem never came up there.

For languages where it makes more sense to generate a custom data type (like struct in Rust and C), I think it's totally fine to raise error in case of a collision (like --file vs <file>).

It's just a matter if we decide on a single namespace, or on multiple nested namespaces (options, arguments, commands).

keleshev avatar Jul 29 '14 17:07 keleshev

Correct me if I'm wrong, but the ref impl generates a dictionary where the only nested data structure is the <args> list. Options and positional arguments are inserted into the dictionary as top-level key value pairs.

Collisions could still occur across separate patterns in the event that e.g. a positional argument's name is the same as a command. I'm of the opinion that avoiding such cases of conflated terms would also remove a potential source of confusion for users.

evnm avatar Jul 29 '14 17:07 evnm

@evnm What you say is true, but that doesn't mean the dictionary isn't namespaced. For example, all flags start with - or --. Likewise, currently, struct fields that are flags start with flag_.

Arguments are always keys of the form <arg> and ARG. A command, is, by definition, everything else. Therefore there is no overlap.

In the reference Docopt, you can have a flag, command and positional argument all have the same name:

Usage: program -a <a> a

And it works fine:

[andrew@Liger docopt] python examples/quick_example.py -a hi a
{'-a': True,
 '<a>': 'hi',
 'a': True}

In other words: namespaced.

(My understanding is that @halst does not like this? Even in the reference implementation? I don't see what native JSON dictionaries have to do with whether you have proper namespaces or not. I had assumed this namespacing was an explicit design decision, so I copied it.)

BurntSushi avatar Jul 29 '14 17:07 BurntSushi

@evnm in the reference implementation, each --option, <argument> and command is inserted as key in a json-like dictionary. The values of those keys could be different:

  • for --options it's boolaen
  • for --options with an argument, it's a string
  • for --options with an argument which could be repeated (thanks to ... operator), it's a list of values
  • for options which take no argument, but could be repeated (-v...), the value is a number (of repetitions)
  • for commands it's same as for options with no argument
  • for <arguments> it is either a single string, or a list of strings if argument could be repeated.

Since in the reference implementation keys are arbitrary strings, the cannot collide.

keleshev avatar Jul 29 '14 17:07 keleshev

@BurntSushi the ref. imp. is using full names (with characters like -<>), not to separate them from each other, but only for readability.

keleshev avatar Jul 29 '14 17:07 keleshev

Gotcha, thank you both. Excuse my ignorance. I'm not very familiar with docopt and came across this project when poking around for Rust CLI libs.

This makes more sense now. Insofar as my less-informed opinion matters, I think the args.{command,options,arguments} approach is a good compromise between adhering to the reference implementation and leveraging Rust structs.

evnm avatar Jul 29 '14 20:07 evnm

@evnm That is the way I'm leaning. Note that structs are a convenience for this library. You can still access values using a hashmap with keys like the reference Docopt implementation: https://github.com/docopt/docopt.rs/blob/master/examples/hashmap.rs

If we really think that having a usage like program -a <a> a is a bad idea, then it should be properly codified in the Docopt spec.

BurntSushi avatar Jul 29 '14 20:07 BurntSushi

Such collision avoidance might be worth posing to the docopt folks via a separate issue on docopt/docopt, but is outside of the scope of #7. As long as the hashmap-based API has parity with the Python lib, then I think docopt.rs should have fairly free reign over the sugary struct-based API.

evnm avatar Jul 29 '14 21:07 evnm

I think docopt.rs should have fairly free reign over the sugary struct-based API.

:+1:

keleshev avatar Jul 30 '14 09:07 keleshev