diff --git a/parse.go b/parse.go index a82e377..60f35ee 100644 --- a/parse.go +++ b/parse.go @@ -174,6 +174,8 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) { } switch { + case strings.HasPrefix(key, "---"): + errs = append(errs, fmt.Sprintf("%s.%s: too many hyphens", t.Name(), field.Name)) case strings.HasPrefix(key, "--"): spec.long = key[2:] case strings.HasPrefix(key, "-"): @@ -285,7 +287,7 @@ func process(specs []*spec, args []string) error { continue } - if !strings.HasPrefix(arg, "-") || allpositional { + if !isFlag(arg) || allpositional { positionals = append(positionals, arg) continue } @@ -309,7 +311,7 @@ func process(specs []*spec, args []string) error { if spec.multiple { var values []string if value == "" { - for i+1 < len(args) && !strings.HasPrefix(args[i+1], "-") { + for i+1 < len(args) && !isFlag(args[i+1]) { values = append(values, args[i+1]) i++ } @@ -331,7 +333,7 @@ func process(specs []*spec, args []string) error { // if we have something like "--foo" then the value is the next argument if value == "" { - if i+1 == len(args) || strings.HasPrefix(args[i+1], "-") { + if i+1 == len(args) || isFlag(args[i+1]) { return fmt.Errorf("missing value for %s", arg) } value = args[i+1] @@ -370,6 +372,11 @@ func process(specs []*spec, args []string) error { return nil } +// isFlag returns true if a token is a flag such as "-v" or "--user" but not "-" or "--" +func isFlag(s string) bool { + return strings.HasPrefix(s, "-") && strings.TrimLeft(s, "-") != "" +} + // validate an argument spec after arguments have been parse func validate(spec []*spec) error { for _, arg := range spec { diff --git a/parse_test.go b/parse_test.go index 5e88700..91e17bb 100644 --- a/parse_test.go +++ b/parse_test.go @@ -693,3 +693,49 @@ func TestEmptyArgs(t *testing.T) { // put the original arguments back os.Args = origArgs } + +func TestTooManyHyphens(t *testing.T) { + var args struct { + TooManyHyphens string `arg:"---x"` + } + err := parse("--foo -", &args) + assert.Error(t, err) +} + +func TestHyphenAsOption(t *testing.T) { + var args struct { + Foo string + } + err := parse("--foo -", &args) + require.NoError(t, err) + assert.Equal(t, "-", args.Foo) +} + +func TestHyphenAsPositional(t *testing.T) { + var args struct { + Foo string `arg:"positional"` + } + err := parse("-", &args) + require.NoError(t, err) + assert.Equal(t, "-", args.Foo) +} + +func TestHyphenInMultiOption(t *testing.T) { + var args struct { + Foo []string + Bar int + } + err := parse("--foo --- x - y --bar 3", &args) + require.NoError(t, err) + assert.Equal(t, []string{"---", "x", "-", "y"}, args.Foo) + assert.Equal(t, 3, args.Bar) +} + +func TestHyphenInMultiPositional(t *testing.T) { + var args struct { + Foo []string `arg:"positional"` + } + err := parse("--- x - y", &args) + require.NoError(t, err) + assert.Equal(t, []string{"---", "x", "-", "y"}, args.Foo) +}