Skip to content

Commit c0795c8

Browse files
authored
Merge pull request #192 from XenoPhex/master
Allow passing value starting with - to custom option
2 parents a1c83c9 + 7fd817b commit c0795c8

File tree

4 files changed

+88
-12
lines changed

4 files changed

+88
-12
lines changed

convert.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ type Unmarshaler interface {
2828
UnmarshalFlag(value string) error
2929
}
3030

31+
// ValueValidator is the interface implemented by types that can validate a
32+
// flag argument themselves. The provided value is directly passed from the
33+
// command line.
34+
type ValueValidator interface {
35+
// IsValidValue returns an error if the provided string value is valid for
36+
// the flag.
37+
IsValidValue(value string) error
38+
}
39+
3140
func getBase(options multiTag, base int) (int, error) {
3241
sbase := options.Get("base")
3342

option.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,30 @@ func (option *Option) isUnmarshaler() Unmarshaler {
389389
return nil
390390
}
391391

392+
func (option *Option) isValueValidator() ValueValidator {
393+
v := option.value
394+
395+
for {
396+
if !v.CanInterface() {
397+
break
398+
}
399+
400+
i := v.Interface()
401+
402+
if u, ok := i.(ValueValidator); ok {
403+
return u
404+
}
405+
406+
if !v.CanAddr() {
407+
break
408+
}
409+
410+
v = v.Addr()
411+
}
412+
413+
return nil
414+
}
415+
392416
func (option *Option) isBool() bool {
393417
tp := option.value.Type()
394418

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

508532
return ret.String()
509533
}
534+
535+
func (option *Option) isValidValue(arg string) error {
536+
if validator := option.isValueValidator(); validator != nil {
537+
return validator.IsValidValue(arg)
538+
}
539+
if argumentIsOption(arg) && !(option.isSignedNumber() && len(arg) > 1 && arg[0] == '-' && arg[1] >= '0' && arg[1] <= '9') {
540+
return fmt.Errorf("expected argument for flag `%s', but got option `%s'", option, arg)
541+
}
542+
return nil
543+
}

parser.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,8 @@ func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg
532532
} else {
533533
arg = s.pop()
534534

535-
if argumentIsOption(arg) && !(option.isSignedNumber() && len(arg) > 1 && arg[0] == '-' && arg[1] >= '0' && arg[1] <= '9') {
536-
return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got option `%s'", option, arg)
535+
if validationErr := option.isValidValue(arg); validationErr != nil {
536+
return newErrorf(ErrExpectedArgument, validationErr.Error())
537537
} else if p.Options&PassDoubleDash != 0 && arg == "--" {
538538
return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got double dash `--'", option)
539539
}

parser_test.go

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package flags
22

33
import (
4+
"errors"
45
"fmt"
56
"os"
67
"reflect"
@@ -363,6 +364,22 @@ func TestEnvDefaults(t *testing.T) {
363364
}
364365
}
365366

367+
type CustomFlag struct {
368+
Value string
369+
}
370+
371+
func (c *CustomFlag) UnmarshalFlag(s string) error {
372+
c.Value = s
373+
return nil
374+
}
375+
376+
func (c *CustomFlag) IsValidValue(s string) error {
377+
if !(s == "-1" || s == "-foo") {
378+
return errors.New("invalid flag value")
379+
}
380+
return nil
381+
}
382+
366383
func TestOptionAsArgument(t *testing.T) {
367384
var tests = []struct {
368385
args []string
@@ -419,30 +436,46 @@ func TestOptionAsArgument(t *testing.T) {
419436
rest: []string{"-", "-"},
420437
},
421438
{
422-
// Accept arguments which start with '-' if the next character is a digit, for number options only
439+
// Accept arguments which start with '-' if the next character is a digit
423440
args: []string{"--int-slice", "-3"},
424441
},
425442
{
426-
// Accept arguments which start with '-' if the next character is a digit, for number options only
443+
// Accept arguments which start with '-' if the next character is a digit
427444
args: []string{"--int16", "-3"},
428445
},
429446
{
430-
// Accept arguments which start with '-' if the next character is a digit, for number options only
447+
// Accept arguments which start with '-' if the next character is a digit
431448
args: []string{"--float32", "-3.2"},
432449
},
433450
{
434-
// Accept arguments which start with '-' if the next character is a digit, for number options only
451+
// Accept arguments which start with '-' if the next character is a digit
435452
args: []string{"--float32ptr", "-3.2"},
436453
},
454+
{
455+
// Accept arguments for values that pass the IsValidValue fuction for value validators
456+
args: []string{"--custom-flag", "-foo"},
457+
},
458+
{
459+
// Accept arguments for values that pass the IsValidValue fuction for value validators
460+
args: []string{"--custom-flag", "-1"},
461+
},
462+
{
463+
// Rejects arguments for values that fail the IsValidValue fuction for value validators
464+
args: []string{"--custom-flag", "-2"},
465+
expectError: true,
466+
errType: ErrExpectedArgument,
467+
errMsg: "invalid flag value",
468+
},
437469
}
438470

439471
var opts struct {
440-
StringSlice []string `long:"string-slice"`
441-
IntSlice []int `long:"int-slice"`
442-
Int16 int16 `long:"int16"`
443-
Float32 float32 `long:"float32"`
444-
Float32Ptr *float32 `long:"float32ptr"`
445-
OtherOption bool `long:"other-option" short:"o"`
472+
StringSlice []string `long:"string-slice"`
473+
IntSlice []int `long:"int-slice"`
474+
Int16 int16 `long:"int16"`
475+
Float32 float32 `long:"float32"`
476+
Float32Ptr *float32 `long:"float32ptr"`
477+
OtherOption bool `long:"other-option" short:"o"`
478+
Custom CustomFlag `long:"custom-flag" short:"c"`
446479
}
447480

448481
for _, test := range tests {

0 commit comments

Comments
 (0)