Add specification of required rest arg range
The `”required”` tag now also accepts a “<min>-<max>” range, which may
be set to “0-0” to not allow any rest arguments.
diff --git a/arg.go b/arg.go
index d160644..8ec6204 100644
--- a/arg.go
+++ b/arg.go
@@ -12,9 +12,12 @@
// A description of the positional argument (used in the help)
Description string
- // Whether a positional argument is required
+ // The minimal number of required positional arguments
Required int
+ // The maximum number of required positional arguments
+ RequiredMaximum int
+
value reflect.Value
tag multiTag
}
diff --git a/arg_test.go b/arg_test.go
index 117e90e..c7c0a61 100644
--- a/arg_test.go
+++ b/arg_test.go
@@ -131,3 +131,33 @@
assertString(t, opts.Positional.Rest[1], "rest2")
assertString(t, opts.Positional.Rest[2], "rest3")
}
+
+func TestPositionalRequiredRestRangeFail(t *testing.T) {
+ var opts = struct {
+ Value bool `short:"v"`
+
+ Positional struct {
+ Rest []string `required:"1-2"`
+ } `positional-args:"yes"`
+ }{}
+
+ p := NewParser(&opts, None)
+ _, err := p.ParseArgs([]string{"rest1", "rest2", "rest3"})
+
+ assertError(t, err, ErrRequired, "the required argument `Rest (at most 2 arguments, but got 3)` was not provided")
+}
+
+func TestPositionalRequiredRestRangeEmptyFail(t *testing.T) {
+ var opts = struct {
+ Value bool `short:"v"`
+
+ Positional struct {
+ Rest []string `required:"0-0"`
+ } `positional-args:"yes"`
+ }{}
+
+ p := NewParser(&opts, None)
+ _, err := p.ParseArgs([]string{"some", "thing"})
+
+ assertError(t, err, ErrRequired, "the required argument `Rest (zero arguments)` was not provided")
+}
diff --git a/command.go b/command.go
index aab34e6..580652c 100644
--- a/command.go
+++ b/command.go
@@ -181,15 +181,28 @@
name = field.Name
}
- var required int
+ required := -1
+ requiredMaximum := -1
sreq := m.Get("required")
if sreq != "" {
required = 1
- if preq, err := strconv.ParseInt(sreq, 10, 32); err == nil {
- required = int(preq)
+ rng := strings.SplitN(sreq, "-", 2)
+
+ if len(rng) > 1 {
+ if preq, err := strconv.ParseInt(rng[0], 10, 32); err == nil {
+ required = int(preq)
+ }
+
+ if preq, err := strconv.ParseInt(rng[1], 10, 32); err == nil {
+ requiredMaximum = int(preq)
+ }
+ } else {
+ if preq, err := strconv.ParseInt(sreq, 10, 32); err == nil {
+ required = int(preq)
+ }
}
}
@@ -197,6 +210,7 @@
Name: name,
Description: m.Get("description"),
Required: required,
+ RequiredMaximum: requiredMaximum,
value: realval.Field(i),
tag: m,
diff --git a/parser.go b/parser.go
index eee6e17..137bc07 100644
--- a/parser.go
+++ b/parser.go
@@ -377,7 +377,7 @@
var reqnames []string
for _, arg := range p.positional {
- argRequired := (!arg.isRemaining() && p.command.ArgsRequired) || arg.Required != 0
+ argRequired := (!arg.isRemaining() && p.command.ArgsRequired) || arg.Required != -1 || arg.RequiredMaximum != -1
if !argRequired {
continue
@@ -394,6 +394,20 @@
}
reqnames = append(reqnames, "`"+arg.Name+" (at least "+fmt.Sprintf("%d", arg.Required)+" "+arguments+")`")
+ } else if arg.RequiredMaximum != -1 && arg.value.Len() > arg.RequiredMaximum {
+ if arg.RequiredMaximum == 0 {
+ reqnames = append(reqnames, "`"+arg.Name+" (zero arguments)`")
+ } else {
+ var arguments string
+
+ if arg.RequiredMaximum > 1 {
+ arguments = "arguments, but got " + fmt.Sprintf("%d", arg.value.Len())
+ } else {
+ arguments = "argument"
+ }
+
+ reqnames = append(reqnames, "`"+arg.Name+" (at most "+fmt.Sprintf("%d", arg.RequiredMaximum)+" "+arguments+")`")
+ }
}
} else {
reqnames = append(reqnames, "`"+arg.Name+"`")