Undocumented arguments can be a burden and forces developers to generate write their own ArgsUsage
Add a new field to App and Command called Args of type []ArgSpec which is an interface like Flag. There would many structs that implement this interface, like those for parsing flags.
type ArgSpec interface {
// Name returns the name that this arg will be accessed by
Name() string
// Required returns true if this arg is required
Required() bool
// Priority means the order (highest to lowest) which are parsed after the required ones
Priority() uint
// Min returns the minimum args to be parsed
Min() uint
// Max returns the maximum args to be parsed, if 0 then any number can be parsed
Max() uint
}
Along with this there are several other rules that will result in a panic (always no matter what the provided args to be parsed are)
Max() == 0 arg per fieldArgs never mean anything for sub-comands.
Args will be accessed in the same manner as Flags currently, they are parsed after flags so will override them (that is how one can have a EnvVar be the default to an arg)
If there are any []ArgSpec then all provided args must be used, otherwise a error is printed as if a required flag was not supplied.
This also means that the Arg(n) and Args() won't return anything after parsing args.
I think this idea will need a few iterations - but it's off to a good start! I think we should really consider something like this ๐
Hey, I think this is a good idea, a few thoughts from my side:
positional argumentsapp.PositionalArgs = []cli.PositionalArg{}positional argumentsls would not be able to be written using this feature?*Command.<Type>() methods.Having done some more preliminary investigation by doing a mock implementation I have found the following to be necessary:
1.0 2.0 3.0 would be the three elements of a float64slice I'm marking this as help wanted for someone to work on the design, since it'll be a few steps before this is ready for implementation
Nice proposal!
Just some drive-by-bikeshedding from a random user:
Wouldn't naming the interface Args instead of ArgSpec fit better with the existing Flag interface? Maybe even Arguments or ArgumentGroup, since this wouldn't be typed often enough to justify sacrificing readability for "writeability".
Also, wouldn't it be possible to implement Required: true via Min: 1? This would leave us with a leaner interface:
type ArgumentGroup interface {
Name() string
Min() uint
Max() uint
}
And as a bonus we wouldn't have to deal with the corner-case of Required: true + Min: 0.
Implementing something like mv or cp would be a matter of:
app := &cli.App{
Name: "cp",
Arguments: []cli.ArgumentGroup{
cli.StringArguments{
Name: "paths",
Min: 2,
},
},
}
(assuming cli.StringArgument could default to Max:0)
But I wonder a bit what the main use-case for multiple argument groups would be.
The relatively common cmd [foo [bar]] idiom could be implemented using multiple groups with something like:
app := &cli.App{
Name: "cmd",
Arguments: []cli.ArgumentGroup{
cli.OptionalStringArgument{
Name: "foo",
},
cli.OptionalStringArgument{
Name: "bar",
},
},
}
Where cli.OptionalArgument is a cli.ArgumentGroup with Min() โ 0 and Max() โ 1 (assuming such a helper would be acceptable).
But OTOH, the same could be accomplished with a single argument group and simple slice logic. I'm not sure the increase in complexity is worth it, just to get a more comfortable access pattern for the individual arguments. :thinking:
We could also allow for some sort of regular expression based system where the Argument has a "is valid" method
Whatever we do for this feature request (eg. https://github.com/urfave/cli/issues/1074), it should also most likely solve this request too => https://github.com/urfave/cli/issues/1069
Arguments have at least the following:
func PlacementName() rune: this value is the value that must be used in the argument placement regex. It is a singular rune because that makes the regex both faster and not ambiguous. If any of these rune's collide then a panic will happen at run-time.func IsValid(arg string) bool: this function is used to build up the placement string for the regex matching. This must be required to be stateless and deterministic. It should also be cheap to run, since it will be run multiple times.func DocName() string: this function is used in the generation of documentation.Arguments will be provided in some manner. The ordering doesn't matter at all as all arguments will be "placed" (like flags but I think it should be required).
With the arguments, a "placement string" must also be supplied. The syntax is a subset of full regular expressions.
?, *, +, {n, m}, etc....| not [...] nor [^...].^ or $ (those are assumed always)PlacementName() methods on the arguments for a specific command.The following is then done in order:
IsValid() method and the "placement string". Creating a string that looks like a concatenation of possible values of that argument, for example if arg[1] could either be A or B the resulting substring would be [AB]. If arg[2] could either be A or C or H then the resulting substring would be [ACH].A+ it would be transformed into ((?:\[[^\]]*(?:A)[^\]*]*\])+). This way each substring can be match for a specific type. This is also why the placement name must be single characters, that way this regex is unambiguous. This is called the "matching string".(A|B)+: ((?:\[[^\]]*(?:A|B)[^\]*]*\])+). If there is some ambiguity (for example an argument gets assigned the pps of [AB] then the precedence is left->right.A(|B)* (where A and B are any character (and the repeats are different) once a match has been found the ordering from left to right is used to choose which type the match really refers too.AB{1, 3}AA: Int32B: string31 h 2 "hello world" 789[AB][B][AB][B][AB]^((?:\[[^\]]*(?:A)[^\]*]*\]))((?:\[[^\]]*(?:B)[^\]*]*\]){1,3})((?:\[[^\]]*(?:A)[^\]*]*\]))$[AB][A][BC][B][AB][AB][B][AB][B][AB][...]) are checked to decide which type to use. In this example, we find it to be: A, B, B, B, A.This issue or PR has been automatically marked as stale because it has not had recent activity. Please add a comment bumping this if you're still interested in it's resolution! Thanks for your help, please let us know if you need anything else.
I'm still super interested in seeing this happen ^^
This issue or PR has been bumped and is no longer marked as stale! Feel free to bump it again in the future, if it's still relevant.
I would really like to see this too
This issue or PR has been automatically marked as stale because it has not had recent activity. Please add a comment bumping this if you're still interested in it's resolution! Thanks for your help, please let us know if you need anything else.
Closing this as it has become stale.
๐
This issue or PR has been bumped and is no longer marked as stale! Feel free to bump it again in the future, if it's still relevant.
As far as I remember, we would love to see this but it needs more design work ๐๐ผ