Support multiple key+value pairs for map flags.
This commit is contained in:
@@ -346,6 +346,8 @@ var CLI struct {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For flags, multiple key+value pairs should be separated by `;` eg. `--set="key1=value1;key2=value2"`.
|
||||||
|
|
||||||
## Custom named decoders
|
## Custom named decoders
|
||||||
|
|
||||||
Kong includes a number of builtin custom type mappers. These can be used by
|
Kong includes a number of builtin custom type mappers. These can be used by
|
||||||
|
|||||||
@@ -284,37 +284,47 @@ func mapDecoder(r *Registry) MapperFunc {
|
|||||||
target.Set(reflect.MakeMap(target.Type()))
|
target.Set(reflect.MakeMap(target.Type()))
|
||||||
}
|
}
|
||||||
el := target.Type()
|
el := target.Type()
|
||||||
token := ctx.Scan.PopValue("map")
|
var childScanner *Scanner
|
||||||
parts := strings.SplitN(token, "=", 2)
|
if ctx.Value.Flag != nil {
|
||||||
if len(parts) != 2 {
|
// If decoding a flag, we need an argument.
|
||||||
return fmt.Errorf("expected \"<key>=<value>\" but got %q", token)
|
childScanner = Scan(SplitEscaped(ctx.Scan.PopValue("map"), ';')...)
|
||||||
|
} else {
|
||||||
|
tokens := ctx.Scan.PopWhile(func(t Token) bool { return t.IsValue() })
|
||||||
|
childScanner = ScanFromTokens(tokens...)
|
||||||
}
|
}
|
||||||
key, value := parts[0], parts[1]
|
for !childScanner.Peek().IsEOL() {
|
||||||
|
token := childScanner.PopValue("map")
|
||||||
keyTypeName, valueTypeName := "", ""
|
parts := strings.SplitN(token, "=", 2)
|
||||||
if typ := ctx.Value.Tag.Type; typ != "" {
|
|
||||||
parts := strings.Split(typ, ":")
|
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return fmt.Errorf("type:\"\" on map field must be in the form \"[<keytype>]:[<valuetype>]\"")
|
return fmt.Errorf("expected \"<key>=<value>\" but got %q", token)
|
||||||
}
|
}
|
||||||
keyTypeName, valueTypeName = parts[0], parts[1]
|
key, value := parts[0], parts[1]
|
||||||
}
|
|
||||||
|
|
||||||
keyScanner := Scan(key)
|
keyTypeName, valueTypeName := "", ""
|
||||||
keyDecoder := r.ForNamedType(keyTypeName, el.Key())
|
if typ := ctx.Value.Tag.Type; typ != "" {
|
||||||
keyValue := reflect.New(el.Key()).Elem()
|
parts := strings.Split(typ, ":")
|
||||||
if err := keyDecoder.Decode(ctx.WithScanner(keyScanner), keyValue); err != nil {
|
if len(parts) != 2 {
|
||||||
return fmt.Errorf("invalid map key %q", key)
|
return fmt.Errorf("type:\"\" on map field must be in the form \"[<keytype>]:[<valuetype>]\"")
|
||||||
}
|
}
|
||||||
|
keyTypeName, valueTypeName = parts[0], parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
valueScanner := Scan(value)
|
keyScanner := Scan(key)
|
||||||
valueDecoder := r.ForNamedType(valueTypeName, el.Elem())
|
keyDecoder := r.ForNamedType(keyTypeName, el.Key())
|
||||||
valueValue := reflect.New(el.Elem()).Elem()
|
keyValue := reflect.New(el.Key()).Elem()
|
||||||
if err := valueDecoder.Decode(ctx.WithScanner(valueScanner), valueValue); err != nil {
|
if err := keyDecoder.Decode(ctx.WithScanner(keyScanner), keyValue); err != nil {
|
||||||
return fmt.Errorf("invalid map value %q", value)
|
return fmt.Errorf("invalid map key %q", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
target.SetMapIndex(keyValue, valueValue)
|
valueScanner := Scan(value)
|
||||||
|
valueDecoder := r.ForNamedType(valueTypeName, el.Elem())
|
||||||
|
valueValue := reflect.New(el.Elem()).Elem()
|
||||||
|
if err := valueDecoder.Decode(ctx.WithScanner(valueScanner), valueValue); err != nil {
|
||||||
|
return fmt.Errorf("invalid map value %q", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
target.SetMapIndex(keyValue, valueValue)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,16 @@ func TestMapWithNamedTypes(t *testing.T) {
|
|||||||
require.Equal(t, map[string]string{"FIRST": "5s", "SECOND": "10s"}, cli.TypedKey)
|
require.Equal(t, map[string]string{"FIRST": "5s", "SECOND": "10s"}, cli.TypedKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMapWithMultipleValues(t *testing.T) {
|
||||||
|
var cli struct {
|
||||||
|
Value map[string]string
|
||||||
|
}
|
||||||
|
k := mustNew(t, &cli)
|
||||||
|
_, err := k.Parse([]string{"--value=a=b;c=d"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, map[string]string{"a": "b", "c": "d"}, cli.Value)
|
||||||
|
}
|
||||||
|
|
||||||
func TestURLMapper(t *testing.T) {
|
func TestURLMapper(t *testing.T) {
|
||||||
var cli struct {
|
var cli struct {
|
||||||
URL *url.URL `arg:""`
|
URL *url.URL `arg:""`
|
||||||
|
|||||||
Reference in New Issue
Block a user