From 709c0c186b2f76a3b461bc88dcea865c60420b1e Mon Sep 17 00:00:00 2001 From: healthyyyoung Date: Fri, 4 Apr 2025 01:15:34 +0800 Subject: [PATCH] chore: replace strings.SplitN(arg, sep, 2) with strings.Cut(arg, sep) (#24147) Co-authored-by: Alex | Interchain Labs --- client/v2/autocli/flag/map.go | 5 +- client/v2/autocli/flag/map_test.go | 125 +++++++++++++++++++++++++ client/v2/autocli/flag/maps/generic.go | 8 +- store/rootmulti/store.go | 10 +- 4 files changed, 135 insertions(+), 13 deletions(-) create mode 100644 client/v2/autocli/flag/map_test.go diff --git a/client/v2/autocli/flag/map.go b/client/v2/autocli/flag/map.go index a5d70c7f7f..9c35967637 100644 --- a/client/v2/autocli/flag/map.go +++ b/client/v2/autocli/flag/map.go @@ -202,11 +202,10 @@ func (m compositeMapType[T]) NewValue(ctx *context.Context, opts *Builder) Value 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 { + key, val, found := strings.Cut(arg, "=") + if !found { return errors.New("invalid format, expected key=value") } - key, val := parts[0], parts[1] keyValue, err := m.keyValueResolver(key) if err != nil { diff --git a/client/v2/autocli/flag/map_test.go b/client/v2/autocli/flag/map_test.go new file mode 100644 index 0000000000..c7de906755 --- /dev/null +++ b/client/v2/autocli/flag/map_test.go @@ -0,0 +1,125 @@ +package flag + +import ( + "context" + "errors" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type mockValue struct { + val string + err error +} + +func (m *mockValue) Set(s string) error { + if m.err != nil { + return m.err + } + m.val = s + return nil +} + +func (m *mockValue) String() string { + return m.val +} + +func (m *mockValue) Type() string { + return "mock" +} + +func (m *mockValue) Get(_ protoreflect.Value) (protoreflect.Value, error) { + if m.err != nil { + return protoreflect.Value{}, m.err + } + return protoreflect.ValueOfString(m.val), nil +} + +type mockType struct { + err error +} + +func (m *mockType) NewValue(ctx *context.Context, opts *Builder) Value { + return &mockValue{err: m.err} +} + +func (m *mockType) DefaultValue() string { + return "" +} + +func TestCompositeMapValue_Set(t *testing.T) { + tests := []struct { + name string + input string + resolver keyValueResolver[int] + valueType Type + expectErr bool + expectVals map[int]string + }{ + { + name: "valid input", + input: "1=foo,2=bar", + resolver: strconv.Atoi, + valueType: &mockType{}, + expectErr: false, + expectVals: map[int]string{ + 1: "foo", + 2: "bar", + }, + }, + { + name: "invalid format", + input: "1foo,2=bar", + resolver: strconv.Atoi, + valueType: &mockType{}, + expectErr: true, + }, + { + name: "key resolver fails", + input: "1=foo,invalid=bar", + resolver: strconv.Atoi, + valueType: &mockType{}, + expectErr: true, + }, + { + name: "value parsing fails", + input: "1=foo,2=bar", + resolver: strconv.Atoi, + valueType: &mockType{err: errors.New("value error")}, + expectErr: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + m := &compositeMapValue[int]{ + keyValueResolver: tc.resolver, + keyType: "int", + valueType: tc.valueType, + ctx: &ctx, + opts: &Builder{}, + values: make(map[int]protoreflect.Value), + } + + err := m.Set(tc.input) + + if tc.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + + // Convert the protoreflect.Value map to a string map for comparison + actualVals := make(map[int]string) + for k, v := range m.values { + actualVals[k] = v.String() + } + + assert.Equal(t, tc.expectVals, actualVals) + } + }) + } +} diff --git a/client/v2/autocli/flag/maps/generic.go b/client/v2/autocli/flag/maps/generic.go index 371cce5f85..c22cdbf130 100644 --- a/client/v2/autocli/flag/maps/generic.go +++ b/client/v2/autocli/flag/maps/generic.go @@ -29,15 +29,15 @@ 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 { + key, val, found := strings.Cut(pair, "=") + if !found { return fmt.Errorf("%s must be formatted as key=value", pair) } - key, err := gm.Options.keyParser(kv[0]) + parsedKey, err := gm.Options.keyParser(key) if err != nil { return err } - out[key], err = gm.Options.valueParser(kv[1]) + out[parsedKey], err = gm.Options.valueParser(val) if err != nil { return err } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index f89eb3e9a1..b998745509 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -804,14 +804,12 @@ func parsePath(path string) (storeName, subpath string, err error) { return storeName, subpath, errorsmod.Wrapf(types.ErrUnknownRequest, "invalid path: %s", path) } - paths := strings.SplitN(path[1:], "/", 2) - storeName = paths[0] - - if len(paths) == 2 { - subpath = "/" + paths[1] + storeName, subpath, found := strings.Cut(path[1:], "/") + if !found { + return storeName, subpath, nil } - return storeName, subpath, nil + return storeName, "/" + subpath, nil } //---------------------- Snapshotting ------------------