Add required minimum rest arguments constraint
You can now specify `required:"$n"` for rest arguments
diff --git a/arg.go b/arg.go
index fd8db9c..d160644 100644
--- a/arg.go
+++ b/arg.go
@@ -12,6 +12,9 @@
// A description of the positional argument (used in the help)
Description string
+ // Whether a positional argument is required
+ Required int
+
value reflect.Value
tag multiTag
}
diff --git a/arg_test.go b/arg_test.go
index faea280..117e90e 100644
--- a/arg_test.go
+++ b/arg_test.go
@@ -51,3 +51,83 @@
assertError(t, err, ErrRequired, "the required argument `Filename` was not provided")
}
+
+func TestPositionalRequiredRest1Fail(t *testing.T) {
+ var opts = struct {
+ Value bool `short:"v"`
+
+ Positional struct {
+ Rest []string `required:"yes"`
+ } `positional-args:"yes"`
+ }{}
+
+ p := NewParser(&opts, None)
+ _, err := p.ParseArgs([]string{})
+
+ assertError(t, err, ErrRequired, "the required argument `Rest (at least 1 argument)` was not provided")
+}
+
+func TestPositionalRequiredRest1Pass(t *testing.T) {
+ var opts = struct {
+ Value bool `short:"v"`
+
+ Positional struct {
+ Rest []string `required:"yes"`
+ } `positional-args:"yes"`
+ }{}
+
+ p := NewParser(&opts, None)
+ _, err := p.ParseArgs([]string{"rest1"})
+
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ return
+ }
+
+ if len(opts.Positional.Rest) != 1 {
+ t.Fatalf("Expected 1 positional rest argument")
+ }
+
+ assertString(t, opts.Positional.Rest[0], "rest1")
+}
+
+func TestPositionalRequiredRest2Fail(t *testing.T) {
+ var opts = struct {
+ Value bool `short:"v"`
+
+ Positional struct {
+ Rest []string `required:"2"`
+ } `positional-args:"yes"`
+ }{}
+
+ p := NewParser(&opts, None)
+ _, err := p.ParseArgs([]string{"rest1"})
+
+ assertError(t, err, ErrRequired, "the required argument `Rest (at least 2 arguments, but got only 1)` was not provided")
+}
+
+func TestPositionalRequiredRest2Pass(t *testing.T) {
+ var opts = struct {
+ Value bool `short:"v"`
+
+ Positional struct {
+ Rest []string `required:"2"`
+ } `positional-args:"yes"`
+ }{}
+
+ p := NewParser(&opts, None)
+ _, err := p.ParseArgs([]string{"rest1", "rest2", "rest3"})
+
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ return
+ }
+
+ if len(opts.Positional.Rest) != 3 {
+ t.Fatalf("Expected 3 positional rest argument")
+ }
+
+ assertString(t, opts.Positional.Rest[0], "rest1")
+ assertString(t, opts.Positional.Rest[1], "rest2")
+ assertString(t, opts.Positional.Rest[2], "rest3")
+}
diff --git a/command.go b/command.go
index 9184aec..5e63ecb 100644
--- a/command.go
+++ b/command.go
@@ -3,6 +3,7 @@
import (
"reflect"
"sort"
+ "strconv"
"strings"
"unsafe"
)
@@ -154,9 +155,22 @@
name = field.Name
}
+ var required int
+
+ sreq := m.Get("required")
+
+ if sreq != "" {
+ required = 1
+
+ if preq, err := strconv.ParseInt(sreq, 10, 32); err == nil {
+ required = int(preq)
+ }
+ }
+
arg := &Arg{
Name: name,
Description: m.Get("description"),
+ Required: required,
value: realval.Field(i),
tag: m,
diff --git a/parser.go b/parser.go
index f2de72c..f9e07ee 100644
--- a/parser.go
+++ b/parser.go
@@ -357,15 +357,31 @@
}
if len(required) == 0 {
- if len(p.positional) > 0 && p.command.ArgsRequired {
+ if len(p.positional) > 0 {
var reqnames []string
for _, arg := range p.positional {
- if arg.isRemaining() {
- break
+ argRequired := (!arg.isRemaining() && p.command.ArgsRequired) || arg.Required != 0
+
+ if !argRequired {
+ continue
}
- reqnames = append(reqnames, "`"+arg.Name+"`")
+ if arg.isRemaining() {
+ if arg.value.Len() < arg.Required {
+ var arguments string
+
+ if arg.Required > 1 {
+ arguments = "arguments, but got only " + fmt.Sprintf("%d", arg.value.Len())
+ } else {
+ arguments = "argument"
+ }
+
+ reqnames = append(reqnames, "`"+arg.Name+" (at least "+fmt.Sprintf("%d", arg.Required)+" "+arguments+")`")
+ }
+ } else {
+ reqnames = append(reqnames, "`"+arg.Name+"`")
+ }
}
if len(reqnames) == 0 {