Skip to content

Commit

Permalink
Merge pull request #192 from XenoPhex/master
Browse files Browse the repository at this point in the history
Allow passing value starting with - to custom option
  • Loading branch information
jessevdk authored Dec 21, 2018
2 parents a1c83c9 + 7fd817b commit c0795c8
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 12 deletions.
9 changes: 9 additions & 0 deletions convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ type Unmarshaler interface {
UnmarshalFlag(value string) error
}

// ValueValidator is the interface implemented by types that can validate a
// flag argument themselves. The provided value is directly passed from the
// command line.
type ValueValidator interface {
// IsValidValue returns an error if the provided string value is valid for
// the flag.
IsValidValue(value string) error
}

func getBase(options multiTag, base int) (int, error) {
sbase := options.Get("base")

Expand Down
34 changes: 34 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,30 @@ func (option *Option) isUnmarshaler() Unmarshaler {
return nil
}

func (option *Option) isValueValidator() ValueValidator {
v := option.value

for {
if !v.CanInterface() {
break
}

i := v.Interface()

if u, ok := i.(ValueValidator); ok {
return u
}

if !v.CanAddr() {
break
}

v = v.Addr()
}

return nil
}

func (option *Option) isBool() bool {
tp := option.value.Type()

Expand Down Expand Up @@ -507,3 +531,13 @@ func (option *Option) shortAndLongName() string {

return ret.String()
}

func (option *Option) isValidValue(arg string) error {
if validator := option.isValueValidator(); validator != nil {
return validator.IsValidValue(arg)
}
if argumentIsOption(arg) && !(option.isSignedNumber() && len(arg) > 1 && arg[0] == '-' && arg[1] >= '0' && arg[1] <= '9') {
return fmt.Errorf("expected argument for flag `%s', but got option `%s'", option, arg)
}
return nil
}
4 changes: 2 additions & 2 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,8 +532,8 @@ func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg
} else {
arg = s.pop()

if argumentIsOption(arg) && !(option.isSignedNumber() && len(arg) > 1 && arg[0] == '-' && arg[1] >= '0' && arg[1] <= '9') {
return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got option `%s'", option, arg)
if validationErr := option.isValidValue(arg); validationErr != nil {
return newErrorf(ErrExpectedArgument, validationErr.Error())
} else if p.Options&PassDoubleDash != 0 && arg == "--" {
return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got double dash `--'", option)
}
Expand Down
53 changes: 43 additions & 10 deletions parser_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package flags

import (
"errors"
"fmt"
"os"
"reflect"
Expand Down Expand Up @@ -363,6 +364,22 @@ func TestEnvDefaults(t *testing.T) {
}
}

type CustomFlag struct {
Value string
}

func (c *CustomFlag) UnmarshalFlag(s string) error {
c.Value = s
return nil
}

func (c *CustomFlag) IsValidValue(s string) error {
if !(s == "-1" || s == "-foo") {
return errors.New("invalid flag value")
}
return nil
}

func TestOptionAsArgument(t *testing.T) {
var tests = []struct {
args []string
Expand Down Expand Up @@ -419,30 +436,46 @@ func TestOptionAsArgument(t *testing.T) {
rest: []string{"-", "-"},
},
{
// Accept arguments which start with '-' if the next character is a digit, for number options only
// Accept arguments which start with '-' if the next character is a digit
args: []string{"--int-slice", "-3"},
},
{
// Accept arguments which start with '-' if the next character is a digit, for number options only
// Accept arguments which start with '-' if the next character is a digit
args: []string{"--int16", "-3"},
},
{
// Accept arguments which start with '-' if the next character is a digit, for number options only
// Accept arguments which start with '-' if the next character is a digit
args: []string{"--float32", "-3.2"},
},
{
// Accept arguments which start with '-' if the next character is a digit, for number options only
// Accept arguments which start with '-' if the next character is a digit
args: []string{"--float32ptr", "-3.2"},
},
{
// Accept arguments for values that pass the IsValidValue fuction for value validators
args: []string{"--custom-flag", "-foo"},
},
{
// Accept arguments for values that pass the IsValidValue fuction for value validators
args: []string{"--custom-flag", "-1"},
},
{
// Rejects arguments for values that fail the IsValidValue fuction for value validators
args: []string{"--custom-flag", "-2"},
expectError: true,
errType: ErrExpectedArgument,
errMsg: "invalid flag value",
},
}

var opts struct {
StringSlice []string `long:"string-slice"`
IntSlice []int `long:"int-slice"`
Int16 int16 `long:"int16"`
Float32 float32 `long:"float32"`
Float32Ptr *float32 `long:"float32ptr"`
OtherOption bool `long:"other-option" short:"o"`
StringSlice []string `long:"string-slice"`
IntSlice []int `long:"int-slice"`
Int16 int16 `long:"int16"`
Float32 float32 `long:"float32"`
Float32Ptr *float32 `long:"float32ptr"`
OtherOption bool `long:"other-option" short:"o"`
Custom CustomFlag `long:"custom-flag" short:"c"`
}

for _, test := range tests {
Expand Down

0 comments on commit c0795c8

Please sign in to comment.