fix: values that look like flags would not be parsed correctly
Specifically, when parsing into slices or maps. Fixes #290
This commit is contained in:
@@ -262,8 +262,7 @@ func (r *Registry) RegisterDefaults() *Registry {
|
|||||||
RegisterKind(reflect.Float32, floatDecoder(32)).
|
RegisterKind(reflect.Float32, floatDecoder(32)).
|
||||||
RegisterKind(reflect.Float64, floatDecoder(64)).
|
RegisterKind(reflect.Float64, floatDecoder(64)).
|
||||||
RegisterKind(reflect.String, MapperFunc(func(ctx *DecodeContext, target reflect.Value) error {
|
RegisterKind(reflect.String, MapperFunc(func(ctx *DecodeContext, target reflect.Value) error {
|
||||||
err := ctx.Scan.PopValueInto("string", target.Addr().Interface())
|
return ctx.Scan.PopValueInto("string", target.Addr().Interface())
|
||||||
return err
|
|
||||||
})).
|
})).
|
||||||
RegisterKind(reflect.Bool, boolMapper{}).
|
RegisterKind(reflect.Bool, boolMapper{}).
|
||||||
RegisterKind(reflect.Slice, sliceDecoder(r)).
|
RegisterKind(reflect.Slice, sliceDecoder(r)).
|
||||||
@@ -450,7 +449,7 @@ func mapDecoder(r *Registry) MapperFunc {
|
|||||||
}
|
}
|
||||||
switch v := t.Value.(type) {
|
switch v := t.Value.(type) {
|
||||||
case string:
|
case string:
|
||||||
childScanner = Scan(SplitEscaped(v, sep)...)
|
childScanner = ScanAsType(t.Type, SplitEscaped(v, sep)...)
|
||||||
|
|
||||||
case []map[string]interface{}:
|
case []map[string]interface{}:
|
||||||
for _, m := range v {
|
for _, m := range v {
|
||||||
@@ -492,14 +491,14 @@ func mapDecoder(r *Registry) MapperFunc {
|
|||||||
keyTypeName, valueTypeName = parts[0], parts[1]
|
keyTypeName, valueTypeName = parts[0], parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
keyScanner := Scan(key)
|
keyScanner := ScanAsType(FlagValueToken, key)
|
||||||
keyDecoder := r.ForNamedType(keyTypeName, el.Key())
|
keyDecoder := r.ForNamedType(keyTypeName, el.Key())
|
||||||
keyValue := reflect.New(el.Key()).Elem()
|
keyValue := reflect.New(el.Key()).Elem()
|
||||||
if err := keyDecoder.Decode(ctx.WithScanner(keyScanner), keyValue); err != nil {
|
if err := keyDecoder.Decode(ctx.WithScanner(keyScanner), keyValue); err != nil {
|
||||||
return fmt.Errorf("invalid map key %q", key)
|
return fmt.Errorf("invalid map key %q", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
valueScanner := Scan(value)
|
valueScanner := ScanAsType(FlagValueToken, value)
|
||||||
valueDecoder := r.ForNamedType(valueTypeName, el.Elem())
|
valueDecoder := r.ForNamedType(valueTypeName, el.Elem())
|
||||||
valueValue := reflect.New(el.Elem()).Elem()
|
valueValue := reflect.New(el.Elem()).Elem()
|
||||||
if err := valueDecoder.Decode(ctx.WithScanner(valueScanner), valueValue); err != nil {
|
if err := valueDecoder.Decode(ctx.WithScanner(valueScanner), valueValue); err != nil {
|
||||||
@@ -525,7 +524,7 @@ func sliceDecoder(r *Registry) MapperFunc {
|
|||||||
}
|
}
|
||||||
switch v := t.Value.(type) {
|
switch v := t.Value.(type) {
|
||||||
case string:
|
case string:
|
||||||
childScanner = Scan(SplitEscaped(v, sep)...)
|
childScanner = ScanAsType(t.Type, SplitEscaped(v, sep)...)
|
||||||
|
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
return jsonTranscode(v, target.Addr().Interface())
|
return jsonTranscode(v, target.Addr().Interface())
|
||||||
|
|||||||
@@ -495,3 +495,21 @@ func (t testMapperVarsContributor) Decode(ctx *kong.DecodeContext, target reflec
|
|||||||
target.SetString("hi")
|
target.SetString("hi")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValuesThatLookLikeFlags(t *testing.T) {
|
||||||
|
var cli struct {
|
||||||
|
Slice []string
|
||||||
|
Map map[string]string
|
||||||
|
}
|
||||||
|
k := mustNew(t, &cli)
|
||||||
|
_, err := k.Parse([]string{"--slice", "-foo"})
|
||||||
|
require.Error(t, err)
|
||||||
|
_, err = k.Parse([]string{"--map", "-foo=-bar"})
|
||||||
|
require.Error(t, err)
|
||||||
|
_, err = k.Parse([]string{"--slice=-foo", "--slice=-bar"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []string{"-foo", "-bar"}, cli.Slice)
|
||||||
|
_, err = k.Parse([]string{"--map=-foo=-bar"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, map[string]string{"-foo": "-bar"}, cli.Map)
|
||||||
|
}
|
||||||
|
|||||||
+8
-3
@@ -114,15 +114,20 @@ type Scanner struct {
|
|||||||
args []Token
|
args []Token
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan creates a new Scanner from args with untyped tokens.
|
// ScanAsType creates a new Scanner from args with the given type.
|
||||||
func Scan(args ...string) *Scanner {
|
func ScanAsType(ttype TokenType, args ...string) *Scanner {
|
||||||
s := &Scanner{}
|
s := &Scanner{}
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
s.args = append(s.args, Token{Value: arg})
|
s.args = append(s.args, Token{Value: arg, Type: ttype})
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scan creates a new Scanner from args with untyped tokens.
|
||||||
|
func Scan(args ...string) *Scanner {
|
||||||
|
return ScanAsType(UntypedToken, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// ScanFromTokens creates a new Scanner from a slice of tokens.
|
// ScanFromTokens creates a new Scanner from a slice of tokens.
|
||||||
func ScanFromTokens(tokens ...Token) *Scanner {
|
func ScanFromTokens(tokens ...Token) *Scanner {
|
||||||
return &Scanner{args: tokens}
|
return &Scanner{args: tokens}
|
||||||
|
|||||||
Reference in New Issue
Block a user