mirror of
https://github.com/hanzokv/go.git
synced 2026-09-24 00:31:51 +00:00
feat(command): Add AR* (array data structure) commands (#3813)
* feat: impl array data structure * add integration tests for array commands Co-Authored-By: Claude Opus 4.7 <[email protected]> * fix: move CmdTypeUint and CmdTypeUintSlice to end of iota sequence Co-Authored-By: Claude Opus 4.7 <[email protected]> * test: add boundary tests for large uint64 index values Covers ARSet, ARGet, ARGetRange, ARMSet, ARMGet, ARDel, ARDelRange, ARScan, ARSeek, ARNext, and AROp. Co-Authored-By: Claude Opus 4.7 <[email protected]> * refactor: replace generic AROp with typed methods and improve test coverage - Split AROp into specific methods: AROpSum, AROpMin, AROpMax, AROpAnd, AROpOr, AROpXor, AROpMatch, AROpUsed with proper return types - Pre-allocate slices in ARScan, ARGrep, ARGrepWithValues, ARLastItems - Add tests: ARInfoFull, negative AND/OR/XOR results, ARGrep nil args Co-Authored-By: Claude Opus 4.7 <[email protected]> * mark ArrayCmdable API as experimental Co-Authored-By: Claude Opus 4.7 <[email protected]> * Use `any` instead of `interface{}` and document index range constraint on ArrayCmdable interface. Co-Authored-By: Claude Opus 4.7 <[email protected]> --------- Co-authored-by: Claude Opus 4.7 <[email protected]>
This commit is contained in:
@@ -0,0 +1,387 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// note: the APIs is experimental and may be subject to change.
|
||||
//
|
||||
// ArrayCmdable defines the interface for Redis Array data structure commands
|
||||
// available in Redis 8.8.0+.
|
||||
//
|
||||
// Redis array supports index range [0, math.MaxUint64-1), so index parameters use uint64.
|
||||
type ArrayCmdable interface {
|
||||
ARSet(ctx context.Context, key string, index uint64, values ...string) *IntCmd
|
||||
ARGet(ctx context.Context, key string, index uint64) *StringCmd
|
||||
ARGetRange(ctx context.Context, key string, start, end uint64) *SliceCmd
|
||||
ARMGet(ctx context.Context, key string, indexes ...uint64) *SliceCmd
|
||||
ARMSet(ctx context.Context, key string, members ...AREntry) *IntCmd
|
||||
ARInsert(ctx context.Context, key string, values ...string) *UintCmd
|
||||
ARDel(ctx context.Context, key string, indexes ...uint64) *IntCmd
|
||||
ARDelRange(ctx context.Context, key string, ranges ...ARRange) *UintCmd
|
||||
ARLen(ctx context.Context, key string) *UintCmd
|
||||
ARCount(ctx context.Context, key string) *UintCmd
|
||||
ARNext(ctx context.Context, key string) *UintCmd
|
||||
ARSeek(ctx context.Context, key string, index uint64) *IntCmd
|
||||
ARInfo(ctx context.Context, key string) *MapStringInterfaceCmd
|
||||
ARInfoFull(ctx context.Context, key string) *MapStringInterfaceCmd
|
||||
ARScan(ctx context.Context, key string, start, end uint64, args *ARScanArgs) *AREntrySliceCmd
|
||||
AROpSum(ctx context.Context, key string, start, end uint64) *StringCmd
|
||||
AROpMin(ctx context.Context, key string, start, end uint64) *StringCmd
|
||||
AROpMax(ctx context.Context, key string, start, end uint64) *StringCmd
|
||||
AROpAnd(ctx context.Context, key string, start, end uint64) *IntCmd
|
||||
AROpOr(ctx context.Context, key string, start, end uint64) *IntCmd
|
||||
AROpXor(ctx context.Context, key string, start, end uint64) *IntCmd
|
||||
AROpMatch(ctx context.Context, key string, start, end uint64, value string) *IntCmd
|
||||
AROpUsed(ctx context.Context, key string, start, end uint64) *IntCmd
|
||||
ARGrep(ctx context.Context, key string, start, end string, args *ARGrepArgs) *UintSliceCmd
|
||||
ARGrepWithValues(ctx context.Context, key string, start, end string, args *ARGrepArgs) *AREntrySliceCmd
|
||||
ARRing(ctx context.Context, key string, size uint64, values ...string) *UintCmd
|
||||
ARLastItems(ctx context.Context, key string, count uint64, rev bool) *SliceCmd
|
||||
}
|
||||
|
||||
// AREntry represents an index-value pair for ARMSET.
|
||||
type AREntry struct {
|
||||
Index uint64
|
||||
Value string
|
||||
}
|
||||
|
||||
// ARRange represents a start-end range for ARDELRANGE.
|
||||
type ARRange struct {
|
||||
Start uint64
|
||||
End uint64
|
||||
}
|
||||
|
||||
// ARScanArgs contains optional arguments for ARSCAN.
|
||||
type ARScanArgs struct {
|
||||
Limit uint64
|
||||
}
|
||||
|
||||
// ARGrepPredicateType defines the type of predicate for ARGREP.
|
||||
type ARGrepPredicateType string
|
||||
|
||||
const (
|
||||
ARGrepExact ARGrepPredicateType = "EXACT"
|
||||
ARGrepMatch ARGrepPredicateType = "MATCH"
|
||||
ARGrepGlob ARGrepPredicateType = "GLOB"
|
||||
ARGrepRegex ARGrepPredicateType = "RE"
|
||||
)
|
||||
|
||||
// ARGrepPredicate represents a search predicate for ARGREP.
|
||||
type ARGrepPredicate struct {
|
||||
Type ARGrepPredicateType
|
||||
Value string
|
||||
}
|
||||
|
||||
// ARGrepArgs contains optional arguments for ARGREP.
|
||||
// Redis ARGREP defaults to OR when multiple predicates are given.
|
||||
// Set CombineAnd to true to combine predicates with AND instead.
|
||||
type ARGrepArgs struct {
|
||||
Predicates []ARGrepPredicate
|
||||
CombineAnd bool
|
||||
Limit uint64
|
||||
NoCase bool
|
||||
}
|
||||
|
||||
// ARSet sets one or more contiguous values starting at an index in an array.
|
||||
// Returns the number of new slots that were set (previously empty).
|
||||
func (c cmdable) ARSet(ctx context.Context, key string, index uint64, values ...string) *IntCmd {
|
||||
args := make([]any, 3, 3+len(values))
|
||||
args[0] = "arset"
|
||||
args[1] = key
|
||||
args[2] = index
|
||||
for _, v := range values {
|
||||
args = append(args, v)
|
||||
}
|
||||
cmd := NewIntCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARGet gets the value at an index in an array.
|
||||
// Returns redis.Nil if the key or index does not exist.
|
||||
func (c cmdable) ARGet(ctx context.Context, key string, index uint64) *StringCmd {
|
||||
cmd := NewStringCmd(ctx, "arget", key, index)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARGetRange gets values in a range of indexes.
|
||||
// Returns values in the range, with nil for unset indexes.
|
||||
func (c cmdable) ARGetRange(ctx context.Context, key string, start, end uint64) *SliceCmd {
|
||||
cmd := NewSliceCmd(ctx, "argetrange", key, start, end)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARMGet gets values at multiple indexes in an array.
|
||||
// Returns values at the specified indexes, with nil for unset indexes.
|
||||
func (c cmdable) ARMGet(ctx context.Context, key string, indexes ...uint64) *SliceCmd {
|
||||
args := make([]any, 2+len(indexes))
|
||||
args[0] = "armget"
|
||||
args[1] = key
|
||||
for i, idx := range indexes {
|
||||
args[2+i] = idx
|
||||
}
|
||||
cmd := NewSliceCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARMSet sets multiple index-value pairs in an array.
|
||||
// Returns the number of new slots that were set (previously empty).
|
||||
func (c cmdable) ARMSet(ctx context.Context, key string, members ...AREntry) *IntCmd {
|
||||
args := make([]any, 2, 2+2*len(members))
|
||||
args[0] = "armset"
|
||||
args[1] = key
|
||||
for _, m := range members {
|
||||
args = append(args, m.Index, m.Value)
|
||||
}
|
||||
cmd := NewIntCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARInsert inserts one or more values at consecutive indexes.
|
||||
// Returns the last index where a value was inserted.
|
||||
func (c cmdable) ARInsert(ctx context.Context, key string, values ...string) *UintCmd {
|
||||
args := make([]any, 2, 2+len(values))
|
||||
args[0] = "arinsert"
|
||||
args[1] = key
|
||||
for _, v := range values {
|
||||
args = append(args, v)
|
||||
}
|
||||
cmd := NewUintCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARDel deletes elements at the specified indexes in an array.
|
||||
// Returns the number of elements deleted.
|
||||
func (c cmdable) ARDel(ctx context.Context, key string, indexes ...uint64) *IntCmd {
|
||||
args := make([]any, 2+len(indexes))
|
||||
args[0] = "ardel"
|
||||
args[1] = key
|
||||
for i, idx := range indexes {
|
||||
args[2+i] = idx
|
||||
}
|
||||
cmd := NewIntCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARDelRange deletes elements in one or more ranges.
|
||||
// Returns the number of elements deleted.
|
||||
func (c cmdable) ARDelRange(ctx context.Context, key string, ranges ...ARRange) *UintCmd {
|
||||
args := make([]any, 2, 2+2*len(ranges))
|
||||
args[0] = "ardelrange"
|
||||
args[1] = key
|
||||
for _, r := range ranges {
|
||||
args = append(args, r.Start, r.End)
|
||||
}
|
||||
cmd := NewUintCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARLen returns the length of an array (max index + 1).
|
||||
// Returns 0 if the key does not exist.
|
||||
func (c cmdable) ARLen(ctx context.Context, key string) *UintCmd {
|
||||
cmd := NewUintCmd(ctx, "arlen", key)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARCount returns the number of non-empty elements in an array.
|
||||
// Returns 0 if the key does not exist.
|
||||
func (c cmdable) ARCount(ctx context.Context, key string) *UintCmd {
|
||||
cmd := NewUintCmd(ctx, "arcount", key)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARNext returns the next index ARINSERT would use.
|
||||
// Returns 0 for missing keys or when no insert happened yet.
|
||||
// Returns nil when the insertion cursor is exhausted / would overflow.
|
||||
func (c cmdable) ARNext(ctx context.Context, key string) *UintCmd {
|
||||
cmd := NewUintCmd(ctx, "arnext", key)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARSeek sets the ARINSERT / ARRING cursor to a specific index.
|
||||
// Returns 1 if the cursor was set, 0 if the key does not exist.
|
||||
func (c cmdable) ARSeek(ctx context.Context, key string, index uint64) *IntCmd {
|
||||
cmd := NewIntCmd(ctx, "arseek", key, index)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARInfo returns metadata about an array.
|
||||
func (c cmdable) ARInfo(ctx context.Context, key string) *MapStringInterfaceCmd {
|
||||
cmd := NewMapStringInterfaceCmd(ctx, "arinfo", key)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARInfoFull returns detailed metadata about an array including slice statistics.
|
||||
func (c cmdable) ARInfoFull(ctx context.Context, key string) *MapStringInterfaceCmd {
|
||||
cmd := NewMapStringInterfaceCmd(ctx, "arinfo", key, "full")
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARScan iterates existing elements in a range, returning index-value pairs.
|
||||
func (c cmdable) ARScan(ctx context.Context, key string, start, end uint64, scanArgs *ARScanArgs) *AREntrySliceCmd {
|
||||
args := make([]any, 4, 6)
|
||||
args[0], args[1], args[2], args[3] = "arscan", key, start, end
|
||||
if scanArgs != nil && scanArgs.Limit > 0 {
|
||||
args = append(args, "limit", scanArgs.Limit)
|
||||
}
|
||||
cmd := NewAREntrySliceCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// AROpSum returns the sum of numeric elements in a range.
|
||||
func (c cmdable) AROpSum(ctx context.Context, key string, start, end uint64) *StringCmd {
|
||||
cmd := NewStringCmd(ctx, "arop", key, start, end, "SUM")
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// AROpMin returns the minimum numeric element in a range.
|
||||
func (c cmdable) AROpMin(ctx context.Context, key string, start, end uint64) *StringCmd {
|
||||
cmd := NewStringCmd(ctx, "arop", key, start, end, "MIN")
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// AROpMax returns the maximum numeric element in a range.
|
||||
func (c cmdable) AROpMax(ctx context.Context, key string, start, end uint64) *StringCmd {
|
||||
cmd := NewStringCmd(ctx, "arop", key, start, end, "MAX")
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// AROpAnd returns the bitwise AND of integer elements in a range.
|
||||
func (c cmdable) AROpAnd(ctx context.Context, key string, start, end uint64) *IntCmd {
|
||||
cmd := NewIntCmd(ctx, "arop", key, start, end, "AND")
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// AROpOr returns the bitwise OR of integer elements in a range.
|
||||
func (c cmdable) AROpOr(ctx context.Context, key string, start, end uint64) *IntCmd {
|
||||
cmd := NewIntCmd(ctx, "arop", key, start, end, "OR")
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// AROpXor returns the bitwise XOR of integer elements in a range.
|
||||
func (c cmdable) AROpXor(ctx context.Context, key string, start, end uint64) *IntCmd {
|
||||
cmd := NewIntCmd(ctx, "arop", key, start, end, "XOR")
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// AROpMatch returns the count of elements matching a target string in a range.
|
||||
func (c cmdable) AROpMatch(ctx context.Context, key string, start, end uint64, value string) *IntCmd {
|
||||
cmd := NewIntCmd(ctx, "arop", key, start, end, "MATCH", value)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// AROpUsed returns the count of non-empty slots in a range.
|
||||
func (c cmdable) AROpUsed(ctx context.Context, key string, start, end uint64) *IntCmd {
|
||||
cmd := NewIntCmd(ctx, "arop", key, start, end, "USED")
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARGrep searches array elements in a range using textual predicates.
|
||||
// Returns matching indexes only. Use ARGrepWithValues to also get the values.
|
||||
func (c cmdable) ARGrep(ctx context.Context, key string, start, end string, grepArgs *ARGrepArgs) *UintSliceCmd {
|
||||
args := make([]any, 4, 4+grepArgs.Len())
|
||||
args[0], args[1], args[2], args[3] = "argrep", key, start, end
|
||||
args = grepArgs.Append(args)
|
||||
cmd := NewUintSliceCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARGrepWithValues searches array elements in a range using textual predicates.
|
||||
// Returns matching indexes and their values as index-value pairs.
|
||||
func (c cmdable) ARGrepWithValues(ctx context.Context, key string, start, end string, grepArgs *ARGrepArgs) *AREntrySliceCmd {
|
||||
args := make([]any, 4, 5+grepArgs.Len())
|
||||
args[0], args[1], args[2], args[3] = "argrep", key, start, end
|
||||
args = grepArgs.Append(args)
|
||||
args = append(args, "withvalues")
|
||||
cmd := NewAREntrySliceCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (args *ARGrepArgs) Len() int {
|
||||
if args == nil {
|
||||
return 0
|
||||
}
|
||||
n := 2 * len(args.Predicates)
|
||||
if args.CombineAnd {
|
||||
n++
|
||||
}
|
||||
if args.Limit > 0 {
|
||||
n += 2
|
||||
}
|
||||
if args.NoCase {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (args *ARGrepArgs) Append(a []any) []any {
|
||||
if args == nil {
|
||||
return a
|
||||
}
|
||||
for _, p := range args.Predicates {
|
||||
a = append(a, string(p.Type), p.Value)
|
||||
}
|
||||
if args.CombineAnd {
|
||||
a = append(a, "and")
|
||||
}
|
||||
if args.Limit > 0 {
|
||||
a = append(a, "limit", args.Limit)
|
||||
}
|
||||
if args.NoCase {
|
||||
a = append(a, "nocase")
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// ARRing inserts values into a ring buffer of specified size, wrapping and truncating as needed.
|
||||
// Returns the last index where a value was inserted.
|
||||
func (c cmdable) ARRing(ctx context.Context, key string, size uint64, values ...string) *UintCmd {
|
||||
args := make([]any, 3, 3+len(values))
|
||||
args[0] = "arring"
|
||||
args[1] = key
|
||||
args[2] = size
|
||||
for _, v := range values {
|
||||
args = append(args, v)
|
||||
}
|
||||
cmd := NewUintCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ARLastItems returns the most recently inserted elements.
|
||||
// When rev is true, returns items in reverse order.
|
||||
func (c cmdable) ARLastItems(ctx context.Context, key string, count uint64, rev bool) *SliceCmd {
|
||||
args := make([]any, 3, 4)
|
||||
args[0], args[1], args[2] = "arlastitems", key, count
|
||||
if rev {
|
||||
args = append(args, "rev")
|
||||
}
|
||||
cmd := NewSliceCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+207
@@ -158,6 +158,9 @@ const (
|
||||
CmdTypeHotKeys
|
||||
CmdTypeIncrEXInt
|
||||
CmdTypeIncrEXFloat
|
||||
CmdTypeUint
|
||||
CmdTypeUintSlice
|
||||
CmdTypeAREntrySlice
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -1046,6 +1049,52 @@ func (cmd *IntCmd) Clone() Cmder {
|
||||
}
|
||||
}
|
||||
|
||||
type UintCmd struct {
|
||||
baseCmd
|
||||
|
||||
val uint64
|
||||
}
|
||||
|
||||
var _ Cmder = (*UintCmd)(nil)
|
||||
|
||||
func NewUintCmd(ctx context.Context, args ...any) *UintCmd {
|
||||
return &UintCmd{
|
||||
baseCmd: baseCmd{
|
||||
ctx: ctx,
|
||||
args: args,
|
||||
cmdType: CmdTypeUint,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *UintCmd) SetVal(val uint64) {
|
||||
cmd.val = val
|
||||
}
|
||||
|
||||
func (cmd *UintCmd) Val() uint64 {
|
||||
return cmd.val
|
||||
}
|
||||
|
||||
func (cmd *UintCmd) Result() (uint64, error) {
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *UintCmd) String() string {
|
||||
return cmdString(cmd, cmd.val)
|
||||
}
|
||||
|
||||
func (cmd *UintCmd) readReply(rd *proto.Reader) (err error) {
|
||||
cmd.val, err = rd.ReadUint()
|
||||
return err
|
||||
}
|
||||
|
||||
func (cmd *UintCmd) Clone() Cmder {
|
||||
return &UintCmd{
|
||||
baseCmd: cmd.cloneBaseCmd(),
|
||||
val: cmd.val,
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// DigestCmd is a command that returns a uint64 xxh3 hash digest.
|
||||
@@ -1177,6 +1226,66 @@ func (cmd *IntSliceCmd) Clone() Cmder {
|
||||
}
|
||||
}
|
||||
|
||||
type UintSliceCmd struct {
|
||||
baseCmd
|
||||
|
||||
val []uint64
|
||||
}
|
||||
|
||||
var _ Cmder = (*UintSliceCmd)(nil)
|
||||
|
||||
func NewUintSliceCmd(ctx context.Context, args ...any) *UintSliceCmd {
|
||||
return &UintSliceCmd{
|
||||
baseCmd: baseCmd{
|
||||
ctx: ctx,
|
||||
args: args,
|
||||
cmdType: CmdTypeUintSlice,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *UintSliceCmd) SetVal(val []uint64) {
|
||||
cmd.val = val
|
||||
}
|
||||
|
||||
func (cmd *UintSliceCmd) Val() []uint64 {
|
||||
return cmd.val
|
||||
}
|
||||
|
||||
func (cmd *UintSliceCmd) Result() ([]uint64, error) {
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *UintSliceCmd) String() string {
|
||||
return cmdString(cmd, cmd.val)
|
||||
}
|
||||
|
||||
func (cmd *UintSliceCmd) readReply(rd *proto.Reader) error {
|
||||
n, err := rd.ReadArrayLen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.val = make([]uint64, n)
|
||||
for i := range cmd.val {
|
||||
if cmd.val[i], err = rd.ReadUint(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *UintSliceCmd) Clone() Cmder {
|
||||
var val []uint64
|
||||
if cmd.val != nil {
|
||||
val = make([]uint64, len(cmd.val))
|
||||
copy(val, cmd.val)
|
||||
}
|
||||
return &UintSliceCmd{
|
||||
baseCmd: cmd.cloneBaseCmd(),
|
||||
val: val,
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type DurationCmd struct {
|
||||
@@ -8070,6 +8179,13 @@ func ExtractCommandValue(cmd interface{}) (interface{}, error) {
|
||||
}); ok {
|
||||
return intCmd.Val(), intCmd.Err()
|
||||
}
|
||||
case CmdTypeUint:
|
||||
if uintCmd, ok := cmd.(interface {
|
||||
Val() uint64
|
||||
Err() error
|
||||
}); ok {
|
||||
return uintCmd.Val(), uintCmd.Err()
|
||||
}
|
||||
case CmdTypeBool:
|
||||
if boolCmd, ok := cmd.(interface {
|
||||
Val() bool
|
||||
@@ -8496,6 +8612,13 @@ func ExtractCommandValue(cmd interface{}) (interface{}, error) {
|
||||
}); ok {
|
||||
return intSliceCmd.Val(), intSliceCmd.Err()
|
||||
}
|
||||
case CmdTypeUintSlice:
|
||||
if uintSliceCmd, ok := cmd.(interface {
|
||||
Val() []uint64
|
||||
Err() error
|
||||
}); ok {
|
||||
return uintSliceCmd.Val(), uintSliceCmd.Err()
|
||||
}
|
||||
case CmdTypeBoolSlice:
|
||||
if boolSliceCmd, ok := cmd.(interface {
|
||||
Val() []bool
|
||||
@@ -8524,6 +8647,13 @@ func ExtractCommandValue(cmd interface{}) (interface{}, error) {
|
||||
}); ok {
|
||||
return keyValueSliceCmd.Val(), keyValueSliceCmd.Err()
|
||||
}
|
||||
case CmdTypeAREntrySlice:
|
||||
if arEntrySliceCmd, ok := cmd.(interface {
|
||||
Val() []AREntry
|
||||
Err() error
|
||||
}); ok {
|
||||
return arEntrySliceCmd.Val(), arEntrySliceCmd.Err()
|
||||
}
|
||||
case CmdTypeMapStringString:
|
||||
if mapCmd, ok := cmd.(interface {
|
||||
Val() map[string]string
|
||||
@@ -8688,3 +8818,80 @@ func (cmd *IncrEXFloatCmd) Clone() Cmder {
|
||||
val: cmd.val,
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// AREntrySliceCmd is a command that returns index-value pairs from ARSCAN or ARGREP.
|
||||
type AREntrySliceCmd struct {
|
||||
baseCmd
|
||||
val []AREntry
|
||||
}
|
||||
|
||||
var _ Cmder = (*AREntrySliceCmd)(nil)
|
||||
|
||||
func NewAREntrySliceCmd(ctx context.Context, args ...any) *AREntrySliceCmd {
|
||||
return &AREntrySliceCmd{
|
||||
baseCmd: baseCmd{
|
||||
ctx: ctx,
|
||||
args: args,
|
||||
cmdType: CmdTypeAREntrySlice,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *AREntrySliceCmd) SetVal(val []AREntry) {
|
||||
cmd.val = val
|
||||
}
|
||||
|
||||
func (cmd *AREntrySliceCmd) Val() []AREntry {
|
||||
return cmd.val
|
||||
}
|
||||
|
||||
func (cmd *AREntrySliceCmd) Result() ([]AREntry, error) {
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *AREntrySliceCmd) String() string {
|
||||
return cmdString(cmd, cmd.val)
|
||||
}
|
||||
|
||||
func (cmd *AREntrySliceCmd) readReply(rd *proto.Reader) error {
|
||||
n, err := rd.ReadArrayLen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n == 0 {
|
||||
cmd.val = make([]AREntry, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.val = make([]AREntry, n)
|
||||
for i := range n {
|
||||
if err = rd.ReadFixedArrayLen(2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.val[i].Index, err = rd.ReadUint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.val[i].Value, err = rd.ReadString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *AREntrySliceCmd) Clone() Cmder {
|
||||
var val []AREntry
|
||||
if cmd.val != nil {
|
||||
val = make([]AREntry, len(cmd.val))
|
||||
copy(val, cmd.val)
|
||||
}
|
||||
return &AREntrySliceCmd{
|
||||
baseCmd: cmd.cloneBaseCmd(),
|
||||
val: val,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +228,7 @@ type Cmdable interface {
|
||||
ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd
|
||||
|
||||
ACLCmdable
|
||||
ArrayCmdable
|
||||
BitMapCmdable
|
||||
ClusterCmdable
|
||||
GenericCmdable
|
||||
|
||||
Reference in New Issue
Block a user