feat(autocli): add map support (#15770)

This commit is contained in:
Jeancarlo Barrios 2023-05-29 22:42:54 -05:00 committed by GitHub
parent f358214b43
commit 62f0c6faf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 2306 additions and 80 deletions

View File

@ -2,13 +2,15 @@ package flag
import (
"context"
"strconv"
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
cosmos_proto "github.com/cosmos/cosmos-proto"
"github.com/spf13/pflag"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
cosmos_proto "github.com/cosmos/cosmos-proto"
"cosmossdk.io/client/v2/internal/util"
)
@ -60,8 +62,13 @@ func (b *Builder) addFieldFlag(ctx context.Context, flagSet *pflag.FlagSet, fiel
// use the built-in pflag StringP, Int32P, etc. functions
var val HasValue
if field.IsList() {
val = bindSimpleListFlag(flagSet, field.Kind(), name, shorthand, usage)
} else if field.IsMap() {
keyKind := field.MapKey().Kind()
valKind := field.MapValue().Kind()
val = bindSimpleMapFlag(flagSet, keyKind, valKind, name, shorthand, usage)
} else {
val = bindSimpleFlag(flagSet, field.Kind(), name, shorthand, usage)
}
@ -81,7 +88,65 @@ func (b *Builder) resolveFlagType(field protoreflect.FieldDescriptor) Type {
if typ != nil {
return compositeListType{simpleType: typ}
}
return nil
}
if field.IsMap() {
keyKind := field.MapKey().Kind()
valType := b.resolveFlagType(field.MapValue())
if valType != nil {
switch keyKind {
case protoreflect.StringKind:
ct := new(compositeMapType[string])
ct.keyValueResolver = func(s string) (string, error) { return s, nil }
ct.valueType = valType
ct.keyType = "string"
return ct
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
ct := new(compositeMapType[int32])
ct.keyValueResolver = func(s string) (int32, error) {
i, err := strconv.ParseInt(s, 10, 32)
return int32(i), err
}
ct.valueType = valType
ct.keyType = "int32"
return ct
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
ct := new(compositeMapType[int64])
ct.keyValueResolver = func(s string) (int64, error) {
i, err := strconv.ParseInt(s, 10, 64)
return i, err
}
ct.valueType = valType
ct.keyType = "int64"
return ct
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
ct := new(compositeMapType[uint32])
ct.keyValueResolver = func(s string) (uint32, error) {
i, err := strconv.ParseUint(s, 10, 32)
return uint32(i), err
}
ct.valueType = valType
ct.keyType = "uint32"
return ct
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
ct := new(compositeMapType[uint64])
ct.keyValueResolver = func(s string) (uint64, error) {
i, err := strconv.ParseUint(s, 10, 64)
return i, err
}
ct.valueType = valType
ct.keyType = "uint64"
return ct
case protoreflect.BoolKind:
ct := new(compositeMapType[bool])
ct.keyValueResolver = strconv.ParseBool
ct.valueType = valType
ct.keyType = "bool"
return ct
}
return nil
}
return nil
}
@ -107,7 +172,6 @@ func (b *Builder) resolveFlagTypeBasic(field protoreflect.FieldDescriptor) Type
if flagType, ok := b.messageFlagTypes[field.Message().FullName()]; ok {
return flagType
}
return jsonMessageFlagType{
messageDesc: field.Message(),
}

View File

@ -0,0 +1,252 @@
package flag
import (
"context"
"fmt"
"strings"
"github.com/cockroachdb/errors"
"github.com/spf13/pflag"
"google.golang.org/protobuf/reflect/protoreflect"
)
func bindSimpleMapFlag(flagSet *pflag.FlagSet, keyKind, valueKind protoreflect.Kind, name, shorthand, usage string) HasValue {
switch keyKind {
case protoreflect.StringKind:
switch valueKind {
case protoreflect.StringKind:
val := flagSet.StringToStringP(name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfString)
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
val := StringToInt32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt32)
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
val := flagSet.StringToInt64P(name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt64)
case protoreflect.Uint32Kind:
val := StringToUint32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint32)
case protoreflect.Uint64Kind:
val := StringToUint64P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint64)
case protoreflect.BoolKind:
val := StringToBoolP(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfBool)
}
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
switch valueKind {
case protoreflect.StringKind:
val := Int32ToStringP(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfString)
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
val := Int32ToInt32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt32)
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
val := Int32ToInt64P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt64)
case protoreflect.Uint32Kind:
val := Int32ToUint32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint32)
case protoreflect.Uint64Kind:
val := Int32ToUint64P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint64)
case protoreflect.BoolKind:
val := Int32ToBoolP(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfBool)
}
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
switch valueKind {
case protoreflect.StringKind:
val := Int64ToStringP(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfString)
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
val := Int64ToInt32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt32)
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
val := Int64ToInt64P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt64)
case protoreflect.Uint32Kind:
val := Int64ToUint32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint32)
case protoreflect.Uint64Kind:
val := Int64ToUint64P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint64)
case protoreflect.BoolKind:
val := Int64ToBoolP(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfBool)
}
case protoreflect.Uint32Kind:
switch valueKind {
case protoreflect.StringKind:
val := Uint32ToStringP(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfString)
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
val := Uint32ToInt32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt32)
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
val := Uint32ToInt64P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt64)
case protoreflect.Uint32Kind:
val := Uint32ToUint32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint32)
case protoreflect.Uint64Kind:
val := Uint32ToUint64P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint64)
case protoreflect.BoolKind:
val := Uint32ToBoolP(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfBool)
}
case protoreflect.Uint64Kind:
switch valueKind {
case protoreflect.StringKind:
val := Uint64ToStringP(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfString)
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
val := Uint64ToInt32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt32)
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
val := Uint64ToInt64P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt64)
case protoreflect.Uint32Kind:
val := Uint64ToUint32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint32)
case protoreflect.Uint64Kind:
val := Uint64ToUint64P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint64)
case protoreflect.BoolKind:
val := Uint64ToBoolP(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfBool)
}
case protoreflect.BoolKind:
switch valueKind {
case protoreflect.StringKind:
val := BoolToStringP(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfString)
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
val := BoolToInt32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt32)
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
val := BoolToInt64P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfInt64)
case protoreflect.Uint32Kind:
val := BoolToUint32P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint32)
case protoreflect.Uint64Kind:
val := BoolToUint64P(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfUint64)
case protoreflect.BoolKind:
val := BoolToBoolP(flagSet, name, shorthand, nil, usage)
return newMapValue(val, protoreflect.ValueOfBool)
}
}
return nil
}
type mapValue[K comparable, V any] struct {
value *map[K]V
toProtoreflectValue func(V) protoreflect.Value
}
func newMapValue[K comparable, V any](mapV *map[K]V, toProtoreflectValue func(V) protoreflect.Value) mapValue[K, V] {
return mapValue[K, V]{value: mapV, toProtoreflectValue: toProtoreflectValue}
}
func (v mapValue[K, V]) Get(mutable protoreflect.Value) (protoreflect.Value, error) {
protoMap := mutable.Map()
for k, val := range *v.value {
protoMap.Set(protoreflect.MapKey(protoreflect.ValueOf(k)), v.toProtoreflectValue(val))
}
return mutable, nil
}
// keyValueResolver is a function that converts a string to a key that is primitive Type T
type keyValueResolver[T comparable] func(string) (T, error)
// compositeMapType is a map type that is composed of a key and value type that are both primitive types
type compositeMapType[T comparable] struct {
keyValueResolver keyValueResolver[T]
keyType string
valueType Type
}
// compositeMapValue is a map value that is composed of a key and value type that are both primitive types
type compositeMapValue[T comparable] struct {
keyValueResolver keyValueResolver[T]
keyType string
valueType Type
values map[T]protoreflect.Value
ctx context.Context
opts *Builder
}
func (m compositeMapType[T]) DefaultValue() string {
return ""
}
func (m compositeMapType[T]) NewValue(ctx context.Context, opts *Builder) Value {
return &compositeMapValue[T]{
keyValueResolver: m.keyValueResolver,
valueType: m.valueType,
keyType: m.keyType,
ctx: ctx,
opts: opts,
values: nil,
}
}
func (m *compositeMapValue[T]) Set(s string) error {
comaArgs := strings.Split(s, ",")
for _, arg := range comaArgs {
parts := strings.SplitN(arg, "=", 2)
if len(parts) != 2 {
return errors.New("invalid format, expected key=value")
}
key, val := parts[0], parts[1]
keyValue, err := m.keyValueResolver(key)
if err != nil {
return err
}
simpleVal := m.valueType.NewValue(m.ctx, m.opts)
err = simpleVal.Set(val)
if err != nil {
return err
}
protoValue, err := simpleVal.Get(protoreflect.Value{})
if err != nil {
return err
}
if m.values == nil {
m.values = make(map[T]protoreflect.Value)
}
m.values[keyValue] = protoValue
}
return nil
}
func (m *compositeMapValue[T]) Get(mutable protoreflect.Value) (protoreflect.Value, error) {
protoMap := mutable.Map()
for key, value := range m.values {
keyVal := protoreflect.ValueOf(key)
protoMap.Set(keyVal.MapKey(), value)
}
return protoreflect.ValueOfMap(protoMap), nil
}
func (m *compositeMapValue[T]) String() string {
if m.values == nil {
return ""
}
return fmt.Sprintf("%+v", m.values)
}
func (m *compositeMapValue[T]) Type() string {
return fmt.Sprintf("map[%s]%s", m.keyType, m.valueType.NewValue(m.ctx, m.opts).Type())
}

View File

@ -0,0 +1,138 @@
package flag
import (
"strconv"
"github.com/spf13/pflag"
)
func newBoolToString[K bool, V string](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newBoolStringMap := newGenericMapValue(val, p)
newBoolStringMap.Options = genericMapValueOptions[K, V]{
genericType: "boolToString",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseBool(s)
return K(value), err
},
valueParser: func(s string) (V, error) {
return V(s), nil
},
}
return newBoolStringMap
}
func BoolToStringP(flagSet *pflag.FlagSet, name, shorthand string, value map[bool]string, usage string) *map[bool]string {
p := make(map[bool]string)
flagSet.VarP(newBoolToString(value, &p), name, shorthand, usage)
return &p
}
func newBoolToInt32[K bool, V int32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newBoolInt32Map := newGenericMapValue(val, p)
newBoolInt32Map.Options = genericMapValueOptions[K, V]{
genericType: "boolToInt32",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseBool(s)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseInt(s, 10, 32)
return V(value), err
},
}
return newBoolInt32Map
}
func BoolToInt32P(flagSet *pflag.FlagSet, name, shorthand string, value map[bool]int32, usage string) *map[bool]int32 {
p := make(map[bool]int32)
flagSet.VarP(newBoolToInt32(value, &p), name, shorthand, usage)
return &p
}
func newBoolToInt64[K bool, V int64](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newBoolInt64Map := newGenericMapValue(val, p)
newBoolInt64Map.Options = genericMapValueOptions[K, V]{
genericType: "boolToInt64",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseBool(s)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseInt(s, 10, 64)
return V(value), err
},
}
return newBoolInt64Map
}
func BoolToInt64P(flagSet *pflag.FlagSet, name, shorthand string, value map[bool]int64, usage string) *map[bool]int64 {
p := make(map[bool]int64)
flagSet.VarP(newBoolToInt64(value, &p), name, shorthand, usage)
return &p
}
func newBoolToUint32[K bool, V uint32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newBoolUint32Map := newGenericMapValue(val, p)
newBoolUint32Map.Options = genericMapValueOptions[K, V]{
genericType: "boolToUint32",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseBool(s)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 32)
return V(value), err
},
}
return newBoolUint32Map
}
func BoolToUint32P(flagSet *pflag.FlagSet, name, shorthand string, value map[bool]uint32, usage string) *map[bool]uint32 {
p := make(map[bool]uint32)
flagSet.VarP(newBoolToUint32(value, &p), name, shorthand, usage)
return &p
}
func newBoolToUint64[K bool, V uint64](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newBoolUint64Map := newGenericMapValue(val, p)
newBoolUint64Map.Options = genericMapValueOptions[K, V]{
genericType: "boolToUint64",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseBool(s)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 64)
return V(value), err
},
}
return newBoolUint64Map
}
func BoolToUint64P(flagSet *pflag.FlagSet, name, shorthand string, value map[bool]uint64, usage string) *map[bool]uint64 {
p := make(map[bool]uint64)
flagSet.VarP(newBoolToUint64(value, &p), name, shorthand, usage)
return &p
}
func newBoolToBool[K, V bool](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newBoolBoolMap := newGenericMapValue(val, p)
newBoolBoolMap.Options = genericMapValueOptions[K, V]{
genericType: "boolToBool",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseBool(s)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseBool(s)
return V(value), err
},
}
return newBoolBoolMap
}
func BoolToBoolP(flagSet *pflag.FlagSet, name, shorthand string, value map[bool]bool, usage string) *map[bool]bool {
p := make(map[bool]bool)
flagSet.VarP(newBoolToBool(value, &p), name, shorthand, usage)
return &p
}

View File

@ -0,0 +1,159 @@
package flag
import (
"strconv"
"github.com/spf13/pflag"
)
func newInt32ToString[K int32, V string](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt32StringMap := newGenericMapValue(val, p)
newInt32StringMap.Options = genericMapValueOptions[K, V]{
genericType: "int32ToString",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
return V(s), nil
},
}
return newInt32StringMap
}
func Int32ToStringP(flagSet *pflag.FlagSet, name, shorthand string, value map[int32]string, usage string) *map[int32]string {
p := make(map[int32]string)
flagSet.VarP(newInt32ToString(value, &p), name, shorthand, usage)
return &p
}
func newInt32ToInt32[K, V int32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt32Int32Map := newGenericMapValue(val, p)
newInt32Int32Map.Options = genericMapValueOptions[K, V]{
genericType: "int32ToInt32",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseInt(s, 10, 32)
return V(value), err
},
}
return newInt32Int32Map
}
func Int32ToInt32P(flagSet *pflag.FlagSet, name, shorthand string, value map[int32]int32, usage string) *map[int32]int32 {
p := make(map[int32]int32)
flagSet.VarP(newInt32ToInt32(value, &p), name, shorthand, usage)
return &p
}
func newInt32ToInt64[K int32, V int64](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt32Int64Map := newGenericMapValue(val, p)
newInt32Int64Map.Options = genericMapValueOptions[K, V]{
genericType: "int32ToInt64",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseInt(s, 10, 64)
return V(value), err
},
}
return newInt32Int64Map
}
func Int32ToInt64P(flagSet *pflag.FlagSet, name, shorthand string, value map[int32]int64, usage string) *map[int32]int64 {
p := make(map[int32]int64)
flagSet.VarP(newInt32ToInt64(value, &p), name, shorthand, usage)
return &p
}
func newInt32ToUint32[K int32, V uint32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt32Uint32Map := newGenericMapValue(val, p)
newInt32Uint32Map.Options = genericMapValueOptions[K, V]{
genericType: "int32ToUint32",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 32)
return V(value), err
},
}
return newInt32Uint32Map
}
func Int32ToUint32P(flagSet *pflag.FlagSet, name, shorthand string, value map[int32]uint32, usage string) *map[int32]uint32 {
p := make(map[int32]uint32)
flagSet.VarP(newInt32ToUint32(value, &p), name, shorthand, usage)
return &p
}
func newInt32ToUint64[K int32, V uint64](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt32Uint64Map := newGenericMapValue(val, p)
newInt32Uint64Map.Options = genericMapValueOptions[K, V]{
genericType: "int32ToUint64",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 64)
return V(value), err
},
}
return newInt32Uint64Map
}
func Int32ToUint64P(flagSet *pflag.FlagSet, name, shorthand string, value map[int32]uint64, usage string) *map[int32]uint64 {
p := make(map[int32]uint64)
flagSet.VarP(newInt32ToUint64(value, &p), name, shorthand, usage)
return &p
}
func newInt32ToBool[K int32, V bool](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt32BoolMap := newGenericMapValue(val, p)
newInt32BoolMap.Options = genericMapValueOptions[K, V]{
genericType: "int32ToBool",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseBool(s)
return V(value), err
},
}
return newInt32BoolMap
}
func Int32ToBoolP(flagSet *pflag.FlagSet, name, shorthand string, value map[int32]bool, usage string) *map[int32]bool {
p := make(map[int32]bool)
flagSet.VarP(newInt32ToBool(value, &p), name, shorthand, usage)
return &p
}
func newStringToBool[K string, V bool](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newStringBoolMap := newGenericMapValue(val, p)
newStringBoolMap.Options = genericMapValueOptions[K, V]{
genericType: "stringToBool",
keyParser: func(s string) (K, error) {
return K(s), nil
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseBool(s)
return V(value), err
},
}
return newStringBoolMap
}
func StringToBoolP(flagSet *pflag.FlagSet, name, shorthand string, value map[string]bool, usage string) *map[string]bool {
p := make(map[string]bool)
flagSet.VarP(newStringToBool(value, &p), name, shorthand, usage)
return &p
}

View File

@ -0,0 +1,180 @@
package flag
import (
"strconv"
"github.com/spf13/pflag"
)
func newInt64ToString[K int64, V string](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt64StringMap := newGenericMapValue(val, p)
newInt64StringMap.Options = genericMapValueOptions[K, V]{
genericType: "int64ToString",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
return V(s), nil
},
}
return newInt64StringMap
}
func Int64ToStringP(flagSet *pflag.FlagSet, name, shorthand string, value map[int64]string, usage string) *map[int64]string {
p := make(map[int64]string)
flagSet.VarP(newInt64ToString(value, &p), name, shorthand, usage)
return &p
}
func newInt64ToInt32[K int64, V int32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt64Int32Map := newGenericMapValue(val, p)
newInt64Int32Map.Options = genericMapValueOptions[K, V]{
genericType: "int64ToInt32",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseInt(s, 10, 32)
return V(value), err
},
}
return newInt64Int32Map
}
func Int64ToInt32P(flagSet *pflag.FlagSet, name, shorthand string, value map[int64]int32, usage string) *map[int64]int32 {
p := make(map[int64]int32)
flagSet.VarP(newInt64ToInt32(value, &p), name, shorthand, usage)
return &p
}
func newInt64ToInt64[K, V int64](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt64Int64Map := newGenericMapValue(val, p)
newInt64Int64Map.Options = genericMapValueOptions[K, V]{
genericType: "int64ToInt64",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseInt(s, 10, 64)
return V(value), err
},
}
return newInt64Int64Map
}
func Int64ToInt64P(flagSet *pflag.FlagSet, name, shorthand string, value map[int64]int64, usage string) *map[int64]int64 {
p := make(map[int64]int64)
flagSet.VarP(newInt64ToInt64(value, &p), name, shorthand, usage)
return &p
}
func newInt64ToUint32[K int64, V uint32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt64Uint32Map := newGenericMapValue(val, p)
newInt64Uint32Map.Options = genericMapValueOptions[K, V]{
genericType: "int64ToUint32",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 32)
return V(value), err
},
}
return newInt64Uint32Map
}
func Int64ToUint32P(flagSet *pflag.FlagSet, name, shorthand string, value map[int64]uint32, usage string) *map[int64]uint32 {
p := make(map[int64]uint32)
flagSet.VarP(newInt64ToUint32(value, &p), name, shorthand, usage)
return &p
}
func newInt64ToUint64[K int64, V uint64](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt64Uint64Map := newGenericMapValue(val, p)
newInt64Uint64Map.Options = genericMapValueOptions[K, V]{
genericType: "int64ToUint64",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 64)
return V(value), err
},
}
return newInt64Uint64Map
}
func Int64ToUint64P(flagSet *pflag.FlagSet, name, shorthand string, value map[int64]uint64, usage string) *map[int64]uint64 {
p := make(map[int64]uint64)
flagSet.VarP(newInt64ToUint64(value, &p), name, shorthand, usage)
return &p
}
func newInt64ToBool[K int64, V bool](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newInt64BoolMap := newGenericMapValue(val, p)
newInt64BoolMap.Options = genericMapValueOptions[K, V]{
genericType: "int64ToBool",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseInt(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseBool(s)
return V(value), err
},
}
return newInt64BoolMap
}
func Int64ToBoolP(flagSet *pflag.FlagSet, name, shorthand string, value map[int64]bool, usage string) *map[int64]bool {
p := make(map[int64]bool)
flagSet.VarP(newInt64ToBool(value, &p), name, shorthand, usage)
return &p
}
func newStringToUint32[K string, V uint32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newStringUintMap := newGenericMapValue(val, p)
newStringUintMap.Options = genericMapValueOptions[K, V]{
genericType: "stringToUint32",
keyParser: func(s string) (K, error) {
return K(s), nil
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 32)
return V(value), err
},
}
return newStringUintMap
}
func StringToUint32P(flagSet *pflag.FlagSet, name, shorthand string, value map[string]uint32, usage string) *map[string]uint32 {
p := make(map[string]uint32)
flagSet.VarP(newStringToUint32(value, &p), name, shorthand, usage)
return &p
}
func newStringToUint64[K string, V uint64](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newStringUint64Map := newGenericMapValue(val, p)
newStringUint64Map.Options = genericMapValueOptions[K, V]{
genericType: "stringToUint64",
keyParser: func(s string) (K, error) {
return K(s), nil
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 64)
return V(value), err
},
}
return newStringUint64Map
}
func StringToUint64P(flagSet *pflag.FlagSet, name, shorthand string, value map[string]uint64, usage string) *map[string]uint64 {
p := make(map[string]uint64)
flagSet.VarP(newStringToUint64(value, &p), name, shorthand, usage)
return &p
}

View File

@ -0,0 +1,28 @@
package flag
import (
"strconv"
"github.com/spf13/pflag"
)
func newStringToInt32[K string, V int32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newStringIntMap := newGenericMapValue(val, p)
newStringIntMap.Options = genericMapValueOptions[K, V]{
genericType: "stringToInt32",
keyParser: func(s string) (K, error) {
return K(s), nil
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseInt(s, 10, 32)
return V(value), err
},
}
return newStringIntMap
}
func StringToInt32P(flagSet *pflag.FlagSet, name, shorthand string, value map[string]int32, usage string) *map[string]int32 {
p := make(map[string]int32)
flagSet.VarP(newStringToInt32(value, &p), name, shorthand, usage)
return &p
}

View File

@ -0,0 +1,62 @@
package flag
import (
"strings"
"github.com/cockroachdb/errors"
)
type genericMapValueOptions[K comparable, V any] struct {
keyParser func(string) (K, error)
valueParser func(string) (V, error)
genericType string
}
type genericMapValue[K comparable, V any] struct {
value *map[K]V
changed bool
Options genericMapValueOptions[K, V]
}
func newGenericMapValue[K comparable, V any](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
ssv := new(genericMapValue[K, V])
ssv.value = p
*ssv.value = val
return ssv
}
func (gm *genericMapValue[K, V]) Set(val string) error {
ss := strings.Split(val, ",")
out := make(map[K]V, len(ss))
for _, pair := range ss {
kv := strings.SplitN(pair, "=", 2)
if len(kv) != 2 {
return errors.Errorf("%s must be formatted as key=value", pair)
}
key, err := gm.Options.keyParser(kv[0])
if err != nil {
return err
}
out[key], err = gm.Options.valueParser(kv[1])
if err != nil {
return err
}
}
if !gm.changed {
*gm.value = out
} else {
for k, v := range out {
(*gm.value)[k] = v
}
}
gm.changed = true
return nil
}
func (gm *genericMapValue[K, V]) Type() string {
return gm.Options.genericType
}
func (gm *genericMapValue[K, V]) String() string {
return ""
}

View File

@ -0,0 +1,138 @@
package flag
import (
"strconv"
"github.com/spf13/pflag"
)
func newUint32ToString[K uint32, V string](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint32StringMap := newGenericMapValue(val, p)
newUint32StringMap.Options = genericMapValueOptions[K, V]{
genericType: "uint32ToString",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
return V(s), nil
},
}
return newUint32StringMap
}
func Uint32ToStringP(flagSet *pflag.FlagSet, name, shorthand string, value map[uint32]string, usage string) *map[uint32]string {
p := make(map[uint32]string)
flagSet.VarP(newUint32ToString(value, &p), name, shorthand, usage)
return &p
}
func newUint32ToInt32[K uint32, V int32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint32Int32Map := newGenericMapValue(val, p)
newUint32Int32Map.Options = genericMapValueOptions[K, V]{
genericType: "uint32ToInt32",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseInt(s, 10, 32)
return V(value), err
},
}
return newUint32Int32Map
}
func Uint32ToInt32P(flagSet *pflag.FlagSet, name, shorthand string, value map[uint32]int32, usage string) *map[uint32]int32 {
p := make(map[uint32]int32)
flagSet.VarP(newUint32ToInt32(value, &p), name, shorthand, usage)
return &p
}
func newUint32ToInt64[K uint32, V int64](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint32Int64Map := newGenericMapValue(val, p)
newUint32Int64Map.Options = genericMapValueOptions[K, V]{
genericType: "uint32ToInt64",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseInt(s, 10, 64)
return V(value), err
},
}
return newUint32Int64Map
}
func Uint32ToInt64P(flagSet *pflag.FlagSet, name, shorthand string, value map[uint32]int64, usage string) *map[uint32]int64 {
p := make(map[uint32]int64)
flagSet.VarP(newUint32ToInt64(value, &p), name, shorthand, usage)
return &p
}
func newUint32ToUint32[K, V uint32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint32Uint32Map := newGenericMapValue(val, p)
newUint32Uint32Map.Options = genericMapValueOptions[K, V]{
genericType: "uint32ToUint32",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 32)
return V(value), err
},
}
return newUint32Uint32Map
}
func Uint32ToUint32P(flagSet *pflag.FlagSet, name, shorthand string, value map[uint32]uint32, usage string) *map[uint32]uint32 {
p := make(map[uint32]uint32)
flagSet.VarP(newUint32ToUint32(value, &p), name, shorthand, usage)
return &p
}
func newUint32ToUint64[K uint32, V uint64](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint32Uint64Map := newGenericMapValue(val, p)
newUint32Uint64Map.Options = genericMapValueOptions[K, V]{
genericType: "uint32ToUint64",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 64)
return V(value), err
},
}
return newUint32Uint64Map
}
func Uint32ToUint64P(flagSet *pflag.FlagSet, name, shorthand string, value map[uint32]uint64, usage string) *map[uint32]uint64 {
p := make(map[uint32]uint64)
flagSet.VarP(newUint32ToUint64(value, &p), name, shorthand, usage)
return &p
}
func newUint32ToBool[K uint32, V bool](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint32BoolMap := newGenericMapValue(val, p)
newUint32BoolMap.Options = genericMapValueOptions[K, V]{
genericType: "uint32ToBool",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 32)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseBool(s)
return V(value), err
},
}
return newUint32BoolMap
}
func Uint32ToBoolP(flagSet *pflag.FlagSet, name, shorthand string, value map[uint32]bool, usage string) *map[uint32]bool {
p := make(map[uint32]bool)
flagSet.VarP(newUint32ToBool(value, &p), name, shorthand, usage)
return &p
}

View File

@ -0,0 +1,138 @@
package flag
import (
"strconv"
"github.com/spf13/pflag"
)
func newUint64ToString[K uint64, V string](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint64StringMap := newGenericMapValue(val, p)
newUint64StringMap.Options = genericMapValueOptions[K, V]{
genericType: "uint64ToString",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
return V(s), nil
},
}
return newUint64StringMap
}
func Uint64ToStringP(flagSet *pflag.FlagSet, name, shorthand string, value map[uint64]string, usage string) *map[uint64]string {
p := make(map[uint64]string)
flagSet.VarP(newUint64ToString(value, &p), name, shorthand, usage)
return &p
}
func newUint64ToInt32[K uint64, V int32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint64Int32Map := newGenericMapValue(val, p)
newUint64Int32Map.Options = genericMapValueOptions[K, V]{
genericType: "uint64ToInt32",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseInt(s, 10, 32)
return V(value), err
},
}
return newUint64Int32Map
}
func Uint64ToInt32P(flagSet *pflag.FlagSet, name, shorthand string, value map[uint64]int32, usage string) *map[uint64]int32 {
p := make(map[uint64]int32)
flagSet.VarP(newUint64ToInt32(value, &p), name, shorthand, usage)
return &p
}
func newUint64ToInt64[K uint64, V int64](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint64Int64Map := newGenericMapValue(val, p)
newUint64Int64Map.Options = genericMapValueOptions[K, V]{
genericType: "uint64ToInt64",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseInt(s, 10, 64)
return V(value), err
},
}
return newUint64Int64Map
}
func Uint64ToInt64P(flagSet *pflag.FlagSet, name, shorthand string, value map[uint64]int64, usage string) *map[uint64]int64 {
p := make(map[uint64]int64)
flagSet.VarP(newUint64ToInt64(value, &p), name, shorthand, usage)
return &p
}
func newUint64ToUint32[K uint64, V uint32](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint64Uint32Map := newGenericMapValue(val, p)
newUint64Uint32Map.Options = genericMapValueOptions[K, V]{
genericType: "uint64ToUint32",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 32)
return V(value), err
},
}
return newUint64Uint32Map
}
func Uint64ToUint32P(flagSet *pflag.FlagSet, name, shorthand string, value map[uint64]uint32, usage string) *map[uint64]uint32 {
p := make(map[uint64]uint32)
flagSet.VarP(newUint64ToUint32(value, &p), name, shorthand, usage)
return &p
}
func newUint64ToUint64[K, V uint64](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint64Uint64Map := newGenericMapValue(val, p)
newUint64Uint64Map.Options = genericMapValueOptions[K, V]{
genericType: "uint64ToUint64",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseUint(s, 10, 64)
return V(value), err
},
}
return newUint64Uint64Map
}
func Uint64ToUint64P(flagSet *pflag.FlagSet, name, shorthand string, value map[uint64]uint64, usage string) *map[uint64]uint64 {
p := make(map[uint64]uint64)
flagSet.VarP(newUint64ToUint64(value, &p), name, shorthand, usage)
return &p
}
func newUint64ToBool[K uint64, V bool](val map[K]V, p *map[K]V) *genericMapValue[K, V] {
newUint64BoolMap := newGenericMapValue(val, p)
newUint64BoolMap.Options = genericMapValueOptions[K, V]{
genericType: "uint64ToBool",
keyParser: func(s string) (K, error) {
value, err := strconv.ParseUint(s, 10, 64)
return K(value), err
},
valueParser: func(s string) (V, error) {
value, err := strconv.ParseBool(s)
return V(value), err
},
}
return newUint64BoolMap
}
func Uint64ToBoolP(flagSet *pflag.FlagSet, name, shorthand string, value map[uint64]bool, usage string) *map[uint64]bool {
p := make(map[uint64]bool)
flagSet.VarP(newUint64ToBool(value, &p), name, shorthand, usage)
return &p
}

View File

@ -82,6 +82,15 @@ var testCmdDesc = &autocliv1.ServiceCommandDescriptor{
"bz": {
Usage: "some bytes",
},
"map_string_string": {
Usage: "some map of string to string",
},
"map_string_uint32": {
Usage: "some map of string to int32",
},
"map_string_coin": {
Usage: "some map of string to coin",
},
},
},
},
@ -121,6 +130,72 @@ func TestCoin(t *testing.T) {
assert.DeepEqual(t, conn.lastRequest, conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform())
}
func TestMap(t *testing.T) {
conn := testExecCommon(t, buildModuleQueryCommand,
"echo",
"1",
"abc",
"1234foo",
"4321bar",
"--map-string-uint32", "bar=123",
"--map-string-string", "val=foo",
"--map-string-coin", "baz=100000foo",
"--map-string-coin", "sec=100000bar",
"--map-string-coin", "multi=100000bar,flag=100000foo",
)
assert.DeepEqual(t, conn.lastRequest, conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform())
conn = testExecCommon(t, buildModuleQueryCommand,
"echo",
"1",
"abc",
"1234foo",
"4321bar",
"--map-string-uint32", "bar=123",
"--map-string-coin", "baz,100000foo",
"--map-string-coin", "sec=100000bar",
)
assert.Equal(t, "Error: invalid argument \"baz,100000foo\" for \"--map-string-coin\" flag: invalid format, expected key=value\n", conn.errorOut.String())
conn = testExecCommon(t, buildModuleQueryCommand,
"echo",
"1",
"abc",
"1234foo",
"4321bar",
"--map-string-uint32", "bar=not-unint32",
"--map-string-coin", "baz=100000foo",
"--map-string-coin", "sec=100000bar",
)
assert.Equal(t, "Error: invalid argument \"bar=not-unint32\" for \"--map-string-uint32\" flag: strconv.ParseUint: parsing \"not-unint32\": invalid syntax\n", conn.errorOut.String())
conn = testExecCommon(t, buildModuleQueryCommand,
"echo",
"1",
"abc",
"1234foo",
"4321bar",
"--map-string-uint32", "bar=123.9",
"--map-string-coin", "baz=100000foo",
"--map-string-coin", "sec=100000bar",
)
assert.Equal(t, "Error: invalid argument \"bar=123.9\" for \"--map-string-uint32\" flag: strconv.ParseUint: parsing \"123.9\": invalid syntax\n", conn.errorOut.String())
}
func TestMapError(t *testing.T) {
conn := testExecCommon(t, buildModuleQueryCommand,
"echo",
"1",
"abc",
"1234foo",
"4321bar",
"--map-string-uint32", "bar=123",
"--map-string-coin", "baz=100000foo",
"--map-string-coin", "sec=100000bar",
)
assert.DeepEqual(t, conn.lastRequest, conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform())
}
func TestEverything(t *testing.T) {
conn := testExecCommon(t, buildModuleQueryCommand,
"echo",
@ -163,9 +238,6 @@ func TestEverything(t *testing.T) {
"--uints", "1,2,3",
"--uints", "4",
)
errOut := conn.errorOut.String()
res := conn.out.String()
fmt.Println(errOut, res)
assert.DeepEqual(t, conn.lastRequest, conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform())
}

View File

@ -21,6 +21,9 @@ Flags:
--hidden-bool
--i32 int32
--i64 int
--map-string-coin map[string]cosmos.base.v1beta1.Coin
--map-string-string stringToString (default [])
--map-string-uint32 stringToUint32
--node string <host>:<port> to CometBFT RPC interface for this chain (default "tcp://localhost:26657")
-o, --output string Output format (text|json) (default "text")
--page-count-total

View File

@ -27,6 +27,9 @@ Flags:
-h, --help help for echo
--i32 int32 some random int32
--i64 int
--map-string-coin map[string]cosmos.base.v1beta1.Coin some map of string to coin
--map-string-string stringToString some map of string to string (default [])
--map-string-uint32 stringToUint32 some map of string to int32
--node string <host>:<port> to CometBFT RPC interface for this chain (default "tcp://localhost:26657")
-o, --output string Output format (text|json) (default "text")
--page-count-total

View File

@ -43,6 +43,9 @@ message EchoRequest {
string deprecated_field = 30;
string shorthand_deprecated_field = 31;
bool hidden_bool = 32;
map<string, string> map_string_string = 33;
map<string, uint32> map_string_uint32 = 34;
map<string, cosmos.base.v1beta1.Coin> map_string_coin = 35;
}
enum Enum {

File diff suppressed because it is too large Load Diff