Skip to content

Commit

Permalink
Add specification of required rest arg range
Browse files Browse the repository at this point in the history
The `”required”` tag now also accepts a “<min>-<max>” range, which may
be set to “0-0” to not allow any rest arguments.
  • Loading branch information
jessevdk committed Aug 25, 2016
1 parent 8692b97 commit 1fb2491
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 5 deletions.
5 changes: 4 additions & 1 deletion arg.go
Expand Up @@ -12,9 +12,12 @@ type Arg struct {
// 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
}
Expand Down
30 changes: 30 additions & 0 deletions arg_test.go
Expand Up @@ -131,3 +131,33 @@ func TestPositionalRequiredRest2Pass(t *testing.T) {
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")
}
20 changes: 17 additions & 3 deletions command.go
Expand Up @@ -181,22 +181,36 @@ func (c *Command) scanSubcommandHandler(parentg *Group) scanHandler {
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)
}
}
}

arg := &Arg{
Name: name,
Description: m.Get("description"),
Required: required,
RequiredMaximum: requiredMaximum,

value: realval.Field(i),
tag: m,
Expand Down
16 changes: 15 additions & 1 deletion parser.go
Expand Up @@ -377,7 +377,7 @@ func (p *parseState) checkRequired(parser *Parser) error {
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
Expand All @@ -394,6 +394,20 @@ func (p *parseState) checkRequired(parser *Parser) error {
}

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+"`")
Expand Down

0 comments on commit 1fb2491

Please sign in to comment.