Skip to content

Commit 9bc3bf9

Browse files
committed
chore: optionally allow parsing of hyphen-prefixied flag parameters
This allows for eg. `foo --number -10`, `foo --flag -bar`. Fixes #478, #315.
1 parent 8e03dbe commit 9bc3bf9

File tree

5 files changed

+69
-22
lines changed

5 files changed

+69
-22
lines changed

context.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ type Context struct {
9999
// This just constructs a new trace. To fully apply the trace you must call Reset(), Resolve(),
100100
// Validate() and Apply().
101101
func Trace(k *Kong, args []string) (*Context, error) {
102-
s := Scan(args...)
102+
s := Scan(args...).AllowHyphenPrefixedParameters(k.allowHyphenated)
103103
c := &Context{
104104
Kong: k,
105105
Args: args,

kong.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,17 @@ type Kong struct {
5656
registry *Registry
5757
ignoreFields []*regexp.Regexp
5858

59-
noDefaultHelp bool
60-
usageOnError usageOnError
61-
help HelpPrinter
62-
shortHelp HelpPrinter
63-
helpFormatter HelpValueFormatter
64-
helpOptions HelpOptions
65-
helpFlag *Flag
66-
groups []Group
67-
vars Vars
68-
flagNamer func(string) string
59+
noDefaultHelp bool
60+
allowHyphenated bool
61+
usageOnError usageOnError
62+
help HelpPrinter
63+
shortHelp HelpPrinter
64+
helpFormatter HelpValueFormatter
65+
helpOptions HelpOptions
66+
helpFlag *Flag
67+
groups []Group
68+
vars Vars
69+
flagNamer func(string) string
6970

7071
// Set temporarily by Options. These are applied after build().
7172
postBuildOptions []Option

kong_test.go

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,15 +1043,6 @@ func TestParentBindings(t *testing.T) {
10431043
assert.Equal(t, "foo", cli.Command.value)
10441044
}
10451045

1046-
func TestNumericParamErrors(t *testing.T) {
1047-
var cli struct {
1048-
Name string
1049-
}
1050-
parser := mustNew(t, &cli)
1051-
_, err := parser.Parse([]string{"--name", "-10"})
1052-
assert.EqualError(t, err, `--name: expected string value but got "-10" (short flag); perhaps try --name="-10"?`)
1053-
}
1054-
10551046
func TestDefaultValueIsHyphen(t *testing.T) {
10561047
var cli struct {
10571048
Flag string `default:"-"`
@@ -2677,3 +2668,39 @@ func TestProviderWithoutError(t *testing.T) {
26772668
err = kctx.Run()
26782669
assert.NoError(t, err)
26792670
}
2671+
2672+
func TestParseHyphenParameter(t *testing.T) {
2673+
type shortFlag struct {
2674+
Flag string `short:"f"`
2675+
Other string `short:"o"`
2676+
Numeric int `short:"n"`
2677+
}
2678+
2679+
t.Run("ShortFlag", func(t *testing.T) {
2680+
actual := &shortFlag{}
2681+
_, err := mustNew(t, actual, kong.WithHyphenPrefixedParameters(true)).Parse([]string{"-f", "-foo"})
2682+
assert.NoError(t, err)
2683+
assert.Equal(t, &shortFlag{Flag: "-foo"}, actual)
2684+
})
2685+
2686+
t.Run("LongFlag", func(t *testing.T) {
2687+
actual := &shortFlag{}
2688+
_, err := mustNew(t, actual, kong.WithHyphenPrefixedParameters(true)).Parse([]string{"--flag", "-foo"})
2689+
assert.NoError(t, err)
2690+
assert.Equal(t, &shortFlag{Flag: "-foo"}, actual)
2691+
})
2692+
2693+
t.Run("ParamMatchesFlag", func(t *testing.T) {
2694+
actual := &shortFlag{}
2695+
_, err := mustNew(t, actual, kong.WithHyphenPrefixedParameters(true)).Parse([]string{"--flag", "-oo"})
2696+
assert.NoError(t, err)
2697+
assert.Equal(t, &shortFlag{Flag: "-oo"}, actual)
2698+
})
2699+
2700+
t.Run("NegativeNumber", func(t *testing.T) {
2701+
actual := &shortFlag{}
2702+
_, err := mustNew(t, actual, kong.WithHyphenPrefixedParameters(true)).Parse([]string{"--numeric", "-10"})
2703+
assert.NoError(t, err)
2704+
assert.Equal(t, &shortFlag{Numeric: -10}, actual)
2705+
})
2706+
}

options.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ func Exit(exit func(int)) Option {
5555
})
5656
}
5757

58+
// WithHyphenPrefixedParameters enables or disables hyphen-prefixed parameters.
59+
//
60+
// These are disabled by default.
61+
func WithHyphenPrefixedParameters(enable bool) Option {
62+
return OptionFunc(func(k *Kong) error {
63+
k.allowHyphenated = enable
64+
return nil
65+
})
66+
}
67+
5868
type embedded struct {
5969
strct any
6070
tags []string

scanner.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ func (t Token) IsValue() bool {
111111
//
112112
// [{FlagToken, "foo"}, {FlagValueToken, "bar"}]
113113
type Scanner struct {
114-
args []Token
114+
allowHyphenated bool
115+
args []Token
115116
}
116117

117118
// ScanAsType creates a new Scanner from args with the given type.
@@ -133,6 +134,14 @@ func ScanFromTokens(tokens ...Token) *Scanner {
133134
return &Scanner{args: tokens}
134135
}
135136

137+
// AllowHyphenPrefixedParameters enables or disables hyphen-prefixed flag parameters on this Scanner.
138+
//
139+
// Disabled by default.
140+
func (s *Scanner) AllowHyphenPrefixedParameters(enable bool) *Scanner {
141+
s.allowHyphenated = enable
142+
return s
143+
}
144+
136145
// Len returns the number of input arguments.
137146
func (s *Scanner) Len() int {
138147
return len(s.args)
@@ -162,7 +171,7 @@ func (e *expectedError) Error() string {
162171
// "context" is used to assist the user if the value can not be popped, eg. "expected <context> value but got <type>"
163172
func (s *Scanner) PopValue(context string) (Token, error) {
164173
t := s.Pop()
165-
if !t.IsValue() {
174+
if !s.allowHyphenated && !t.IsValue() {
166175
return t, &expectedError{context, t}
167176
}
168177
return t, nil

0 commit comments

Comments
 (0)