diff --git a/base/commands/common.go b/base/commands/common.go index 1653af99..a0f8959f 100644 --- a/base/commands/common.go +++ b/base/commands/common.go @@ -207,6 +207,28 @@ func AddValueTypeFlag(cc plug.InitContext) { cc.AddStringFlag(FlagValueType, "v", "string", false, help) } +func MakeValueFromString(s string, typeStr string) (any, error) { + if typeStr == "" { + typeStr = "string" + } + return mk.ValueFromString(s, typeStr) +} + +func MakeValuesFromStrings(typeStr string, items []string) ([]any, error) { + if typeStr == "" { + typeStr = "string" + } + vs := make([]any, len(items)) + for i, s := range items { + v, err := mk.ValueFromString(s, typeStr) + if err != nil { + return nil, fmt.Errorf("converting string to key: %w", err) + } + vs[i] = v + } + return vs, nil +} + func MakeKeyData(ec plug.ExecContext, ci *hazelcast.ClientInternal, keyStr string) (hazelcast.Data, error) { kt := ec.Props().GetString(FlagKeyType) if kt == "" { diff --git a/base/commands/map/map_entry_set.go b/base/commands/map/map_entry_set.go index 21610a6c..66df367d 100644 --- a/base/commands/map/map_entry_set.go +++ b/base/commands/map/map_entry_set.go @@ -10,6 +10,6 @@ import ( ) func init() { - c := commands.NewMapEntrySetCommand("Map", codec.EncodeMapEntrySetRequest, codec.DecodeMapEntrySetResponse) + c := commands.NewMapEntrySetCommand("Map", codec.EncodeMapEntrySetRequest, codec.DecodeMapEntrySetResponse, getMap) check.Must(plug.Registry.RegisterCommand("map:entry-set", c)) } diff --git a/base/commands/map/map_get.go b/base/commands/map/map_get.go index 2eaba89d..61a257ea 100644 --- a/base/commands/map/map_get.go +++ b/base/commands/map/map_get.go @@ -3,6 +3,8 @@ package _map import ( + "github.com/hazelcast/hazelcast-go-client" + "github.com/hazelcast/hazelcast-commandline-client/base/commands" "github.com/hazelcast/hazelcast-commandline-client/internal/check" "github.com/hazelcast/hazelcast-commandline-client/internal/plug" @@ -10,6 +12,6 @@ import ( ) func init() { - c := commands.NewMapGetCommand("Map", codec.EncodeMapGetRequest, makeDecodeResponseRowsFunc(codec.DecodeMapGetResponse)) + c := commands.NewMapGetCommand[*hazelcast.Map]("Map", codec.EncodeMapGetRequest, makeDecodeResponseRowsFunc(codec.DecodeMapGetResponse), getMap) check.Must(plug.Registry.RegisterCommand("map:get", c)) } diff --git a/base/commands/map/map_key_set.go b/base/commands/map/map_key_set.go index bcdb67fb..d08fc805 100644 --- a/base/commands/map/map_key_set.go +++ b/base/commands/map/map_key_set.go @@ -10,6 +10,6 @@ import ( ) func init() { - c := commands.NewMapKeySetCommand("Map", codec.EncodeMapKeySetRequest, codec.DecodeMapKeySetResponse) + c := commands.NewMapKeySetCommand("Map", codec.EncodeMapKeySetRequest, codec.DecodeMapKeySetResponse, getMap) check.Must(plug.Registry.RegisterCommand("map:key-set", c)) } diff --git a/base/commands/map/map_load_all.go b/base/commands/map/map_load_all.go index 41c9ca06..a3a05911 100644 --- a/base/commands/map/map_load_all.go +++ b/base/commands/map/map_load_all.go @@ -35,6 +35,8 @@ If no key is given, all keys are loaded.` func (MapLoadAllCommand) Exec(ctx context.Context, ec plug.ExecContext) error { name := ec.Props().GetString(base.FlagName) _, stop, err := ec.ExecuteBlocking(ctx, func(ctx context.Context, sp clc.Spinner) (any, error) { + replace := ec.Props().GetBool(mapFlagReplace) + // TODO: use Map.LoadAllX methods in the Go client when they are fixed. --YT ci, err := cmd.ClientInternal(ctx, ec, sp) if err != nil { return nil, err @@ -47,7 +49,6 @@ func (MapLoadAllCommand) Exec(ctx context.Context, ec plug.ExecContext) error { } keys = append(keys, keyData) } - replace := ec.Props().GetBool(mapFlagReplace) var req *hazelcast.ClientMessage if len(keys) == 0 { req = codec.EncodeMapLoadAllRequest(name, replace) diff --git a/base/commands/map/map_size.go b/base/commands/map/map_size.go index 26c822ec..d933f3f3 100644 --- a/base/commands/map/map_size.go +++ b/base/commands/map/map_size.go @@ -9,6 +9,6 @@ import ( ) func init() { - cmd := commands.NewSizeCommand("Map", getMap) - check.Must(plug.Registry.RegisterCommand("map:size", cmd)) + c := commands.NewSizeCommand("Map", getMap) + check.Must(plug.Registry.RegisterCommand("map:size", c)) } diff --git a/base/commands/map/map_values.go b/base/commands/map/map_values.go index b0bd093c..59639160 100644 --- a/base/commands/map/map_values.go +++ b/base/commands/map/map_values.go @@ -10,6 +10,6 @@ import ( ) func init() { - c := commands.NewMapValuesCommand("Map", codec.EncodeMapValuesRequest, codec.DecodeMapValuesResponse) + c := commands.NewMapValuesCommand("Map", codec.EncodeMapValuesRequest, codec.DecodeMapValuesResponse, getMap) check.Must(plug.Registry.RegisterCommand("map:values", c)) } diff --git a/base/commands/map_common.go b/base/commands/map_common.go index e8997e8c..9f4dde55 100644 --- a/base/commands/map_common.go +++ b/base/commands/map_common.go @@ -15,31 +15,35 @@ import ( "github.com/hazelcast/hazelcast-commandline-client/internal/serialization" ) +type mapPrefixFunc[T any] func(ctx context.Context, ec plug.ExecContext, sp clc.Spinner) (T, error) + type nameRequestEncodeFunc func(name string) *hazelcast.ClientMessage type pairsResponseDecodeFunc func(message *hazelcast.ClientMessage) []hazelcast.Pair -type MapEntrySetCommand struct { +type MapEntrySetCommand[T any] struct { typeName string encoder nameRequestEncodeFunc decoder pairsResponseDecodeFunc + prefixer mapPrefixFunc[T] } -func NewMapEntrySetCommand(typeName string, encoder nameRequestEncodeFunc, decoder pairsResponseDecodeFunc) *MapEntrySetCommand { - return &MapEntrySetCommand{ +func NewMapEntrySetCommand[T any](typeName string, encoder nameRequestEncodeFunc, decoder pairsResponseDecodeFunc, prefixer mapPrefixFunc[T]) *MapEntrySetCommand[T] { + return &MapEntrySetCommand[T]{ typeName: typeName, encoder: encoder, decoder: decoder, + prefixer: prefixer, } } -func (cm MapEntrySetCommand) Init(cc plug.InitContext) error { +func (cm MapEntrySetCommand[T]) Init(cc plug.InitContext) error { cc.SetCommandUsage("entry-set") help := fmt.Sprintf("Get all entries of a %s", cm.typeName) cc.SetCommandHelp(help, help) return nil } -func (cm MapEntrySetCommand) Exec(ctx context.Context, ec plug.ExecContext) error { +func (cm MapEntrySetCommand[T]) Exec(ctx context.Context, ec plug.ExecContext) error { name := ec.Props().GetString(base.FlagName) showType := ec.Props().GetBool(base.FlagShowType) rowsV, stop, err := ec.ExecuteBlocking(ctx, func(ctx context.Context, sp clc.Spinner) (any, error) { @@ -47,6 +51,9 @@ func (cm MapEntrySetCommand) Exec(ctx context.Context, ec plug.ExecContext) erro if err != nil { return nil, err } + if _, err = cm.prefixer(ctx, ec, sp); err != nil { + return nil, err + } req := cm.encoder(name) sp.SetText(fmt.Sprintf("Getting entries of %s", name)) resp, err := ci.InvokeOnRandomTarget(ctx, req, nil) @@ -67,21 +74,23 @@ func (cm MapEntrySetCommand) Exec(ctx context.Context, ec plug.ExecContext) erro type getRequestEncodeFunc func(name string, keyData hazelcast.Data, threadID int64) *hazelcast.ClientMessage type getResponseDecodeFunc func(ctx context.Context, ec plug.ExecContext, res *hazelcast.ClientMessage) ([]output.Row, error) -type MapGetCommand struct { +type MapGetCommand[T any] struct { typeName string encoder getRequestEncodeFunc decoder getResponseDecodeFunc + prefixer mapPrefixFunc[T] } -func NewMapGetCommand(typeName string, encoder getRequestEncodeFunc, decoder getResponseDecodeFunc) *MapGetCommand { - return &MapGetCommand{ +func NewMapGetCommand[T any](typeName string, encoder getRequestEncodeFunc, decoder getResponseDecodeFunc, prefixer mapPrefixFunc[T]) *MapGetCommand[T] { + return &MapGetCommand[T]{ typeName: typeName, encoder: encoder, decoder: decoder, + prefixer: prefixer, } } -func (cm MapGetCommand) Init(cc plug.InitContext) error { +func (cm MapGetCommand[T]) Init(cc plug.InitContext) error { cc.SetCommandUsage("get") AddKeyTypeFlag(cc) help := fmt.Sprintf("Get a value from the given %s", cm.typeName) @@ -90,7 +99,7 @@ func (cm MapGetCommand) Init(cc plug.InitContext) error { return nil } -func (cm MapGetCommand) Exec(ctx context.Context, ec plug.ExecContext) error { +func (cm MapGetCommand[T]) Exec(ctx context.Context, ec plug.ExecContext) error { name := ec.Props().GetString(base.FlagName) keyStr := ec.GetStringArg(ArgKey) rowsV, stop, err := ec.ExecuteBlocking(ctx, func(ctx context.Context, sp clc.Spinner) (any, error) { @@ -98,6 +107,9 @@ func (cm MapGetCommand) Exec(ctx context.Context, ec plug.ExecContext) error { if err != nil { return nil, err } + if _, err = cm.prefixer(ctx, ec, sp); err != nil { + return nil, err + } sp.SetText(fmt.Sprintf("Getting from %s '%s'", cm.typeName, name)) keyData, err := MakeKeyData(ec, ci, keyStr) if err != nil { @@ -124,28 +136,30 @@ func (cm MapGetCommand) Exec(ctx context.Context, ec plug.ExecContext) error { type dataSliceDecoderFunc func(message *hazelcast.ClientMessage) []*hazelcast.Data -type MapKeySetCommand struct { +type MapKeySetCommand[T any] struct { typeName string encoder nameRequestEncodeFunc decoder dataSliceDecoderFunc + prefixer mapPrefixFunc[T] } -func NewMapKeySetCommand(typeName string, encoder nameRequestEncodeFunc, decoder dataSliceDecoderFunc) *MapKeySetCommand { - return &MapKeySetCommand{ +func NewMapKeySetCommand[T any](typeName string, encoder nameRequestEncodeFunc, decoder dataSliceDecoderFunc, prefixer mapPrefixFunc[T]) *MapKeySetCommand[T] { + return &MapKeySetCommand[T]{ typeName: typeName, encoder: encoder, decoder: decoder, + prefixer: prefixer, } } -func (cm MapKeySetCommand) Init(cc plug.InitContext) error { +func (cm MapKeySetCommand[T]) Init(cc plug.InitContext) error { cc.SetCommandUsage("key-set") help := fmt.Sprintf("Get all keys of a %s", cm.typeName) cc.SetCommandHelp(help, help) return nil } -func (cm MapKeySetCommand) Exec(ctx context.Context, ec plug.ExecContext) error { +func (cm MapKeySetCommand[T]) Exec(ctx context.Context, ec plug.ExecContext) error { name := ec.Props().GetString(base.FlagName) showType := ec.Props().GetBool(base.FlagShowType) rowsV, stop, err := ec.ExecuteBlocking(ctx, func(ctx context.Context, sp clc.Spinner) (any, error) { @@ -153,6 +167,9 @@ func (cm MapKeySetCommand) Exec(ctx context.Context, ec plug.ExecContext) error if err != nil { return nil, err } + if _, err = cm.prefixer(ctx, ec, sp); err != nil { + return nil, err + } req := cm.encoder(name) sp.SetText(fmt.Sprintf("Getting keys of %s '%s'", cm.typeName, name)) resp, err := ci.InvokeOnRandomTarget(ctx, req, nil) @@ -270,7 +287,7 @@ This command is only available in the interactive mode.`, cm.typeName) func (cm LockCommand[T]) Exec(ctx context.Context, ec plug.ExecContext) error { name := ec.Props().GetString(base.FlagName) _, stop, err := ec.ExecuteBlocking(ctx, func(ctx context.Context, sp clc.Spinner) (any, error) { - ci, err := cmd.ClientInternal(ctx, ec, sp) + key, err := MakeValueFromString(ec.GetStringArg(ArgKey), ec.Props().GetString(FlagKeyType)) if err != nil { return nil, err } @@ -278,16 +295,11 @@ func (cm LockCommand[T]) Exec(ctx context.Context, ec plug.ExecContext) error { if err != nil { return nil, err } - keyStr := ec.GetStringArg(ArgKey) - keyData, err := MakeKeyData(ec, ci, keyStr) - if err != nil { - return nil, err - } sp.SetText(fmt.Sprintf("Locking the key in %s '%s'", cm.typeName, name)) if ttl := GetTTL(ec); ttl != clc.TTLUnset { - return nil, m.LockWithLease(ctx, keyData, time.Duration(GetTTL(ec))) + return nil, m.LockWithLease(ctx, key, time.Duration(GetTTL(ec))) } - return nil, m.Lock(ctx, keyData) + return nil, m.Lock(ctx, key) }) if err != nil { return err @@ -335,25 +347,20 @@ This command is only available in the interactive mode.`, cm.typeName) func (cm MapTryLockCommand[T]) Exec(ctx context.Context, ec plug.ExecContext) error { rowsV, stop, err := ec.ExecuteBlocking(ctx, func(ctx context.Context, sp clc.Spinner) (any, error) { mapName := ec.Props().GetString(base.FlagName) - ci, err := cmd.ClientInternal(ctx, ec, sp) - if err != nil { - return nil, err - } sp.SetText(fmt.Sprintf("Locking key in map %s", mapName)) m, err := cm.getFn(ctx, ec, sp) if err != nil { return nil, err } - keyStr := ec.GetStringArg(ArgKey) - keyData, err := MakeKeyData(ec, ci, keyStr) + key, err := MakeValueFromString(ec.GetStringArg(ArgKey), ec.Props().GetString(FlagKeyType)) if err != nil { return nil, err } var locked bool if ttl := GetTTL(ec); ttl != clc.TTLUnset { - locked, err = m.TryLockWithLease(ctx, keyData, time.Duration(GetTTL(ec))) + locked, err = m.TryLockWithLease(ctx, key, time.Duration(GetTTL(ec))) } else { - locked, err = m.TryLock(ctx, keyData) + locked, err = m.TryLock(ctx, key) } row := output.Row{ { @@ -411,21 +418,13 @@ This command is only available in the interactive mode.`, cm.typeName) func (cm MapUnlockCommand[T]) Exec(ctx context.Context, ec plug.ExecContext) error { name := ec.Props().GetString(base.FlagName) _, stop, err := ec.ExecuteBlocking(ctx, func(ctx context.Context, sp clc.Spinner) (any, error) { - ci, err := cmd.ClientInternal(ctx, ec, sp) - if err != nil { - return nil, err - } + key, err := MakeValueFromString(ec.GetStringArg(ArgKey), ec.Props().GetString(FlagKeyType)) sp.SetText(fmt.Sprintf("Unlocking key in %s '%s'", cm.typeName, name)) m, err := cm.getFn(ctx, ec, sp) if err != nil { return nil, err } - keyStr := ec.GetStringArg(ArgKey) - keyData, err := MakeKeyData(ec, ci, keyStr) - if err != nil { - return nil, err - } - return nil, m.Unlock(ctx, keyData) + return nil, m.Unlock(ctx, key) }) if err != nil { return err @@ -436,28 +435,30 @@ func (cm MapUnlockCommand[T]) Exec(ctx context.Context, ec plug.ExecContext) err return nil } -type MapValuesCommand struct { +type MapValuesCommand[T any] struct { typeName string encoder nameRequestEncodeFunc decoder dataSliceDecoderFunc + prefixer mapPrefixFunc[T] } -func NewMapValuesCommand(typeName string, encoder nameRequestEncodeFunc, decoder dataSliceDecoderFunc) *MapValuesCommand { - return &MapValuesCommand{ +func NewMapValuesCommand[T any](typeName string, encoder nameRequestEncodeFunc, decoder dataSliceDecoderFunc, prefixer mapPrefixFunc[T]) *MapValuesCommand[T] { + return &MapValuesCommand[T]{ typeName: typeName, encoder: encoder, decoder: decoder, + prefixer: prefixer, } } -func (cm MapValuesCommand) Init(cc plug.InitContext) error { +func (cm MapValuesCommand[T]) Init(cc plug.InitContext) error { cc.SetCommandUsage("values") help := fmt.Sprintf("Get all values of a %s", cm.typeName) cc.SetCommandHelp(help, help) return nil } -func (cm *MapValuesCommand) Exec(ctx context.Context, ec plug.ExecContext) error { +func (cm *MapValuesCommand[T]) Exec(ctx context.Context, ec plug.ExecContext) error { name := ec.Props().GetString(base.FlagName) showType := ec.Props().GetBool(base.FlagShowType) rowsV, stop, err := ec.ExecuteBlocking(ctx, func(ctx context.Context, sp clc.Spinner) (any, error) { @@ -465,6 +466,9 @@ func (cm *MapValuesCommand) Exec(ctx context.Context, ec plug.ExecContext) error if err != nil { return nil, err } + if _, err := cm.prefixer(ctx, ec, sp); err != nil { + return nil, err + } sp.SetText(fmt.Sprintf("Getting values of %s", name)) req := cm.encoder(name) resp, err := ci.InvokeOnRandomTarget(ctx, req, nil) diff --git a/base/commands/multimap/multimap_entry_set.go b/base/commands/multimap/multimap_entry_set.go index b919d885..a943bb5b 100644 --- a/base/commands/multimap/multimap_entry_set.go +++ b/base/commands/multimap/multimap_entry_set.go @@ -10,6 +10,6 @@ import ( ) func init() { - c := commands.NewMapEntrySetCommand("MultiMap", codec.EncodeMultiMapEntrySetRequest, codec.DecodeMultiMapEntrySetResponse) + c := commands.NewMapEntrySetCommand("MultiMap", codec.EncodeMultiMapEntrySetRequest, codec.DecodeMultiMapEntrySetResponse, getMultiMap) check.Must(plug.Registry.RegisterCommand("multi-map:entry-set", c)) } diff --git a/base/commands/multimap/multimap_get.go b/base/commands/multimap/multimap_get.go index 46b7acb5..0a1f950f 100644 --- a/base/commands/multimap/multimap_get.go +++ b/base/commands/multimap/multimap_get.go @@ -10,6 +10,7 @@ import ( ) func init() { - c := commands.NewMapGetCommand("MultiMap", codec.EncodeMultiMapGetRequest, makeDecodeResponseRowsFunc(codec.DecodeMultiMapGetResponse)) + d := makeDecodeResponseRowsFunc(codec.DecodeMultiMapGetResponse) + c := commands.NewMapGetCommand("MultiMap", codec.EncodeMultiMapGetRequest, d, getMultiMap) check.Must(plug.Registry.RegisterCommand("multi-map:get", c)) } diff --git a/base/commands/multimap/multimap_key_set.go b/base/commands/multimap/multimap_key_set.go index 659dbb4e..4f1eab86 100644 --- a/base/commands/multimap/multimap_key_set.go +++ b/base/commands/multimap/multimap_key_set.go @@ -10,6 +10,6 @@ import ( ) func init() { - c := commands.NewMapKeySetCommand("MultiMap", codec.EncodeMultiMapKeySetRequest, codec.DecodeMultiMapKeySetResponse) + c := commands.NewMapKeySetCommand("MultiMap", codec.EncodeMultiMapKeySetRequest, codec.DecodeMultiMapKeySetResponse, getMultiMap) check.Must(plug.Registry.RegisterCommand("multi-map:key-set", c)) } diff --git a/base/commands/multimap/multimap_values.go b/base/commands/multimap/multimap_values.go index c3b370e8..7da9b124 100644 --- a/base/commands/multimap/multimap_values.go +++ b/base/commands/multimap/multimap_values.go @@ -10,6 +10,6 @@ import ( ) func init() { - c := commands.NewMapValuesCommand("MultiMap", codec.EncodeMultiMapValuesRequest, codec.DecodeMultiMapValuesResponse) + c := commands.NewMapValuesCommand("MultiMap", codec.EncodeMultiMapValuesRequest, codec.DecodeMultiMapValuesResponse, getMultiMap) check.Must(plug.Registry.RegisterCommand("multi-map:values", c)) }