mirror of https://github.com/go-redis/redis.git
Merge pull request #830 from go-redis/feature/x-group
Add streams group related commands
This commit is contained in:
commit
ad7024da36
709
command.go
709
command.go
|
@ -3,6 +3,7 @@ package redis
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -181,6 +182,33 @@ func (cmd *Cmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func sliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
vals := make([]interface{}, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
v, err := rd.ReadReply(sliceParser)
|
||||
if err != nil {
|
||||
if err == Nil {
|
||||
vals = append(vals, nil)
|
||||
continue
|
||||
}
|
||||
if err, ok := err.(proto.RedisError); ok {
|
||||
vals = append(vals, err)
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch v := v.(type) {
|
||||
case []byte:
|
||||
vals = append(vals, string(v))
|
||||
default:
|
||||
vals = append(vals, v)
|
||||
}
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type SliceCmd struct {
|
||||
|
@ -363,6 +391,25 @@ func (cmd *TimeCmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func timeParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
if n != 2 {
|
||||
return nil, fmt.Errorf("got %d elements, expected 2", n)
|
||||
}
|
||||
|
||||
sec, err := rd.ReadInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
microsec, err := rd.ReadInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return time.Unix(sec, microsec*1000), nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type BoolCmd struct {
|
||||
|
@ -560,6 +607,22 @@ func (cmd *StringSliceCmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func stringSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
ss := make([]string, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
s, err := rd.ReadStringReply()
|
||||
if err == Nil {
|
||||
ss = append(ss, "")
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ss = append(ss, s)
|
||||
}
|
||||
}
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type BoolSliceCmd struct {
|
||||
|
@ -598,6 +661,19 @@ func (cmd *BoolSliceCmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func boolSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
bools := make([]bool, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
n, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bools = append(bools, n == 1)
|
||||
}
|
||||
return bools, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type StringStringMapCmd struct {
|
||||
|
@ -636,6 +712,25 @@ func (cmd *StringStringMapCmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func stringStringMapParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
m := make(map[string]string, n/2)
|
||||
for i := int64(0); i < n; i += 2 {
|
||||
key, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m[key] = value
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type StringIntMapCmd struct {
|
||||
|
@ -674,6 +769,25 @@ func (cmd *StringIntMapCmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func stringIntMapParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
m := make(map[string]int64, n/2)
|
||||
for i := int64(0); i < n; i += 2 {
|
||||
key, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m[key] = n
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type StringStructMapCmd struct {
|
||||
|
@ -712,24 +826,121 @@ func (cmd *StringStructMapCmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type XStream struct {
|
||||
Stream string
|
||||
Messages []*XMessage
|
||||
// Implements proto.MultiBulkParse
|
||||
func stringStructMapParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
m := make(map[string]struct{}, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
key, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m[key] = struct{}{}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type XMessage struct {
|
||||
ID string
|
||||
Values map[string]interface{}
|
||||
}
|
||||
|
||||
type XMessageSliceCmd struct {
|
||||
baseCmd
|
||||
|
||||
val []XMessage
|
||||
}
|
||||
|
||||
var _ Cmder = (*XMessageSliceCmd)(nil)
|
||||
|
||||
func NewXMessageSliceCmd(args ...interface{}) *XMessageSliceCmd {
|
||||
return &XMessageSliceCmd{
|
||||
baseCmd: baseCmd{_args: args},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *XMessageSliceCmd) Val() []XMessage {
|
||||
return cmd.val
|
||||
}
|
||||
|
||||
func (cmd *XMessageSliceCmd) Result() ([]XMessage, error) {
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *XMessageSliceCmd) String() string {
|
||||
return cmdString(cmd, cmd.val)
|
||||
}
|
||||
|
||||
func (cmd *XMessageSliceCmd) readReply(cn *pool.Conn) error {
|
||||
var v interface{}
|
||||
v, cmd.err = cn.Rd.ReadArrayReply(xMessageSliceParser)
|
||||
if cmd.err != nil {
|
||||
return cmd.err
|
||||
}
|
||||
cmd.val = v.([]XMessage)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func xMessageSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
msgs := make([]XMessage, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
id, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, err := rd.ReadArrayReply(stringInterfaceMapParser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msgs = append(msgs, XMessage{
|
||||
ID: id,
|
||||
Values: v.(map[string]interface{}),
|
||||
})
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return msgs, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func stringInterfaceMapParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
m := make(map[string]interface{}, n/2)
|
||||
for i := int64(0); i < n; i += 2 {
|
||||
key, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m[key] = value
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type XStream struct {
|
||||
Stream string
|
||||
Messages []XMessage
|
||||
}
|
||||
|
||||
type XStreamSliceCmd struct {
|
||||
baseCmd
|
||||
|
||||
val []*XStream
|
||||
val []XStream
|
||||
}
|
||||
|
||||
var _ Cmder = (*XStreamSliceCmd)(nil)
|
||||
|
@ -740,11 +951,11 @@ func NewXStreamSliceCmd(args ...interface{}) *XStreamSliceCmd {
|
|||
}
|
||||
}
|
||||
|
||||
func (cmd *XStreamSliceCmd) Val() []*XStream {
|
||||
func (cmd *XStreamSliceCmd) Val() []XStream {
|
||||
return cmd.val
|
||||
}
|
||||
|
||||
func (cmd *XStreamSliceCmd) Result() ([]*XStream, error) {
|
||||
func (cmd *XStreamSliceCmd) Result() ([]XStream, error) {
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
|
@ -758,25 +969,15 @@ func (cmd *XStreamSliceCmd) readReply(cn *pool.Conn) error {
|
|||
if cmd.err != nil {
|
||||
return cmd.err
|
||||
}
|
||||
cmd.val = v.([]*XStream)
|
||||
cmd.val = v.([]XStream)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func xStreamSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
xx := make([]*XStream, n)
|
||||
ret := make([]XStream, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
v, err := rd.ReadArrayReply(xStreamParser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
xx[i] = v.(*XStream)
|
||||
}
|
||||
return xx, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func xStreamParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
if n != 2 {
|
||||
return nil, fmt.Errorf("got %d, wanted 2", n)
|
||||
}
|
||||
|
@ -791,99 +992,218 @@ func xStreamParser(rd *proto.Reader, n int64) (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &XStream{
|
||||
ret = append(ret, XStream{
|
||||
Stream: stream,
|
||||
Messages: v.([]*XMessage),
|
||||
}, nil
|
||||
Messages: v.([]XMessage),
|
||||
})
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type XMessageSliceCmd struct {
|
||||
baseCmd
|
||||
|
||||
val []*XMessage
|
||||
type XPending struct {
|
||||
Count int64
|
||||
Lower string
|
||||
Higher string
|
||||
Consumers map[string]int64
|
||||
}
|
||||
|
||||
var _ Cmder = (*XMessageSliceCmd)(nil)
|
||||
type XPendingCmd struct {
|
||||
baseCmd
|
||||
val *XPending
|
||||
}
|
||||
|
||||
func NewXMessageSliceCmd(args ...interface{}) *XMessageSliceCmd {
|
||||
return &XMessageSliceCmd{
|
||||
var _ Cmder = (*XPendingCmd)(nil)
|
||||
|
||||
func NewXPendingCmd(args ...interface{}) *XPendingCmd {
|
||||
return &XPendingCmd{
|
||||
baseCmd: baseCmd{_args: args},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *XMessageSliceCmd) Val() []*XMessage {
|
||||
func (cmd *XPendingCmd) Val() *XPending {
|
||||
return cmd.val
|
||||
}
|
||||
|
||||
func (cmd *XMessageSliceCmd) Result() ([]*XMessage, error) {
|
||||
func (cmd *XPendingCmd) Result() (*XPending, error) {
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *XMessageSliceCmd) String() string {
|
||||
func (cmd *XPendingCmd) String() string {
|
||||
return cmdString(cmd, cmd.val)
|
||||
}
|
||||
|
||||
func (cmd *XMessageSliceCmd) readReply(cn *pool.Conn) error {
|
||||
var v interface{}
|
||||
v, cmd.err = cn.Rd.ReadArrayReply(xMessageSliceParser)
|
||||
func (cmd *XPendingCmd) readReply(cn *pool.Conn) error {
|
||||
var info interface{}
|
||||
info, cmd.err = cn.Rd.ReadArrayReply(xPendingParser)
|
||||
if cmd.err != nil {
|
||||
return cmd.err
|
||||
}
|
||||
cmd.val = v.([]*XMessage)
|
||||
cmd.val = info.(*XPending)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func xMessageSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
msgs := make([]*XMessage, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
v, err := rd.ReadArrayReply(xMessageParser)
|
||||
func xPendingParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
if n != 4 {
|
||||
return nil, fmt.Errorf("got %d, wanted 4", n)
|
||||
}
|
||||
|
||||
count, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgs[i] = v.(*XMessage)
|
||||
}
|
||||
return msgs, nil
|
||||
|
||||
lower, err := rd.ReadStringReply()
|
||||
if err != nil && err != Nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
higher, err := rd.ReadStringReply()
|
||||
if err != nil && err != Nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pending := &XPending{
|
||||
Count: count,
|
||||
Lower: lower,
|
||||
Higher: higher,
|
||||
}
|
||||
_, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
for i := int64(0); i < n; i++ {
|
||||
_, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
if n != 2 {
|
||||
return nil, fmt.Errorf("got %d, wanted 2", n)
|
||||
}
|
||||
|
||||
consumerName, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
consumerPendingStr, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
consumerPending, err := strconv.ParseInt(consumerPendingStr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pending.Consumers == nil {
|
||||
pending.Consumers = make(map[string]int64)
|
||||
}
|
||||
pending.Consumers[consumerName] = consumerPending
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil && err != Nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pending, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type XPendingExt struct {
|
||||
Id string
|
||||
Consumer string
|
||||
Idle time.Duration
|
||||
RetryCount int64
|
||||
}
|
||||
|
||||
type XPendingExtCmd struct {
|
||||
baseCmd
|
||||
val []XPendingExt
|
||||
}
|
||||
|
||||
var _ Cmder = (*XPendingExtCmd)(nil)
|
||||
|
||||
func NewXPendingExtCmd(args ...interface{}) *XPendingExtCmd {
|
||||
return &XPendingExtCmd{
|
||||
baseCmd: baseCmd{_args: args},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *XPendingExtCmd) Val() []XPendingExt {
|
||||
return cmd.val
|
||||
}
|
||||
|
||||
func (cmd *XPendingExtCmd) Result() ([]XPendingExt, error) {
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *XPendingExtCmd) String() string {
|
||||
return cmdString(cmd, cmd.val)
|
||||
}
|
||||
|
||||
func (cmd *XPendingExtCmd) readReply(cn *pool.Conn) error {
|
||||
var info interface{}
|
||||
info, cmd.err = cn.Rd.ReadArrayReply(xPendingExtSliceParser)
|
||||
if cmd.err != nil {
|
||||
return cmd.err
|
||||
}
|
||||
cmd.val = info.([]XPendingExt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func xPendingExtSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
ret := make([]XPendingExt, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
if n != 4 {
|
||||
return nil, fmt.Errorf("got %d, wanted 4", n)
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func xMessageParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
id, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, err := rd.ReadArrayReply(xKeyValueParser)
|
||||
if err != nil {
|
||||
consumer, err := rd.ReadStringReply()
|
||||
if err != nil && err != Nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &XMessage{
|
||||
ID: id,
|
||||
Values: v.(map[string]interface{}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func xKeyValueParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
values := make(map[string]interface{}, n)
|
||||
for i := int64(0); i < n; i += 2 {
|
||||
key, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
idle, err := rd.ReadIntReply()
|
||||
if err != nil && err != Nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
retryCount, err := rd.ReadIntReply()
|
||||
if err != nil && err != Nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
values[key] = value
|
||||
ret = append(ret, XPendingExt{
|
||||
Id: id,
|
||||
Consumer: consumer,
|
||||
Idle: time.Duration(idle) * time.Millisecond,
|
||||
RetryCount: retryCount,
|
||||
})
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
@ -923,6 +1243,27 @@ func (cmd *ZSliceCmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func zSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
zz := make([]Z, n/2)
|
||||
for i := int64(0); i < n; i += 2 {
|
||||
var err error
|
||||
|
||||
z := &zz[i/2]
|
||||
|
||||
z.Member, err = rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
z.Score, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return zz, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type ScanCmd struct {
|
||||
|
@ -1016,6 +1357,69 @@ func (cmd *ClusterSlotsCmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func clusterSlotsParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
slots := make([]ClusterSlot, n)
|
||||
for i := 0; i < len(slots); i++ {
|
||||
n, err := rd.ReadArrayLen()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n < 2 {
|
||||
err := fmt.Errorf("redis: got %d elements in cluster info, expected at least 2", n)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
start, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
end, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodes := make([]ClusterNode, n-2)
|
||||
for j := 0; j < len(nodes); j++ {
|
||||
n, err := rd.ReadArrayLen()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != 2 && n != 3 {
|
||||
err := fmt.Errorf("got %d elements in cluster info address, expected 2 or 3", n)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ip, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes[j].Addr = net.JoinHostPort(ip, strconv.FormatInt(port, 10))
|
||||
|
||||
if n == 3 {
|
||||
id, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes[j].Id = id
|
||||
}
|
||||
}
|
||||
|
||||
slots[i] = ClusterSlot{
|
||||
Start: int(start),
|
||||
End: int(end),
|
||||
Nodes: nodes,
|
||||
}
|
||||
}
|
||||
return slots, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// GeoLocation is used with GeoAdd to add geospatial location.
|
||||
|
@ -1107,6 +1511,73 @@ func (cmd *GeoLocationCmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func newGeoLocationParser(q *GeoRadiusQuery) proto.MultiBulkParse {
|
||||
return func(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
var loc GeoLocation
|
||||
var err error
|
||||
|
||||
loc.Name, err = rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if q.WithDist {
|
||||
loc.Dist, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if q.WithGeoHash {
|
||||
loc.GeoHash, err = rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if q.WithCoord {
|
||||
n, err := rd.ReadArrayLen()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != 2 {
|
||||
return nil, fmt.Errorf("got %d coordinates, expected 2", n)
|
||||
}
|
||||
|
||||
loc.Longitude, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
loc.Latitude, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &loc, nil
|
||||
}
|
||||
}
|
||||
|
||||
func newGeoLocationSliceParser(q *GeoRadiusQuery) proto.MultiBulkParse {
|
||||
return func(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
locs := make([]GeoLocation, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
v, err := rd.ReadReply(newGeoLocationParser(q))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch vv := v.(type) {
|
||||
case []byte:
|
||||
locs = append(locs, GeoLocation{
|
||||
Name: string(vv),
|
||||
})
|
||||
case *GeoLocation:
|
||||
locs = append(locs, *vv)
|
||||
default:
|
||||
return nil, fmt.Errorf("got %T, expected string or *GeoLocation", v)
|
||||
}
|
||||
}
|
||||
return locs, nil
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type GeoPos struct {
|
||||
|
@ -1149,6 +1620,44 @@ func (cmd *GeoPosCmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func geoPosSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
positions := make([]*GeoPos, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
v, err := rd.ReadReply(geoPosParser)
|
||||
if err != nil {
|
||||
if err == Nil {
|
||||
positions = append(positions, nil)
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case *GeoPos:
|
||||
positions = append(positions, v)
|
||||
default:
|
||||
return nil, fmt.Errorf("got %T, expected *GeoPos", v)
|
||||
}
|
||||
}
|
||||
return positions, nil
|
||||
}
|
||||
|
||||
func geoPosParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
var pos GeoPos
|
||||
var err error
|
||||
|
||||
pos.Longitude, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pos.Latitude, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pos, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type CommandInfo struct {
|
||||
|
@ -1197,6 +1706,74 @@ func (cmd *CommandsInfoCmd) readReply(cn *pool.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func commandInfoSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
m := make(map[string]*CommandInfo, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
v, err := rd.ReadReply(commandInfoParser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vv := v.(*CommandInfo)
|
||||
m[vv.Name] = vv
|
||||
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
var cmd CommandInfo
|
||||
var err error
|
||||
|
||||
if n != 6 {
|
||||
return nil, fmt.Errorf("redis: got %d elements in COMMAND reply, wanted 6", n)
|
||||
}
|
||||
|
||||
cmd.Name, err = rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
arity, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.Arity = int8(arity)
|
||||
|
||||
flags, err := rd.ReadReply(stringSliceParser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.Flags = flags.([]string)
|
||||
|
||||
firstKeyPos, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.FirstKeyPos = int8(firstKeyPos)
|
||||
|
||||
lastKeyPos, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.LastKeyPos = int8(lastKeyPos)
|
||||
|
||||
stepCount, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.StepCount = int8(stepCount)
|
||||
|
||||
for _, flag := range cmd.Flags {
|
||||
if flag == "readonly" {
|
||||
cmd.ReadOnly = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &cmd, nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type cmdsInfoCache struct {
|
||||
|
|
242
commands.go
242
commands.go
|
@ -172,16 +172,26 @@ type Cmdable interface {
|
|||
SRem(key string, members ...interface{}) *IntCmd
|
||||
SUnion(keys ...string) *StringSliceCmd
|
||||
SUnionStore(destination string, keys ...string) *IntCmd
|
||||
XAdd(stream, id string, els map[string]interface{}) *StringCmd
|
||||
XAddExt(opt *XAddExt) *StringCmd
|
||||
XLen(key string) *IntCmd
|
||||
XAdd(a *XAddArgs) *StringCmd
|
||||
XLen(stream string) *IntCmd
|
||||
XRange(stream, start, stop string) *XMessageSliceCmd
|
||||
XRangeN(stream, start, stop string, count int64) *XMessageSliceCmd
|
||||
XRevRange(stream string, start, stop string) *XMessageSliceCmd
|
||||
XRevRangeN(stream string, start, stop string, count int64) *XMessageSliceCmd
|
||||
XRead(streams ...string) *XStreamSliceCmd
|
||||
XReadN(count int64, streams ...string) *XStreamSliceCmd
|
||||
XReadExt(opt *XReadExt) *XStreamSliceCmd
|
||||
XRead(a *XReadArgs) *XStreamSliceCmd
|
||||
XReadStreams(streams ...string) *XStreamSliceCmd
|
||||
XGroupCreate(stream, group, start string) *StatusCmd
|
||||
XGroupSetID(stream, group, start string) *StatusCmd
|
||||
XGroupDestroy(stream, group string) *IntCmd
|
||||
XGroupDelConsumer(stream, group, consumer string) *IntCmd
|
||||
XReadGroup(a *XReadGroupArgs) *XStreamSliceCmd
|
||||
XAck(stream, group string, ids ...string) *IntCmd
|
||||
XPending(stream, group string) *XPendingCmd
|
||||
XPendingExt(a *XPendingExtArgs) *XPendingExtCmd
|
||||
XClaim(a *XClaimArgs) *XMessageSliceCmd
|
||||
XClaimJustID(a *XClaimArgs) *StringSliceCmd
|
||||
XTrim(key string, maxLen int64) *IntCmd
|
||||
XTrimApprox(key string, maxLen int64) *IntCmd
|
||||
ZAdd(key string, members ...Z) *IntCmd
|
||||
ZAddNX(key string, members ...Z) *IntCmd
|
||||
ZAddXX(key string, members ...Z) *IntCmd
|
||||
|
@ -1300,7 +1310,7 @@ func (c *cmdable) SUnionStore(destination string, keys ...string) *IntCmd {
|
|||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type XAddExt struct {
|
||||
type XAddArgs struct {
|
||||
Stream string
|
||||
MaxLen int64 // MAXLEN N
|
||||
MaxLenApprox int64 // MAXLEN ~ N
|
||||
|
@ -1308,40 +1318,32 @@ type XAddExt struct {
|
|||
Values map[string]interface{}
|
||||
}
|
||||
|
||||
func (c *cmdable) XAddExt(opt *XAddExt) *StringCmd {
|
||||
a := make([]interface{}, 0, 6+len(opt.Values)*2)
|
||||
a = append(a, "xadd")
|
||||
a = append(a, opt.Stream)
|
||||
if opt.MaxLen > 0 {
|
||||
a = append(a, "maxlen", opt.MaxLen)
|
||||
} else if opt.MaxLenApprox > 0 {
|
||||
a = append(a, "maxlen", "~", opt.MaxLenApprox)
|
||||
func (c *cmdable) XAdd(a *XAddArgs) *StringCmd {
|
||||
args := make([]interface{}, 0, 6+len(a.Values)*2)
|
||||
args = append(args, "xadd")
|
||||
args = append(args, a.Stream)
|
||||
if a.MaxLen > 0 {
|
||||
args = append(args, "maxlen", a.MaxLen)
|
||||
} else if a.MaxLenApprox > 0 {
|
||||
args = append(args, "maxlen", "~", a.MaxLenApprox)
|
||||
}
|
||||
if opt.ID != "" {
|
||||
a = append(a, opt.ID)
|
||||
if a.ID != "" {
|
||||
args = append(args, a.ID)
|
||||
} else {
|
||||
a = append(a, "*")
|
||||
args = append(args, "*")
|
||||
}
|
||||
for k, v := range opt.Values {
|
||||
a = append(a, k)
|
||||
a = append(a, v)
|
||||
for k, v := range a.Values {
|
||||
args = append(args, k)
|
||||
args = append(args, v)
|
||||
}
|
||||
|
||||
cmd := NewStringCmd(a...)
|
||||
cmd := NewStringCmd(args...)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *cmdable) XAdd(stream, id string, values map[string]interface{}) *StringCmd {
|
||||
return c.XAddExt(&XAddExt{
|
||||
Stream: stream,
|
||||
ID: id,
|
||||
Values: values,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *cmdable) XLen(key string) *IntCmd {
|
||||
cmd := NewIntCmd("xlen", key)
|
||||
func (c *cmdable) XLen(stream string) *IntCmd {
|
||||
cmd := NewIntCmd("xlen", stream)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
@ -1370,55 +1372,173 @@ func (c *cmdable) XRevRangeN(stream, start, stop string, count int64) *XMessageS
|
|||
return cmd
|
||||
}
|
||||
|
||||
type XReadExt struct {
|
||||
type XReadArgs struct {
|
||||
Streams []string
|
||||
Count int64
|
||||
Block time.Duration
|
||||
}
|
||||
|
||||
func (c *cmdable) XReadExt(opt *XReadExt) *XStreamSliceCmd {
|
||||
a := make([]interface{}, 0, 5+len(opt.Streams))
|
||||
a = append(a, "xread")
|
||||
if opt != nil {
|
||||
if opt.Count > 0 {
|
||||
a = append(a, "count")
|
||||
a = append(a, opt.Count)
|
||||
func (c *cmdable) XRead(a *XReadArgs) *XStreamSliceCmd {
|
||||
args := make([]interface{}, 0, 5+len(a.Streams))
|
||||
args = append(args, "xread")
|
||||
if a.Count > 0 {
|
||||
args = append(args, "count")
|
||||
args = append(args, a.Count)
|
||||
}
|
||||
if opt.Block >= 0 {
|
||||
a = append(a, "block")
|
||||
a = append(a, int64(opt.Block/time.Millisecond))
|
||||
if a.Block >= 0 {
|
||||
args = append(args, "block")
|
||||
args = append(args, int64(a.Block/time.Millisecond))
|
||||
}
|
||||
}
|
||||
a = append(a, "streams")
|
||||
for _, s := range opt.Streams {
|
||||
a = append(a, s)
|
||||
args = append(args, "streams")
|
||||
for _, s := range a.Streams {
|
||||
args = append(args, s)
|
||||
}
|
||||
|
||||
cmd := NewXStreamSliceCmd(a...)
|
||||
cmd := NewXStreamSliceCmd(args...)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *cmdable) XRead(streams ...string) *XStreamSliceCmd {
|
||||
return c.XReadExt(&XReadExt{
|
||||
func (c *cmdable) XReadStreams(streams ...string) *XStreamSliceCmd {
|
||||
return c.XRead(&XReadArgs{
|
||||
Streams: streams,
|
||||
Block: -1,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *cmdable) XReadN(count int64, streams ...string) *XStreamSliceCmd {
|
||||
return c.XReadExt(&XReadExt{
|
||||
Streams: streams,
|
||||
Count: count,
|
||||
Block: -1,
|
||||
})
|
||||
func (c *cmdable) XGroupCreate(stream, group, start string) *StatusCmd {
|
||||
cmd := NewStatusCmd("xgroup", "create", stream, group, start)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *cmdable) XReadBlock(block time.Duration, streams ...string) *XStreamSliceCmd {
|
||||
return c.XReadExt(&XReadExt{
|
||||
Streams: streams,
|
||||
Block: block,
|
||||
})
|
||||
func (c *cmdable) XGroupSetID(stream, group, start string) *StatusCmd {
|
||||
cmd := NewStatusCmd("xgroup", "setid", stream, group, start)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *cmdable) XGroupDestroy(stream, group string) *IntCmd {
|
||||
cmd := NewIntCmd("xgroup", "destroy", stream, group)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *cmdable) XGroupDelConsumer(stream, group, consumer string) *IntCmd {
|
||||
cmd := NewIntCmd("xgroup", "delconsumer", stream, group, consumer)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
type XReadGroupArgs struct {
|
||||
Group string
|
||||
Consumer string
|
||||
Streams []string
|
||||
Count int64
|
||||
Block time.Duration
|
||||
}
|
||||
|
||||
func (c *cmdable) XReadGroup(a *XReadGroupArgs) *XStreamSliceCmd {
|
||||
args := make([]interface{}, 0, 8+len(a.Streams))
|
||||
args = append(args, "xreadgroup", "group", a.Group, a.Consumer)
|
||||
if a.Count > 0 {
|
||||
args = append(args, "count", a.Count)
|
||||
}
|
||||
if a.Block >= 0 {
|
||||
args = append(args, "block", int64(a.Block/time.Millisecond))
|
||||
}
|
||||
args = append(args, "streams")
|
||||
for _, s := range a.Streams {
|
||||
args = append(args, s)
|
||||
}
|
||||
|
||||
cmd := NewXStreamSliceCmd(args...)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *cmdable) XAck(stream, group string, ids ...string) *IntCmd {
|
||||
args := []interface{}{"xack", stream, group}
|
||||
for _, id := range ids {
|
||||
args = append(args, id)
|
||||
}
|
||||
cmd := NewIntCmd(args...)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *cmdable) XPending(stream, group string) *XPendingCmd {
|
||||
cmd := NewXPendingCmd("xpending", stream, group)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
type XPendingExtArgs struct {
|
||||
Stream string
|
||||
Group string
|
||||
Start string
|
||||
End string
|
||||
Count int64
|
||||
Consumer string
|
||||
}
|
||||
|
||||
func (c *cmdable) XPendingExt(a *XPendingExtArgs) *XPendingExtCmd {
|
||||
args := make([]interface{}, 0, 7)
|
||||
args = append(args, "xpending", a.Stream, a.Group, a.Start, a.End, a.Count)
|
||||
if a.Consumer != "" {
|
||||
args = append(args, a.Consumer)
|
||||
}
|
||||
cmd := NewXPendingExtCmd(args...)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
type XClaimArgs struct {
|
||||
Stream string
|
||||
Group string
|
||||
Consumer string
|
||||
MinIdle time.Duration
|
||||
Messages []string
|
||||
}
|
||||
|
||||
func (c *cmdable) XClaim(a *XClaimArgs) *XMessageSliceCmd {
|
||||
args := xClaimArgs(a)
|
||||
cmd := NewXMessageSliceCmd(args...)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *cmdable) XClaimJustID(a *XClaimArgs) *StringSliceCmd {
|
||||
args := xClaimArgs(a)
|
||||
args = append(args, "justid")
|
||||
cmd := NewStringSliceCmd(args...)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func xClaimArgs(a *XClaimArgs) []interface{} {
|
||||
args := make([]interface{}, 0, 4+len(a.Messages))
|
||||
args = append(args,
|
||||
"xclaim",
|
||||
a.Stream,
|
||||
a.Group, a.Consumer,
|
||||
int64(a.MinIdle/time.Millisecond))
|
||||
for _, id := range a.Messages {
|
||||
args = append(args, id)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func (c *cmdable) XTrim(key string, maxLen int64) *IntCmd {
|
||||
cmd := NewIntCmd("xtrim", key, "maxlen", maxLen)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *cmdable) XTrimApprox(key string, maxLen int64) *IntCmd {
|
||||
cmd := NewIntCmd("xtrim", key, "maxlen", "~", maxLen)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
206
commands_test.go
206
commands_test.go
|
@ -3042,37 +3042,54 @@ var _ = Describe("Commands", func() {
|
|||
})
|
||||
|
||||
Describe("streams", func() {
|
||||
createStream := func() {
|
||||
id, err := client.XAdd("stream", "1-0", map[string]interface{}{
|
||||
"uno": "un",
|
||||
BeforeEach(func() {
|
||||
id, err := client.XAdd(&redis.XAddArgs{
|
||||
Stream: "stream",
|
||||
ID: "1-0",
|
||||
Values: map[string]interface{}{"uno": "un"},
|
||||
}).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(id).To(Equal("1-0"))
|
||||
|
||||
id, err = client.XAdd("stream", "2-0", map[string]interface{}{
|
||||
"dos": "deux",
|
||||
id, err = client.XAdd(&redis.XAddArgs{
|
||||
Stream: "stream",
|
||||
ID: "2-0",
|
||||
Values: map[string]interface{}{"dos": "deux"},
|
||||
}).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(id).To(Equal("2-0"))
|
||||
|
||||
id, err = client.XAdd("stream", "3-0", map[string]interface{}{
|
||||
"tres": "troix",
|
||||
id, err = client.XAdd(&redis.XAddArgs{
|
||||
Stream: "stream",
|
||||
ID: "3-0",
|
||||
Values: map[string]interface{}{"tres": "troix"},
|
||||
}).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(id).To(Equal("3-0"))
|
||||
}
|
||||
})
|
||||
|
||||
It("should XTrim", func() {
|
||||
n, err := client.XTrim("stream", 0).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(n).To(Equal(int64(3)))
|
||||
})
|
||||
|
||||
It("should XTrimApprox", func() {
|
||||
n, err := client.XTrimApprox("stream", 0).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(n).To(Equal(int64(3)))
|
||||
})
|
||||
|
||||
It("should XAdd", func() {
|
||||
createStream()
|
||||
|
||||
id, err := client.XAdd("stream", "*", map[string]interface{}{
|
||||
"quatro": "quatre",
|
||||
id, err := client.XAdd(&redis.XAddArgs{
|
||||
Stream: "stream",
|
||||
Values: map[string]interface{}{"quatro": "quatre"},
|
||||
}).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
vals, err := client.XRange("stream", "-", "+").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(vals).To(Equal([]*redis.XMessage{
|
||||
Expect(vals).To(Equal([]redis.XMessage{
|
||||
{ID: "1-0", Values: map[string]interface{}{"uno": "un"}},
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
{ID: "3-0", Values: map[string]interface{}{"tres": "troix"}},
|
||||
|
@ -3080,10 +3097,8 @@ var _ = Describe("Commands", func() {
|
|||
}))
|
||||
})
|
||||
|
||||
It("should XAddExt", func() {
|
||||
createStream()
|
||||
|
||||
id, err := client.XAddExt(&redis.XAddExt{
|
||||
It("should XAdd with MaxLen", func() {
|
||||
id, err := client.XAdd(&redis.XAddArgs{
|
||||
Stream: "stream",
|
||||
MaxLen: 1,
|
||||
Values: map[string]interface{}{"quatro": "quatre"},
|
||||
|
@ -3092,25 +3107,21 @@ var _ = Describe("Commands", func() {
|
|||
|
||||
vals, err := client.XRange("stream", "-", "+").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(vals).To(Equal([]*redis.XMessage{
|
||||
Expect(vals).To(Equal([]redis.XMessage{
|
||||
{ID: id, Values: map[string]interface{}{"quatro": "quatre"}},
|
||||
}))
|
||||
})
|
||||
|
||||
It("should XLen", func() {
|
||||
createStream()
|
||||
|
||||
n, err := client.XLen("stream").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(n).To(Equal(int64(3)))
|
||||
})
|
||||
|
||||
It("should XRange", func() {
|
||||
createStream()
|
||||
|
||||
msgs, err := client.XRange("stream", "-", "+").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(msgs).To(Equal([]*redis.XMessage{
|
||||
Expect(msgs).To(Equal([]redis.XMessage{
|
||||
{ID: "1-0", Values: map[string]interface{}{"uno": "un"}},
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
{ID: "3-0", Values: map[string]interface{}{"tres": "troix"}},
|
||||
|
@ -3118,48 +3129,44 @@ var _ = Describe("Commands", func() {
|
|||
|
||||
msgs, err = client.XRange("stream", "2", "+").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(msgs).To(Equal([]*redis.XMessage{
|
||||
Expect(msgs).To(Equal([]redis.XMessage{
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
{ID: "3-0", Values: map[string]interface{}{"tres": "troix"}},
|
||||
}))
|
||||
|
||||
msgs, err = client.XRange("stream", "-", "2").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(msgs).To(Equal([]*redis.XMessage{
|
||||
Expect(msgs).To(Equal([]redis.XMessage{
|
||||
{ID: "1-0", Values: map[string]interface{}{"uno": "un"}},
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
}))
|
||||
})
|
||||
|
||||
It("should XRangeN", func() {
|
||||
createStream()
|
||||
|
||||
msgs, err := client.XRangeN("stream", "-", "+", 2).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(msgs).To(Equal([]*redis.XMessage{
|
||||
Expect(msgs).To(Equal([]redis.XMessage{
|
||||
{ID: "1-0", Values: map[string]interface{}{"uno": "un"}},
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
}))
|
||||
|
||||
msgs, err = client.XRangeN("stream", "2", "+", 1).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(msgs).To(Equal([]*redis.XMessage{
|
||||
Expect(msgs).To(Equal([]redis.XMessage{
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
}))
|
||||
|
||||
msgs, err = client.XRangeN("stream", "-", "2", 1).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(msgs).To(Equal([]*redis.XMessage{
|
||||
Expect(msgs).To(Equal([]redis.XMessage{
|
||||
{ID: "1-0", Values: map[string]interface{}{"uno": "un"}},
|
||||
}))
|
||||
})
|
||||
|
||||
It("should XRevRange", func() {
|
||||
createStream()
|
||||
|
||||
msgs, err := client.XRevRange("stream", "+", "-").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(msgs).To(Equal([]*redis.XMessage{
|
||||
Expect(msgs).To(Equal([]redis.XMessage{
|
||||
{ID: "3-0", Values: map[string]interface{}{"tres": "troix"}},
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
{ID: "1-0", Values: map[string]interface{}{"uno": "un"}},
|
||||
|
@ -3167,82 +3174,171 @@ var _ = Describe("Commands", func() {
|
|||
|
||||
msgs, err = client.XRevRange("stream", "+", "2").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(msgs).To(Equal([]*redis.XMessage{
|
||||
Expect(msgs).To(Equal([]redis.XMessage{
|
||||
{ID: "3-0", Values: map[string]interface{}{"tres": "troix"}},
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
}))
|
||||
})
|
||||
|
||||
It("should XRevRangeN", func() {
|
||||
createStream()
|
||||
|
||||
msgs, err := client.XRevRangeN("stream", "+", "-", 2).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(msgs).To(Equal([]*redis.XMessage{
|
||||
Expect(msgs).To(Equal([]redis.XMessage{
|
||||
{ID: "3-0", Values: map[string]interface{}{"tres": "troix"}},
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
}))
|
||||
|
||||
msgs, err = client.XRevRangeN("stream", "+", "2", 1).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(msgs).To(Equal([]*redis.XMessage{
|
||||
Expect(msgs).To(Equal([]redis.XMessage{
|
||||
{ID: "3-0", Values: map[string]interface{}{"tres": "troix"}},
|
||||
}))
|
||||
})
|
||||
|
||||
It("should XRead", func() {
|
||||
createStream()
|
||||
|
||||
res, err := client.XRead("stream", "0").Result()
|
||||
res, err := client.XReadStreams("stream", "0").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(res).To(Equal([]*redis.XStream{{
|
||||
Expect(res).To(Equal([]redis.XStream{{
|
||||
Stream: "stream",
|
||||
Messages: []*redis.XMessage{
|
||||
Messages: []redis.XMessage{
|
||||
{ID: "1-0", Values: map[string]interface{}{"uno": "un"}},
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
{ID: "3-0", Values: map[string]interface{}{"tres": "troix"}},
|
||||
}},
|
||||
}))
|
||||
|
||||
_, err = client.XRead("stream", "3").Result()
|
||||
_, err = client.XReadStreams("stream", "3").Result()
|
||||
Expect(err).To(Equal(redis.Nil))
|
||||
})
|
||||
|
||||
It("should XReadExt", func() {
|
||||
createStream()
|
||||
|
||||
res, err := client.XReadExt(&redis.XReadExt{
|
||||
It("should XRead", func() {
|
||||
res, err := client.XRead(&redis.XReadArgs{
|
||||
Streams: []string{"stream", "0"},
|
||||
Count: 2,
|
||||
Block: 100 * time.Millisecond,
|
||||
}).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(res).To(Equal([]*redis.XStream{{
|
||||
Expect(res).To(Equal([]redis.XStream{{
|
||||
Stream: "stream",
|
||||
Messages: []*redis.XMessage{
|
||||
Messages: []redis.XMessage{
|
||||
{ID: "1-0", Values: map[string]interface{}{"uno": "un"}},
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
}},
|
||||
}))
|
||||
|
||||
_, err = client.XReadExt(&redis.XReadExt{
|
||||
_, err = client.XRead(&redis.XReadArgs{
|
||||
Streams: []string{"stream", "3"},
|
||||
Count: 1,
|
||||
Block: 100 * time.Millisecond,
|
||||
}).Result()
|
||||
Expect(err).To(Equal(redis.Nil))
|
||||
})
|
||||
|
||||
Describe("group", func() {
|
||||
BeforeEach(func() {
|
||||
err := client.XGroupCreate("stream", "group", "0").Err()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
res, err := client.XReadGroup(&redis.XReadGroupArgs{
|
||||
Group: "group",
|
||||
Consumer: "consumer",
|
||||
Streams: []string{"stream", "0"},
|
||||
}).Result()
|
||||
Expect(res).To(Equal([]redis.XStream{{
|
||||
Stream: "stream",
|
||||
Messages: []redis.XMessage{
|
||||
{ID: "1-0", Values: map[string]interface{}{"uno": "un"}},
|
||||
{ID: "2-0", Values: map[string]interface{}{"dos": "deux"}},
|
||||
{ID: "3-0", Values: map[string]interface{}{"tres": "troix"}},
|
||||
}},
|
||||
}))
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
n, err := client.XGroupDestroy("stream", "group").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(n).To(Equal(int64(1)))
|
||||
})
|
||||
|
||||
It("should XPending", func() {
|
||||
info, err := client.XPending("stream", "group").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(info).To(Equal(&redis.XPending{
|
||||
Count: 3,
|
||||
Lower: "1-0",
|
||||
Higher: "3-0",
|
||||
Consumers: map[string]int64{"consumer": 3},
|
||||
}))
|
||||
|
||||
infoExt, err := client.XPendingExt(&redis.XPendingExtArgs{
|
||||
Stream: "stream",
|
||||
Group: "group",
|
||||
Start: "-",
|
||||
End: "+",
|
||||
Count: 10,
|
||||
Consumer: "consumer",
|
||||
}).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
for i := range infoExt {
|
||||
infoExt[i].Idle = 0
|
||||
}
|
||||
Expect(infoExt).To(Equal([]redis.XPendingExt{
|
||||
{Id: "1-0", Consumer: "consumer", Idle: 0, RetryCount: 1},
|
||||
{Id: "2-0", Consumer: "consumer", Idle: 0, RetryCount: 1},
|
||||
{Id: "3-0", Consumer: "consumer", Idle: 0, RetryCount: 1},
|
||||
}))
|
||||
|
||||
n, err := client.XGroupDelConsumer("stream", "group", "consumer").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(n).To(Equal(int64(3)))
|
||||
})
|
||||
|
||||
It("should XClaim", func() {
|
||||
msgs, err := client.XClaim(&redis.XClaimArgs{
|
||||
Stream: "stream",
|
||||
Group: "group",
|
||||
Consumer: "consumer",
|
||||
Messages: []string{"1-0", "2-0", "3-0"},
|
||||
}).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(msgs).To(Equal([]redis.XMessage{{
|
||||
ID: "1-0",
|
||||
Values: map[string]interface{}{"uno": "un"},
|
||||
}, {
|
||||
ID: "2-0",
|
||||
Values: map[string]interface{}{"dos": "deux"},
|
||||
}, {
|
||||
ID: "3-0",
|
||||
Values: map[string]interface{}{"tres": "troix"},
|
||||
}}))
|
||||
|
||||
ids, err := client.XClaimJustID(&redis.XClaimArgs{
|
||||
Stream: "stream",
|
||||
Group: "group",
|
||||
Consumer: "consumer",
|
||||
Messages: []string{"1-0", "2-0", "3-0"},
|
||||
}).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(ids).To(Equal([]string{"1-0", "2-0", "3-0"}))
|
||||
})
|
||||
|
||||
It("should XAck", func() {
|
||||
n, err := client.XAck("stream", "group", "1-0", "2-0", "4-0").Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(n).To(Equal(int64(2)))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Geo add and radius search", func() {
|
||||
BeforeEach(func() {
|
||||
geoAdd := client.GeoAdd(
|
||||
n, err := client.GeoAdd(
|
||||
"Sicily",
|
||||
&redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
|
||||
&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"},
|
||||
)
|
||||
Expect(geoAdd.Err()).NotTo(HaveOccurred())
|
||||
Expect(geoAdd.Val()).To(Equal(int64(2)))
|
||||
).Result()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(n).To(Equal(int64(2)))
|
||||
})
|
||||
|
||||
It("should not add same geo location", func() {
|
||||
|
|
394
parser.go
394
parser.go
|
@ -1,394 +0,0 @@
|
|||
package redis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/internal/proto"
|
||||
)
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func sliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
vals := make([]interface{}, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
v, err := rd.ReadReply(sliceParser)
|
||||
if err != nil {
|
||||
if err == Nil {
|
||||
vals = append(vals, nil)
|
||||
continue
|
||||
}
|
||||
if err, ok := err.(proto.RedisError); ok {
|
||||
vals = append(vals, err)
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch v := v.(type) {
|
||||
case []byte:
|
||||
vals = append(vals, string(v))
|
||||
default:
|
||||
vals = append(vals, v)
|
||||
}
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func boolSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
bools := make([]bool, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
n, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bools = append(bools, n == 1)
|
||||
}
|
||||
return bools, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func stringSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
ss := make([]string, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
s, err := rd.ReadStringReply()
|
||||
if err == Nil {
|
||||
ss = append(ss, "")
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ss = append(ss, s)
|
||||
}
|
||||
}
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func stringStringMapParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
m := make(map[string]string, n/2)
|
||||
for i := int64(0); i < n; i += 2 {
|
||||
key, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m[key] = value
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func stringIntMapParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
m := make(map[string]int64, n/2)
|
||||
for i := int64(0); i < n; i += 2 {
|
||||
key, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m[key] = n
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func stringStructMapParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
m := make(map[string]struct{}, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
key, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m[key] = struct{}{}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func zSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
zz := make([]Z, n/2)
|
||||
for i := int64(0); i < n; i += 2 {
|
||||
var err error
|
||||
|
||||
z := &zz[i/2]
|
||||
|
||||
z.Member, err = rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
z.Score, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return zz, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func clusterSlotsParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
slots := make([]ClusterSlot, n)
|
||||
for i := 0; i < len(slots); i++ {
|
||||
n, err := rd.ReadArrayLen()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n < 2 {
|
||||
err := fmt.Errorf("redis: got %d elements in cluster info, expected at least 2", n)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
start, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
end, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodes := make([]ClusterNode, n-2)
|
||||
for j := 0; j < len(nodes); j++ {
|
||||
n, err := rd.ReadArrayLen()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != 2 && n != 3 {
|
||||
err := fmt.Errorf("got %d elements in cluster info address, expected 2 or 3", n)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ip, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes[j].Addr = net.JoinHostPort(ip, strconv.FormatInt(port, 10))
|
||||
|
||||
if n == 3 {
|
||||
id, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes[j].Id = id
|
||||
}
|
||||
}
|
||||
|
||||
slots[i] = ClusterSlot{
|
||||
Start: int(start),
|
||||
End: int(end),
|
||||
Nodes: nodes,
|
||||
}
|
||||
}
|
||||
return slots, nil
|
||||
}
|
||||
|
||||
func newGeoLocationParser(q *GeoRadiusQuery) proto.MultiBulkParse {
|
||||
return func(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
var loc GeoLocation
|
||||
var err error
|
||||
|
||||
loc.Name, err = rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if q.WithDist {
|
||||
loc.Dist, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if q.WithGeoHash {
|
||||
loc.GeoHash, err = rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if q.WithCoord {
|
||||
n, err := rd.ReadArrayLen()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != 2 {
|
||||
return nil, fmt.Errorf("got %d coordinates, expected 2", n)
|
||||
}
|
||||
|
||||
loc.Longitude, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
loc.Latitude, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &loc, nil
|
||||
}
|
||||
}
|
||||
|
||||
func newGeoLocationSliceParser(q *GeoRadiusQuery) proto.MultiBulkParse {
|
||||
return func(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
locs := make([]GeoLocation, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
v, err := rd.ReadReply(newGeoLocationParser(q))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch vv := v.(type) {
|
||||
case []byte:
|
||||
locs = append(locs, GeoLocation{
|
||||
Name: string(vv),
|
||||
})
|
||||
case *GeoLocation:
|
||||
locs = append(locs, *vv)
|
||||
default:
|
||||
return nil, fmt.Errorf("got %T, expected string or *GeoLocation", v)
|
||||
}
|
||||
}
|
||||
return locs, nil
|
||||
}
|
||||
}
|
||||
|
||||
func geoPosParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
var pos GeoPos
|
||||
var err error
|
||||
|
||||
pos.Longitude, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pos.Latitude, err = rd.ReadFloatReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pos, nil
|
||||
}
|
||||
|
||||
func geoPosSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
positions := make([]*GeoPos, 0, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
v, err := rd.ReadReply(geoPosParser)
|
||||
if err != nil {
|
||||
if err == Nil {
|
||||
positions = append(positions, nil)
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case *GeoPos:
|
||||
positions = append(positions, v)
|
||||
default:
|
||||
return nil, fmt.Errorf("got %T, expected *GeoPos", v)
|
||||
}
|
||||
}
|
||||
return positions, nil
|
||||
}
|
||||
|
||||
func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
var cmd CommandInfo
|
||||
var err error
|
||||
|
||||
if n != 6 {
|
||||
return nil, fmt.Errorf("redis: got %d elements in COMMAND reply, wanted 6", n)
|
||||
}
|
||||
|
||||
cmd.Name, err = rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
arity, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.Arity = int8(arity)
|
||||
|
||||
flags, err := rd.ReadReply(stringSliceParser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.Flags = flags.([]string)
|
||||
|
||||
firstKeyPos, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.FirstKeyPos = int8(firstKeyPos)
|
||||
|
||||
lastKeyPos, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.LastKeyPos = int8(lastKeyPos)
|
||||
|
||||
stepCount, err := rd.ReadIntReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.StepCount = int8(stepCount)
|
||||
|
||||
for _, flag := range cmd.Flags {
|
||||
if flag == "readonly" {
|
||||
cmd.ReadOnly = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &cmd, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func commandInfoSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
m := make(map[string]*CommandInfo, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
v, err := rd.ReadReply(commandInfoParser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vv := v.(*CommandInfo)
|
||||
m[vv.Name] = vv
|
||||
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func timeParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
if n != 2 {
|
||||
return nil, fmt.Errorf("got %d elements, expected 2", n)
|
||||
}
|
||||
|
||||
sec, err := rd.ReadInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
microsec, err := rd.ReadInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return time.Unix(sec, microsec*1000), nil
|
||||
}
|
Loading…
Reference in New Issue