1package redis
2
3import (
4	"context"
5	"fmt"
6	"net"
7	"strconv"
8	"time"
9
10	"github.com/go-redis/redis/v8/internal"
11	"github.com/go-redis/redis/v8/internal/hscan"
12	"github.com/go-redis/redis/v8/internal/proto"
13	"github.com/go-redis/redis/v8/internal/util"
14)
15
16type Cmder interface {
17	Name() string
18	FullName() string
19	Args() []interface{}
20	String() string
21	stringArg(int) string
22	firstKeyPos() int8
23	setFirstKeyPos(int8)
24
25	readTimeout() *time.Duration
26	readReply(rd *proto.Reader) error
27
28	SetErr(error)
29	Err() error
30}
31
32func setCmdsErr(cmds []Cmder, e error) {
33	for _, cmd := range cmds {
34		if cmd.Err() == nil {
35			cmd.SetErr(e)
36		}
37	}
38}
39
40func cmdsFirstErr(cmds []Cmder) error {
41	for _, cmd := range cmds {
42		if err := cmd.Err(); err != nil {
43			return err
44		}
45	}
46	return nil
47}
48
49func writeCmds(wr *proto.Writer, cmds []Cmder) error {
50	for _, cmd := range cmds {
51		if err := writeCmd(wr, cmd); err != nil {
52			return err
53		}
54	}
55	return nil
56}
57
58func writeCmd(wr *proto.Writer, cmd Cmder) error {
59	return wr.WriteArgs(cmd.Args())
60}
61
62func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
63	if pos := cmd.firstKeyPos(); pos != 0 {
64		return int(pos)
65	}
66
67	switch cmd.Name() {
68	case "eval", "evalsha":
69		if cmd.stringArg(2) != "0" {
70			return 3
71		}
72
73		return 0
74	case "publish":
75		return 1
76	case "memory":
77		// https://github.com/redis/redis/issues/7493
78		if cmd.stringArg(1) == "usage" {
79			return 2
80		}
81	}
82
83	if info != nil {
84		return int(info.FirstKeyPos)
85	}
86	return 0
87}
88
89func cmdString(cmd Cmder, val interface{}) string {
90	b := make([]byte, 0, 64)
91
92	for i, arg := range cmd.Args() {
93		if i > 0 {
94			b = append(b, ' ')
95		}
96		b = internal.AppendArg(b, arg)
97	}
98
99	if err := cmd.Err(); err != nil {
100		b = append(b, ": "...)
101		b = append(b, err.Error()...)
102	} else if val != nil {
103		b = append(b, ": "...)
104		b = internal.AppendArg(b, val)
105	}
106
107	return internal.String(b)
108}
109
110//------------------------------------------------------------------------------
111
112type baseCmd struct {
113	ctx    context.Context
114	args   []interface{}
115	err    error
116	keyPos int8
117
118	_readTimeout *time.Duration
119}
120
121var _ Cmder = (*Cmd)(nil)
122
123func (cmd *baseCmd) Name() string {
124	if len(cmd.args) == 0 {
125		return ""
126	}
127	// Cmd name must be lower cased.
128	return internal.ToLower(cmd.stringArg(0))
129}
130
131func (cmd *baseCmd) FullName() string {
132	switch name := cmd.Name(); name {
133	case "cluster", "command":
134		if len(cmd.args) == 1 {
135			return name
136		}
137		if s2, ok := cmd.args[1].(string); ok {
138			return name + " " + s2
139		}
140		return name
141	default:
142		return name
143	}
144}
145
146func (cmd *baseCmd) Args() []interface{} {
147	return cmd.args
148}
149
150func (cmd *baseCmd) stringArg(pos int) string {
151	if pos < 0 || pos >= len(cmd.args) {
152		return ""
153	}
154	s, _ := cmd.args[pos].(string)
155	return s
156}
157
158func (cmd *baseCmd) firstKeyPos() int8 {
159	return cmd.keyPos
160}
161
162func (cmd *baseCmd) setFirstKeyPos(keyPos int8) {
163	cmd.keyPos = keyPos
164}
165
166func (cmd *baseCmd) SetErr(e error) {
167	cmd.err = e
168}
169
170func (cmd *baseCmd) Err() error {
171	return cmd.err
172}
173
174func (cmd *baseCmd) readTimeout() *time.Duration {
175	return cmd._readTimeout
176}
177
178func (cmd *baseCmd) setReadTimeout(d time.Duration) {
179	cmd._readTimeout = &d
180}
181
182//------------------------------------------------------------------------------
183
184type Cmd struct {
185	baseCmd
186
187	val interface{}
188}
189
190func NewCmd(ctx context.Context, args ...interface{}) *Cmd {
191	return &Cmd{
192		baseCmd: baseCmd{
193			ctx:  ctx,
194			args: args,
195		},
196	}
197}
198
199func (cmd *Cmd) String() string {
200	return cmdString(cmd, cmd.val)
201}
202
203func (cmd *Cmd) Val() interface{} {
204	return cmd.val
205}
206
207func (cmd *Cmd) Result() (interface{}, error) {
208	return cmd.val, cmd.err
209}
210
211func (cmd *Cmd) Text() (string, error) {
212	if cmd.err != nil {
213		return "", cmd.err
214	}
215	switch val := cmd.val.(type) {
216	case string:
217		return val, nil
218	default:
219		err := fmt.Errorf("redis: unexpected type=%T for String", val)
220		return "", err
221	}
222}
223
224func (cmd *Cmd) Int() (int, error) {
225	if cmd.err != nil {
226		return 0, cmd.err
227	}
228	switch val := cmd.val.(type) {
229	case int64:
230		return int(val), nil
231	case string:
232		return strconv.Atoi(val)
233	default:
234		err := fmt.Errorf("redis: unexpected type=%T for Int", val)
235		return 0, err
236	}
237}
238
239func (cmd *Cmd) Int64() (int64, error) {
240	if cmd.err != nil {
241		return 0, cmd.err
242	}
243	switch val := cmd.val.(type) {
244	case int64:
245		return val, nil
246	case string:
247		return strconv.ParseInt(val, 10, 64)
248	default:
249		err := fmt.Errorf("redis: unexpected type=%T for Int64", val)
250		return 0, err
251	}
252}
253
254func (cmd *Cmd) Uint64() (uint64, error) {
255	if cmd.err != nil {
256		return 0, cmd.err
257	}
258	switch val := cmd.val.(type) {
259	case int64:
260		return uint64(val), nil
261	case string:
262		return strconv.ParseUint(val, 10, 64)
263	default:
264		err := fmt.Errorf("redis: unexpected type=%T for Uint64", val)
265		return 0, err
266	}
267}
268
269func (cmd *Cmd) Float32() (float32, error) {
270	if cmd.err != nil {
271		return 0, cmd.err
272	}
273	switch val := cmd.val.(type) {
274	case int64:
275		return float32(val), nil
276	case string:
277		f, err := strconv.ParseFloat(val, 32)
278		if err != nil {
279			return 0, err
280		}
281		return float32(f), nil
282	default:
283		err := fmt.Errorf("redis: unexpected type=%T for Float32", val)
284		return 0, err
285	}
286}
287
288func (cmd *Cmd) Float64() (float64, error) {
289	if cmd.err != nil {
290		return 0, cmd.err
291	}
292	switch val := cmd.val.(type) {
293	case int64:
294		return float64(val), nil
295	case string:
296		return strconv.ParseFloat(val, 64)
297	default:
298		err := fmt.Errorf("redis: unexpected type=%T for Float64", val)
299		return 0, err
300	}
301}
302
303func (cmd *Cmd) Bool() (bool, error) {
304	if cmd.err != nil {
305		return false, cmd.err
306	}
307	switch val := cmd.val.(type) {
308	case int64:
309		return val != 0, nil
310	case string:
311		return strconv.ParseBool(val)
312	default:
313		err := fmt.Errorf("redis: unexpected type=%T for Bool", val)
314		return false, err
315	}
316}
317
318func (cmd *Cmd) readReply(rd *proto.Reader) (err error) {
319	cmd.val, err = rd.ReadReply(sliceParser)
320	return err
321}
322
323// sliceParser implements proto.MultiBulkParse.
324func sliceParser(rd *proto.Reader, n int64) (interface{}, error) {
325	vals := make([]interface{}, n)
326	for i := 0; i < len(vals); i++ {
327		v, err := rd.ReadReply(sliceParser)
328		if err != nil {
329			if err == Nil {
330				vals[i] = nil
331				continue
332			}
333			if err, ok := err.(proto.RedisError); ok {
334				vals[i] = err
335				continue
336			}
337			return nil, err
338		}
339		vals[i] = v
340	}
341	return vals, nil
342}
343
344//------------------------------------------------------------------------------
345
346type SliceCmd struct {
347	baseCmd
348
349	val []interface{}
350}
351
352var _ Cmder = (*SliceCmd)(nil)
353
354func NewSliceCmd(ctx context.Context, args ...interface{}) *SliceCmd {
355	return &SliceCmd{
356		baseCmd: baseCmd{
357			ctx:  ctx,
358			args: args,
359		},
360	}
361}
362
363func (cmd *SliceCmd) Val() []interface{} {
364	return cmd.val
365}
366
367func (cmd *SliceCmd) Result() ([]interface{}, error) {
368	return cmd.val, cmd.err
369}
370
371func (cmd *SliceCmd) String() string {
372	return cmdString(cmd, cmd.val)
373}
374
375// Scan scans the results from the map into a destination struct. The map keys
376// are matched in the Redis struct fields by the `redis:"field"` tag.
377func (cmd *SliceCmd) Scan(dst interface{}) error {
378	if cmd.err != nil {
379		return cmd.err
380	}
381
382	// Pass the list of keys and values.
383	// Skip the first two args for: HMGET key
384	var args []interface{}
385	if cmd.args[0] == "hmget" {
386		args = cmd.args[2:]
387	} else {
388		// Otherwise, it's: MGET field field ...
389		args = cmd.args[1:]
390	}
391
392	return hscan.Scan(dst, args, cmd.val)
393}
394
395func (cmd *SliceCmd) readReply(rd *proto.Reader) error {
396	v, err := rd.ReadArrayReply(sliceParser)
397	if err != nil {
398		return err
399	}
400	cmd.val = v.([]interface{})
401	return nil
402}
403
404//------------------------------------------------------------------------------
405
406type StatusCmd struct {
407	baseCmd
408
409	val string
410}
411
412var _ Cmder = (*StatusCmd)(nil)
413
414func NewStatusCmd(ctx context.Context, args ...interface{}) *StatusCmd {
415	return &StatusCmd{
416		baseCmd: baseCmd{
417			ctx:  ctx,
418			args: args,
419		},
420	}
421}
422
423func (cmd *StatusCmd) Val() string {
424	return cmd.val
425}
426
427func (cmd *StatusCmd) Result() (string, error) {
428	return cmd.val, cmd.err
429}
430
431func (cmd *StatusCmd) String() string {
432	return cmdString(cmd, cmd.val)
433}
434
435func (cmd *StatusCmd) readReply(rd *proto.Reader) (err error) {
436	cmd.val, err = rd.ReadString()
437	return err
438}
439
440//------------------------------------------------------------------------------
441
442type IntCmd struct {
443	baseCmd
444
445	val int64
446}
447
448var _ Cmder = (*IntCmd)(nil)
449
450func NewIntCmd(ctx context.Context, args ...interface{}) *IntCmd {
451	return &IntCmd{
452		baseCmd: baseCmd{
453			ctx:  ctx,
454			args: args,
455		},
456	}
457}
458
459func (cmd *IntCmd) Val() int64 {
460	return cmd.val
461}
462
463func (cmd *IntCmd) Result() (int64, error) {
464	return cmd.val, cmd.err
465}
466
467func (cmd *IntCmd) Uint64() (uint64, error) {
468	return uint64(cmd.val), cmd.err
469}
470
471func (cmd *IntCmd) String() string {
472	return cmdString(cmd, cmd.val)
473}
474
475func (cmd *IntCmd) readReply(rd *proto.Reader) (err error) {
476	cmd.val, err = rd.ReadIntReply()
477	return err
478}
479
480//------------------------------------------------------------------------------
481
482type IntSliceCmd struct {
483	baseCmd
484
485	val []int64
486}
487
488var _ Cmder = (*IntSliceCmd)(nil)
489
490func NewIntSliceCmd(ctx context.Context, args ...interface{}) *IntSliceCmd {
491	return &IntSliceCmd{
492		baseCmd: baseCmd{
493			ctx:  ctx,
494			args: args,
495		},
496	}
497}
498
499func (cmd *IntSliceCmd) Val() []int64 {
500	return cmd.val
501}
502
503func (cmd *IntSliceCmd) Result() ([]int64, error) {
504	return cmd.val, cmd.err
505}
506
507func (cmd *IntSliceCmd) String() string {
508	return cmdString(cmd, cmd.val)
509}
510
511func (cmd *IntSliceCmd) readReply(rd *proto.Reader) error {
512	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
513		cmd.val = make([]int64, n)
514		for i := 0; i < len(cmd.val); i++ {
515			num, err := rd.ReadIntReply()
516			if err != nil {
517				return nil, err
518			}
519			cmd.val[i] = num
520		}
521		return nil, nil
522	})
523	return err
524}
525
526//------------------------------------------------------------------------------
527
528type DurationCmd struct {
529	baseCmd
530
531	val       time.Duration
532	precision time.Duration
533}
534
535var _ Cmder = (*DurationCmd)(nil)
536
537func NewDurationCmd(ctx context.Context, precision time.Duration, args ...interface{}) *DurationCmd {
538	return &DurationCmd{
539		baseCmd: baseCmd{
540			ctx:  ctx,
541			args: args,
542		},
543		precision: precision,
544	}
545}
546
547func (cmd *DurationCmd) Val() time.Duration {
548	return cmd.val
549}
550
551func (cmd *DurationCmd) Result() (time.Duration, error) {
552	return cmd.val, cmd.err
553}
554
555func (cmd *DurationCmd) String() string {
556	return cmdString(cmd, cmd.val)
557}
558
559func (cmd *DurationCmd) readReply(rd *proto.Reader) error {
560	n, err := rd.ReadIntReply()
561	if err != nil {
562		return err
563	}
564	switch n {
565	// -2 if the key does not exist
566	// -1 if the key exists but has no associated expire
567	case -2, -1:
568		cmd.val = time.Duration(n)
569	default:
570		cmd.val = time.Duration(n) * cmd.precision
571	}
572	return nil
573}
574
575//------------------------------------------------------------------------------
576
577type TimeCmd struct {
578	baseCmd
579
580	val time.Time
581}
582
583var _ Cmder = (*TimeCmd)(nil)
584
585func NewTimeCmd(ctx context.Context, args ...interface{}) *TimeCmd {
586	return &TimeCmd{
587		baseCmd: baseCmd{
588			ctx:  ctx,
589			args: args,
590		},
591	}
592}
593
594func (cmd *TimeCmd) Val() time.Time {
595	return cmd.val
596}
597
598func (cmd *TimeCmd) Result() (time.Time, error) {
599	return cmd.val, cmd.err
600}
601
602func (cmd *TimeCmd) String() string {
603	return cmdString(cmd, cmd.val)
604}
605
606func (cmd *TimeCmd) readReply(rd *proto.Reader) error {
607	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
608		if n != 2 {
609			return nil, fmt.Errorf("got %d elements, expected 2", n)
610		}
611
612		sec, err := rd.ReadInt()
613		if err != nil {
614			return nil, err
615		}
616
617		microsec, err := rd.ReadInt()
618		if err != nil {
619			return nil, err
620		}
621
622		cmd.val = time.Unix(sec, microsec*1000)
623		return nil, nil
624	})
625	return err
626}
627
628//------------------------------------------------------------------------------
629
630type BoolCmd struct {
631	baseCmd
632
633	val bool
634}
635
636var _ Cmder = (*BoolCmd)(nil)
637
638func NewBoolCmd(ctx context.Context, args ...interface{}) *BoolCmd {
639	return &BoolCmd{
640		baseCmd: baseCmd{
641			ctx:  ctx,
642			args: args,
643		},
644	}
645}
646
647func (cmd *BoolCmd) Val() bool {
648	return cmd.val
649}
650
651func (cmd *BoolCmd) Result() (bool, error) {
652	return cmd.val, cmd.err
653}
654
655func (cmd *BoolCmd) String() string {
656	return cmdString(cmd, cmd.val)
657}
658
659func (cmd *BoolCmd) readReply(rd *proto.Reader) error {
660	v, err := rd.ReadReply(nil)
661	// `SET key value NX` returns nil when key already exists. But
662	// `SETNX key value` returns bool (0/1). So convert nil to bool.
663	if err == Nil {
664		cmd.val = false
665		return nil
666	}
667	if err != nil {
668		return err
669	}
670	switch v := v.(type) {
671	case int64:
672		cmd.val = v == 1
673		return nil
674	case string:
675		cmd.val = v == "OK"
676		return nil
677	default:
678		return fmt.Errorf("got %T, wanted int64 or string", v)
679	}
680}
681
682//------------------------------------------------------------------------------
683
684type StringCmd struct {
685	baseCmd
686
687	val string
688}
689
690var _ Cmder = (*StringCmd)(nil)
691
692func NewStringCmd(ctx context.Context, args ...interface{}) *StringCmd {
693	return &StringCmd{
694		baseCmd: baseCmd{
695			ctx:  ctx,
696			args: args,
697		},
698	}
699}
700
701func (cmd *StringCmd) Val() string {
702	return cmd.val
703}
704
705func (cmd *StringCmd) Result() (string, error) {
706	return cmd.Val(), cmd.err
707}
708
709func (cmd *StringCmd) Bytes() ([]byte, error) {
710	return util.StringToBytes(cmd.val), cmd.err
711}
712
713func (cmd *StringCmd) Bool() (bool, error) {
714	if cmd.err != nil {
715		return false, cmd.err
716	}
717	return strconv.ParseBool(cmd.val)
718}
719
720func (cmd *StringCmd) Int() (int, error) {
721	if cmd.err != nil {
722		return 0, cmd.err
723	}
724	return strconv.Atoi(cmd.Val())
725}
726
727func (cmd *StringCmd) Int64() (int64, error) {
728	if cmd.err != nil {
729		return 0, cmd.err
730	}
731	return strconv.ParseInt(cmd.Val(), 10, 64)
732}
733
734func (cmd *StringCmd) Uint64() (uint64, error) {
735	if cmd.err != nil {
736		return 0, cmd.err
737	}
738	return strconv.ParseUint(cmd.Val(), 10, 64)
739}
740
741func (cmd *StringCmd) Float32() (float32, error) {
742	if cmd.err != nil {
743		return 0, cmd.err
744	}
745	f, err := strconv.ParseFloat(cmd.Val(), 32)
746	if err != nil {
747		return 0, err
748	}
749	return float32(f), nil
750}
751
752func (cmd *StringCmd) Float64() (float64, error) {
753	if cmd.err != nil {
754		return 0, cmd.err
755	}
756	return strconv.ParseFloat(cmd.Val(), 64)
757}
758
759func (cmd *StringCmd) Time() (time.Time, error) {
760	if cmd.err != nil {
761		return time.Time{}, cmd.err
762	}
763	return time.Parse(time.RFC3339Nano, cmd.Val())
764}
765
766func (cmd *StringCmd) Scan(val interface{}) error {
767	if cmd.err != nil {
768		return cmd.err
769	}
770	return proto.Scan([]byte(cmd.val), val)
771}
772
773func (cmd *StringCmd) String() string {
774	return cmdString(cmd, cmd.val)
775}
776
777func (cmd *StringCmd) readReply(rd *proto.Reader) (err error) {
778	cmd.val, err = rd.ReadString()
779	return err
780}
781
782//------------------------------------------------------------------------------
783
784type FloatCmd struct {
785	baseCmd
786
787	val float64
788}
789
790var _ Cmder = (*FloatCmd)(nil)
791
792func NewFloatCmd(ctx context.Context, args ...interface{}) *FloatCmd {
793	return &FloatCmd{
794		baseCmd: baseCmd{
795			ctx:  ctx,
796			args: args,
797		},
798	}
799}
800
801func (cmd *FloatCmd) Val() float64 {
802	return cmd.val
803}
804
805func (cmd *FloatCmd) Result() (float64, error) {
806	return cmd.Val(), cmd.Err()
807}
808
809func (cmd *FloatCmd) String() string {
810	return cmdString(cmd, cmd.val)
811}
812
813func (cmd *FloatCmd) readReply(rd *proto.Reader) (err error) {
814	cmd.val, err = rd.ReadFloatReply()
815	return err
816}
817
818//------------------------------------------------------------------------------
819
820type FloatSliceCmd struct {
821	baseCmd
822
823	val []float64
824}
825
826var _ Cmder = (*FloatSliceCmd)(nil)
827
828func NewFloatSliceCmd(ctx context.Context, args ...interface{}) *FloatSliceCmd {
829	return &FloatSliceCmd{
830		baseCmd: baseCmd{
831			ctx:  ctx,
832			args: args,
833		},
834	}
835}
836
837func (cmd *FloatSliceCmd) Val() []float64 {
838	return cmd.val
839}
840
841func (cmd *FloatSliceCmd) Result() ([]float64, error) {
842	return cmd.val, cmd.err
843}
844
845func (cmd *FloatSliceCmd) String() string {
846	return cmdString(cmd, cmd.val)
847}
848
849func (cmd *FloatSliceCmd) readReply(rd *proto.Reader) error {
850	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
851		cmd.val = make([]float64, n)
852		for i := 0; i < len(cmd.val); i++ {
853			switch num, err := rd.ReadFloatReply(); {
854			case err == Nil:
855				cmd.val[i] = 0
856			case err != nil:
857				return nil, err
858			default:
859				cmd.val[i] = num
860			}
861		}
862		return nil, nil
863	})
864	return err
865}
866
867//------------------------------------------------------------------------------
868
869type StringSliceCmd struct {
870	baseCmd
871
872	val []string
873}
874
875var _ Cmder = (*StringSliceCmd)(nil)
876
877func NewStringSliceCmd(ctx context.Context, args ...interface{}) *StringSliceCmd {
878	return &StringSliceCmd{
879		baseCmd: baseCmd{
880			ctx:  ctx,
881			args: args,
882		},
883	}
884}
885
886func (cmd *StringSliceCmd) Val() []string {
887	return cmd.val
888}
889
890func (cmd *StringSliceCmd) Result() ([]string, error) {
891	return cmd.Val(), cmd.Err()
892}
893
894func (cmd *StringSliceCmd) String() string {
895	return cmdString(cmd, cmd.val)
896}
897
898func (cmd *StringSliceCmd) ScanSlice(container interface{}) error {
899	return proto.ScanSlice(cmd.Val(), container)
900}
901
902func (cmd *StringSliceCmd) readReply(rd *proto.Reader) error {
903	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
904		cmd.val = make([]string, n)
905		for i := 0; i < len(cmd.val); i++ {
906			switch s, err := rd.ReadString(); {
907			case err == Nil:
908				cmd.val[i] = ""
909			case err != nil:
910				return nil, err
911			default:
912				cmd.val[i] = s
913			}
914		}
915		return nil, nil
916	})
917	return err
918}
919
920//------------------------------------------------------------------------------
921
922type BoolSliceCmd struct {
923	baseCmd
924
925	val []bool
926}
927
928var _ Cmder = (*BoolSliceCmd)(nil)
929
930func NewBoolSliceCmd(ctx context.Context, args ...interface{}) *BoolSliceCmd {
931	return &BoolSliceCmd{
932		baseCmd: baseCmd{
933			ctx:  ctx,
934			args: args,
935		},
936	}
937}
938
939func (cmd *BoolSliceCmd) Val() []bool {
940	return cmd.val
941}
942
943func (cmd *BoolSliceCmd) Result() ([]bool, error) {
944	return cmd.val, cmd.err
945}
946
947func (cmd *BoolSliceCmd) String() string {
948	return cmdString(cmd, cmd.val)
949}
950
951func (cmd *BoolSliceCmd) readReply(rd *proto.Reader) error {
952	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
953		cmd.val = make([]bool, n)
954		for i := 0; i < len(cmd.val); i++ {
955			n, err := rd.ReadIntReply()
956			if err != nil {
957				return nil, err
958			}
959			cmd.val[i] = n == 1
960		}
961		return nil, nil
962	})
963	return err
964}
965
966//------------------------------------------------------------------------------
967
968type StringStringMapCmd struct {
969	baseCmd
970
971	val map[string]string
972}
973
974var _ Cmder = (*StringStringMapCmd)(nil)
975
976func NewStringStringMapCmd(ctx context.Context, args ...interface{}) *StringStringMapCmd {
977	return &StringStringMapCmd{
978		baseCmd: baseCmd{
979			ctx:  ctx,
980			args: args,
981		},
982	}
983}
984
985func (cmd *StringStringMapCmd) Val() map[string]string {
986	return cmd.val
987}
988
989func (cmd *StringStringMapCmd) Result() (map[string]string, error) {
990	return cmd.val, cmd.err
991}
992
993func (cmd *StringStringMapCmd) String() string {
994	return cmdString(cmd, cmd.val)
995}
996
997// Scan scans the results from the map into a destination struct. The map keys
998// are matched in the Redis struct fields by the `redis:"field"` tag.
999func (cmd *StringStringMapCmd) Scan(dst interface{}) error {
1000	if cmd.err != nil {
1001		return cmd.err
1002	}
1003
1004	strct, err := hscan.Struct(dst)
1005	if err != nil {
1006		return err
1007	}
1008
1009	for k, v := range cmd.val {
1010		if err := strct.Scan(k, v); err != nil {
1011			return err
1012		}
1013	}
1014
1015	return nil
1016}
1017
1018func (cmd *StringStringMapCmd) readReply(rd *proto.Reader) error {
1019	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1020		cmd.val = make(map[string]string, n/2)
1021		for i := int64(0); i < n; i += 2 {
1022			key, err := rd.ReadString()
1023			if err != nil {
1024				return nil, err
1025			}
1026
1027			value, err := rd.ReadString()
1028			if err != nil {
1029				return nil, err
1030			}
1031
1032			cmd.val[key] = value
1033		}
1034		return nil, nil
1035	})
1036	return err
1037}
1038
1039//------------------------------------------------------------------------------
1040
1041type StringIntMapCmd struct {
1042	baseCmd
1043
1044	val map[string]int64
1045}
1046
1047var _ Cmder = (*StringIntMapCmd)(nil)
1048
1049func NewStringIntMapCmd(ctx context.Context, args ...interface{}) *StringIntMapCmd {
1050	return &StringIntMapCmd{
1051		baseCmd: baseCmd{
1052			ctx:  ctx,
1053			args: args,
1054		},
1055	}
1056}
1057
1058func (cmd *StringIntMapCmd) Val() map[string]int64 {
1059	return cmd.val
1060}
1061
1062func (cmd *StringIntMapCmd) Result() (map[string]int64, error) {
1063	return cmd.val, cmd.err
1064}
1065
1066func (cmd *StringIntMapCmd) String() string {
1067	return cmdString(cmd, cmd.val)
1068}
1069
1070func (cmd *StringIntMapCmd) readReply(rd *proto.Reader) error {
1071	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1072		cmd.val = make(map[string]int64, n/2)
1073		for i := int64(0); i < n; i += 2 {
1074			key, err := rd.ReadString()
1075			if err != nil {
1076				return nil, err
1077			}
1078
1079			n, err := rd.ReadIntReply()
1080			if err != nil {
1081				return nil, err
1082			}
1083
1084			cmd.val[key] = n
1085		}
1086		return nil, nil
1087	})
1088	return err
1089}
1090
1091//------------------------------------------------------------------------------
1092
1093type StringStructMapCmd struct {
1094	baseCmd
1095
1096	val map[string]struct{}
1097}
1098
1099var _ Cmder = (*StringStructMapCmd)(nil)
1100
1101func NewStringStructMapCmd(ctx context.Context, args ...interface{}) *StringStructMapCmd {
1102	return &StringStructMapCmd{
1103		baseCmd: baseCmd{
1104			ctx:  ctx,
1105			args: args,
1106		},
1107	}
1108}
1109
1110func (cmd *StringStructMapCmd) Val() map[string]struct{} {
1111	return cmd.val
1112}
1113
1114func (cmd *StringStructMapCmd) Result() (map[string]struct{}, error) {
1115	return cmd.val, cmd.err
1116}
1117
1118func (cmd *StringStructMapCmd) String() string {
1119	return cmdString(cmd, cmd.val)
1120}
1121
1122func (cmd *StringStructMapCmd) readReply(rd *proto.Reader) error {
1123	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1124		cmd.val = make(map[string]struct{}, n)
1125		for i := int64(0); i < n; i++ {
1126			key, err := rd.ReadString()
1127			if err != nil {
1128				return nil, err
1129			}
1130			cmd.val[key] = struct{}{}
1131		}
1132		return nil, nil
1133	})
1134	return err
1135}
1136
1137//------------------------------------------------------------------------------
1138
1139type XMessage struct {
1140	ID     string
1141	Values map[string]interface{}
1142}
1143
1144type XMessageSliceCmd struct {
1145	baseCmd
1146
1147	val []XMessage
1148}
1149
1150var _ Cmder = (*XMessageSliceCmd)(nil)
1151
1152func NewXMessageSliceCmd(ctx context.Context, args ...interface{}) *XMessageSliceCmd {
1153	return &XMessageSliceCmd{
1154		baseCmd: baseCmd{
1155			ctx:  ctx,
1156			args: args,
1157		},
1158	}
1159}
1160
1161func (cmd *XMessageSliceCmd) Val() []XMessage {
1162	return cmd.val
1163}
1164
1165func (cmd *XMessageSliceCmd) Result() ([]XMessage, error) {
1166	return cmd.val, cmd.err
1167}
1168
1169func (cmd *XMessageSliceCmd) String() string {
1170	return cmdString(cmd, cmd.val)
1171}
1172
1173func (cmd *XMessageSliceCmd) readReply(rd *proto.Reader) error {
1174	var err error
1175	cmd.val, err = readXMessageSlice(rd)
1176	return err
1177}
1178
1179func readXMessageSlice(rd *proto.Reader) ([]XMessage, error) {
1180	n, err := rd.ReadArrayLen()
1181	if err != nil {
1182		return nil, err
1183	}
1184
1185	msgs := make([]XMessage, n)
1186	for i := 0; i < n; i++ {
1187		var err error
1188		msgs[i], err = readXMessage(rd)
1189		if err != nil {
1190			return nil, err
1191		}
1192	}
1193	return msgs, nil
1194}
1195
1196func readXMessage(rd *proto.Reader) (XMessage, error) {
1197	n, err := rd.ReadArrayLen()
1198	if err != nil {
1199		return XMessage{}, err
1200	}
1201	if n != 2 {
1202		return XMessage{}, fmt.Errorf("got %d, wanted 2", n)
1203	}
1204
1205	id, err := rd.ReadString()
1206	if err != nil {
1207		return XMessage{}, err
1208	}
1209
1210	var values map[string]interface{}
1211
1212	v, err := rd.ReadArrayReply(stringInterfaceMapParser)
1213	if err != nil {
1214		if err != proto.Nil {
1215			return XMessage{}, err
1216		}
1217	} else {
1218		values = v.(map[string]interface{})
1219	}
1220
1221	return XMessage{
1222		ID:     id,
1223		Values: values,
1224	}, nil
1225}
1226
1227// stringInterfaceMapParser implements proto.MultiBulkParse.
1228func stringInterfaceMapParser(rd *proto.Reader, n int64) (interface{}, error) {
1229	m := make(map[string]interface{}, n/2)
1230	for i := int64(0); i < n; i += 2 {
1231		key, err := rd.ReadString()
1232		if err != nil {
1233			return nil, err
1234		}
1235
1236		value, err := rd.ReadString()
1237		if err != nil {
1238			return nil, err
1239		}
1240
1241		m[key] = value
1242	}
1243	return m, nil
1244}
1245
1246//------------------------------------------------------------------------------
1247
1248type XStream struct {
1249	Stream   string
1250	Messages []XMessage
1251}
1252
1253type XStreamSliceCmd struct {
1254	baseCmd
1255
1256	val []XStream
1257}
1258
1259var _ Cmder = (*XStreamSliceCmd)(nil)
1260
1261func NewXStreamSliceCmd(ctx context.Context, args ...interface{}) *XStreamSliceCmd {
1262	return &XStreamSliceCmd{
1263		baseCmd: baseCmd{
1264			ctx:  ctx,
1265			args: args,
1266		},
1267	}
1268}
1269
1270func (cmd *XStreamSliceCmd) Val() []XStream {
1271	return cmd.val
1272}
1273
1274func (cmd *XStreamSliceCmd) Result() ([]XStream, error) {
1275	return cmd.val, cmd.err
1276}
1277
1278func (cmd *XStreamSliceCmd) String() string {
1279	return cmdString(cmd, cmd.val)
1280}
1281
1282func (cmd *XStreamSliceCmd) readReply(rd *proto.Reader) error {
1283	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1284		cmd.val = make([]XStream, n)
1285		for i := 0; i < len(cmd.val); i++ {
1286			i := i
1287			_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1288				if n != 2 {
1289					return nil, fmt.Errorf("got %d, wanted 2", n)
1290				}
1291
1292				stream, err := rd.ReadString()
1293				if err != nil {
1294					return nil, err
1295				}
1296
1297				msgs, err := readXMessageSlice(rd)
1298				if err != nil {
1299					return nil, err
1300				}
1301
1302				cmd.val[i] = XStream{
1303					Stream:   stream,
1304					Messages: msgs,
1305				}
1306				return nil, nil
1307			})
1308			if err != nil {
1309				return nil, err
1310			}
1311		}
1312		return nil, nil
1313	})
1314	return err
1315}
1316
1317//------------------------------------------------------------------------------
1318
1319type XPending struct {
1320	Count     int64
1321	Lower     string
1322	Higher    string
1323	Consumers map[string]int64
1324}
1325
1326type XPendingCmd struct {
1327	baseCmd
1328	val *XPending
1329}
1330
1331var _ Cmder = (*XPendingCmd)(nil)
1332
1333func NewXPendingCmd(ctx context.Context, args ...interface{}) *XPendingCmd {
1334	return &XPendingCmd{
1335		baseCmd: baseCmd{
1336			ctx:  ctx,
1337			args: args,
1338		},
1339	}
1340}
1341
1342func (cmd *XPendingCmd) Val() *XPending {
1343	return cmd.val
1344}
1345
1346func (cmd *XPendingCmd) Result() (*XPending, error) {
1347	return cmd.val, cmd.err
1348}
1349
1350func (cmd *XPendingCmd) String() string {
1351	return cmdString(cmd, cmd.val)
1352}
1353
1354func (cmd *XPendingCmd) readReply(rd *proto.Reader) error {
1355	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1356		if n != 4 {
1357			return nil, fmt.Errorf("got %d, wanted 4", n)
1358		}
1359
1360		count, err := rd.ReadIntReply()
1361		if err != nil {
1362			return nil, err
1363		}
1364
1365		lower, err := rd.ReadString()
1366		if err != nil && err != Nil {
1367			return nil, err
1368		}
1369
1370		higher, err := rd.ReadString()
1371		if err != nil && err != Nil {
1372			return nil, err
1373		}
1374
1375		cmd.val = &XPending{
1376			Count:  count,
1377			Lower:  lower,
1378			Higher: higher,
1379		}
1380		_, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1381			for i := int64(0); i < n; i++ {
1382				_, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1383					if n != 2 {
1384						return nil, fmt.Errorf("got %d, wanted 2", n)
1385					}
1386
1387					consumerName, err := rd.ReadString()
1388					if err != nil {
1389						return nil, err
1390					}
1391
1392					consumerPending, err := rd.ReadInt()
1393					if err != nil {
1394						return nil, err
1395					}
1396
1397					if cmd.val.Consumers == nil {
1398						cmd.val.Consumers = make(map[string]int64)
1399					}
1400					cmd.val.Consumers[consumerName] = consumerPending
1401
1402					return nil, nil
1403				})
1404				if err != nil {
1405					return nil, err
1406				}
1407			}
1408			return nil, nil
1409		})
1410		if err != nil && err != Nil {
1411			return nil, err
1412		}
1413
1414		return nil, nil
1415	})
1416	return err
1417}
1418
1419//------------------------------------------------------------------------------
1420
1421type XPendingExt struct {
1422	ID         string
1423	Consumer   string
1424	Idle       time.Duration
1425	RetryCount int64
1426}
1427
1428type XPendingExtCmd struct {
1429	baseCmd
1430	val []XPendingExt
1431}
1432
1433var _ Cmder = (*XPendingExtCmd)(nil)
1434
1435func NewXPendingExtCmd(ctx context.Context, args ...interface{}) *XPendingExtCmd {
1436	return &XPendingExtCmd{
1437		baseCmd: baseCmd{
1438			ctx:  ctx,
1439			args: args,
1440		},
1441	}
1442}
1443
1444func (cmd *XPendingExtCmd) Val() []XPendingExt {
1445	return cmd.val
1446}
1447
1448func (cmd *XPendingExtCmd) Result() ([]XPendingExt, error) {
1449	return cmd.val, cmd.err
1450}
1451
1452func (cmd *XPendingExtCmd) String() string {
1453	return cmdString(cmd, cmd.val)
1454}
1455
1456func (cmd *XPendingExtCmd) readReply(rd *proto.Reader) error {
1457	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1458		cmd.val = make([]XPendingExt, 0, n)
1459		for i := int64(0); i < n; i++ {
1460			_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1461				if n != 4 {
1462					return nil, fmt.Errorf("got %d, wanted 4", n)
1463				}
1464
1465				id, err := rd.ReadString()
1466				if err != nil {
1467					return nil, err
1468				}
1469
1470				consumer, err := rd.ReadString()
1471				if err != nil && err != Nil {
1472					return nil, err
1473				}
1474
1475				idle, err := rd.ReadIntReply()
1476				if err != nil && err != Nil {
1477					return nil, err
1478				}
1479
1480				retryCount, err := rd.ReadIntReply()
1481				if err != nil && err != Nil {
1482					return nil, err
1483				}
1484
1485				cmd.val = append(cmd.val, XPendingExt{
1486					ID:         id,
1487					Consumer:   consumer,
1488					Idle:       time.Duration(idle) * time.Millisecond,
1489					RetryCount: retryCount,
1490				})
1491				return nil, nil
1492			})
1493			if err != nil {
1494				return nil, err
1495			}
1496		}
1497		return nil, nil
1498	})
1499	return err
1500}
1501
1502//------------------------------------------------------------------------------
1503
1504type XInfoConsumersCmd struct {
1505	baseCmd
1506	val []XInfoConsumer
1507}
1508
1509type XInfoConsumer struct {
1510	Name    string
1511	Pending int64
1512	Idle    int64
1513}
1514
1515var _ Cmder = (*XInfoConsumersCmd)(nil)
1516
1517func NewXInfoConsumersCmd(ctx context.Context, stream string, group string) *XInfoConsumersCmd {
1518	return &XInfoConsumersCmd{
1519		baseCmd: baseCmd{
1520			ctx:  ctx,
1521			args: []interface{}{"xinfo", "consumers", stream, group},
1522		},
1523	}
1524}
1525
1526func (cmd *XInfoConsumersCmd) Val() []XInfoConsumer {
1527	return cmd.val
1528}
1529
1530func (cmd *XInfoConsumersCmd) Result() ([]XInfoConsumer, error) {
1531	return cmd.val, cmd.err
1532}
1533
1534func (cmd *XInfoConsumersCmd) String() string {
1535	return cmdString(cmd, cmd.val)
1536}
1537
1538func (cmd *XInfoConsumersCmd) readReply(rd *proto.Reader) error {
1539	n, err := rd.ReadArrayLen()
1540	if err != nil {
1541		return err
1542	}
1543
1544	cmd.val = make([]XInfoConsumer, n)
1545
1546	for i := 0; i < n; i++ {
1547		cmd.val[i], err = readXConsumerInfo(rd)
1548		if err != nil {
1549			return err
1550		}
1551	}
1552
1553	return nil
1554}
1555
1556func readXConsumerInfo(rd *proto.Reader) (XInfoConsumer, error) {
1557	var consumer XInfoConsumer
1558
1559	n, err := rd.ReadArrayLen()
1560	if err != nil {
1561		return consumer, err
1562	}
1563	if n != 6 {
1564		return consumer, fmt.Errorf("redis: got %d elements in XINFO CONSUMERS reply, wanted 6", n)
1565	}
1566
1567	for i := 0; i < 3; i++ {
1568		key, err := rd.ReadString()
1569		if err != nil {
1570			return consumer, err
1571		}
1572
1573		val, err := rd.ReadString()
1574		if err != nil {
1575			return consumer, err
1576		}
1577
1578		switch key {
1579		case "name":
1580			consumer.Name = val
1581		case "pending":
1582			consumer.Pending, err = strconv.ParseInt(val, 0, 64)
1583			if err != nil {
1584				return consumer, err
1585			}
1586		case "idle":
1587			consumer.Idle, err = strconv.ParseInt(val, 0, 64)
1588			if err != nil {
1589				return consumer, err
1590			}
1591		default:
1592			return consumer, fmt.Errorf("redis: unexpected content %s in XINFO CONSUMERS reply", key)
1593		}
1594	}
1595
1596	return consumer, nil
1597}
1598
1599//------------------------------------------------------------------------------
1600
1601type XInfoGroupsCmd struct {
1602	baseCmd
1603	val []XInfoGroup
1604}
1605
1606type XInfoGroup struct {
1607	Name            string
1608	Consumers       int64
1609	Pending         int64
1610	LastDeliveredID string
1611}
1612
1613var _ Cmder = (*XInfoGroupsCmd)(nil)
1614
1615func NewXInfoGroupsCmd(ctx context.Context, stream string) *XInfoGroupsCmd {
1616	return &XInfoGroupsCmd{
1617		baseCmd: baseCmd{
1618			ctx:  ctx,
1619			args: []interface{}{"xinfo", "groups", stream},
1620		},
1621	}
1622}
1623
1624func (cmd *XInfoGroupsCmd) Val() []XInfoGroup {
1625	return cmd.val
1626}
1627
1628func (cmd *XInfoGroupsCmd) Result() ([]XInfoGroup, error) {
1629	return cmd.val, cmd.err
1630}
1631
1632func (cmd *XInfoGroupsCmd) String() string {
1633	return cmdString(cmd, cmd.val)
1634}
1635
1636func (cmd *XInfoGroupsCmd) readReply(rd *proto.Reader) error {
1637	n, err := rd.ReadArrayLen()
1638	if err != nil {
1639		return err
1640	}
1641
1642	cmd.val = make([]XInfoGroup, n)
1643
1644	for i := 0; i < n; i++ {
1645		cmd.val[i], err = readXGroupInfo(rd)
1646		if err != nil {
1647			return err
1648		}
1649	}
1650
1651	return nil
1652}
1653
1654func readXGroupInfo(rd *proto.Reader) (XInfoGroup, error) {
1655	var group XInfoGroup
1656
1657	n, err := rd.ReadArrayLen()
1658	if err != nil {
1659		return group, err
1660	}
1661	if n != 8 {
1662		return group, fmt.Errorf("redis: got %d elements in XINFO GROUPS reply, wanted 8", n)
1663	}
1664
1665	for i := 0; i < 4; i++ {
1666		key, err := rd.ReadString()
1667		if err != nil {
1668			return group, err
1669		}
1670
1671		val, err := rd.ReadString()
1672		if err != nil {
1673			return group, err
1674		}
1675
1676		switch key {
1677		case "name":
1678			group.Name = val
1679		case "consumers":
1680			group.Consumers, err = strconv.ParseInt(val, 0, 64)
1681			if err != nil {
1682				return group, err
1683			}
1684		case "pending":
1685			group.Pending, err = strconv.ParseInt(val, 0, 64)
1686			if err != nil {
1687				return group, err
1688			}
1689		case "last-delivered-id":
1690			group.LastDeliveredID = val
1691		default:
1692			return group, fmt.Errorf("redis: unexpected content %s in XINFO GROUPS reply", key)
1693		}
1694	}
1695
1696	return group, nil
1697}
1698
1699//------------------------------------------------------------------------------
1700
1701type XInfoStreamCmd struct {
1702	baseCmd
1703	val *XInfoStream
1704}
1705
1706type XInfoStream struct {
1707	Length          int64
1708	RadixTreeKeys   int64
1709	RadixTreeNodes  int64
1710	Groups          int64
1711	LastGeneratedID string
1712	FirstEntry      XMessage
1713	LastEntry       XMessage
1714}
1715
1716var _ Cmder = (*XInfoStreamCmd)(nil)
1717
1718func NewXInfoStreamCmd(ctx context.Context, stream string) *XInfoStreamCmd {
1719	return &XInfoStreamCmd{
1720		baseCmd: baseCmd{
1721			ctx:  ctx,
1722			args: []interface{}{"xinfo", "stream", stream},
1723		},
1724	}
1725}
1726
1727func (cmd *XInfoStreamCmd) Val() *XInfoStream {
1728	return cmd.val
1729}
1730
1731func (cmd *XInfoStreamCmd) Result() (*XInfoStream, error) {
1732	return cmd.val, cmd.err
1733}
1734
1735func (cmd *XInfoStreamCmd) String() string {
1736	return cmdString(cmd, cmd.val)
1737}
1738
1739func (cmd *XInfoStreamCmd) readReply(rd *proto.Reader) error {
1740	v, err := rd.ReadReply(xStreamInfoParser)
1741	if err != nil {
1742		return err
1743	}
1744	cmd.val = v.(*XInfoStream)
1745	return nil
1746}
1747
1748func xStreamInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
1749	if n != 14 {
1750		return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM reply,"+
1751			"wanted 14", n)
1752	}
1753	var info XInfoStream
1754	for i := 0; i < 7; i++ {
1755		key, err := rd.ReadString()
1756		if err != nil {
1757			return nil, err
1758		}
1759		switch key {
1760		case "length":
1761			info.Length, err = rd.ReadIntReply()
1762		case "radix-tree-keys":
1763			info.RadixTreeKeys, err = rd.ReadIntReply()
1764		case "radix-tree-nodes":
1765			info.RadixTreeNodes, err = rd.ReadIntReply()
1766		case "groups":
1767			info.Groups, err = rd.ReadIntReply()
1768		case "last-generated-id":
1769			info.LastGeneratedID, err = rd.ReadString()
1770		case "first-entry":
1771			info.FirstEntry, err = readXMessage(rd)
1772			if err == Nil {
1773				err = nil
1774			}
1775		case "last-entry":
1776			info.LastEntry, err = readXMessage(rd)
1777			if err == Nil {
1778				err = nil
1779			}
1780		default:
1781			return nil, fmt.Errorf("redis: unexpected content %s "+
1782				"in XINFO STREAM reply", key)
1783		}
1784		if err != nil {
1785			return nil, err
1786		}
1787	}
1788	return &info, nil
1789}
1790
1791//------------------------------------------------------------------------------
1792
1793type XInfoStreamFullCmd struct {
1794	baseCmd
1795	val *XInfoStreamFull
1796}
1797
1798type XInfoStreamFull struct {
1799	Length          int64
1800	RadixTreeKeys   int64
1801	RadixTreeNodes  int64
1802	LastGeneratedID string
1803	Entries         []XMessage
1804	Groups          []XInfoStreamGroup
1805}
1806
1807type XInfoStreamGroup struct {
1808	Name            string
1809	LastDeliveredID string
1810	PelCount        int64
1811	Pending         []XInfoStreamGroupPending
1812	Consumers       []XInfoStreamConsumer
1813}
1814
1815type XInfoStreamGroupPending struct {
1816	ID            string
1817	Consumer      string
1818	DeliveryTime  time.Time
1819	DeliveryCount int64
1820}
1821
1822type XInfoStreamConsumer struct {
1823	Name     string
1824	SeenTime time.Time
1825	PelCount int64
1826	Pending  []XInfoStreamConsumerPending
1827}
1828
1829type XInfoStreamConsumerPending struct {
1830	ID            string
1831	DeliveryTime  time.Time
1832	DeliveryCount int64
1833}
1834
1835var _ Cmder = (*XInfoStreamFullCmd)(nil)
1836
1837func NewXInfoStreamFullCmd(ctx context.Context, args ...interface{}) *XInfoStreamFullCmd {
1838	return &XInfoStreamFullCmd{
1839		baseCmd: baseCmd{
1840			ctx:  ctx,
1841			args: args,
1842		},
1843	}
1844}
1845
1846func (cmd *XInfoStreamFullCmd) Val() *XInfoStreamFull {
1847	return cmd.val
1848}
1849
1850func (cmd *XInfoStreamFullCmd) Result() (*XInfoStreamFull, error) {
1851	return cmd.val, cmd.err
1852}
1853
1854func (cmd *XInfoStreamFullCmd) String() string {
1855	return cmdString(cmd, cmd.val)
1856}
1857
1858func (cmd *XInfoStreamFullCmd) readReply(rd *proto.Reader) error {
1859	n, err := rd.ReadArrayLen()
1860	if err != nil {
1861		return err
1862	}
1863	if n != 12 {
1864		return fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+
1865			"wanted 12", n)
1866	}
1867
1868	cmd.val = &XInfoStreamFull{}
1869
1870	for i := 0; i < 6; i++ {
1871		key, err := rd.ReadString()
1872		if err != nil {
1873			return err
1874		}
1875
1876		switch key {
1877		case "length":
1878			cmd.val.Length, err = rd.ReadIntReply()
1879		case "radix-tree-keys":
1880			cmd.val.RadixTreeKeys, err = rd.ReadIntReply()
1881		case "radix-tree-nodes":
1882			cmd.val.RadixTreeNodes, err = rd.ReadIntReply()
1883		case "last-generated-id":
1884			cmd.val.LastGeneratedID, err = rd.ReadString()
1885		case "entries":
1886			cmd.val.Entries, err = readXMessageSlice(rd)
1887		case "groups":
1888			cmd.val.Groups, err = readStreamGroups(rd)
1889		default:
1890			return fmt.Errorf("redis: unexpected content %s "+
1891				"in XINFO STREAM reply", key)
1892		}
1893		if err != nil {
1894			return err
1895		}
1896	}
1897	return nil
1898}
1899
1900func readStreamGroups(rd *proto.Reader) ([]XInfoStreamGroup, error) {
1901	n, err := rd.ReadArrayLen()
1902	if err != nil {
1903		return nil, err
1904	}
1905	groups := make([]XInfoStreamGroup, 0, n)
1906	for i := 0; i < n; i++ {
1907		nn, err := rd.ReadArrayLen()
1908		if err != nil {
1909			return nil, err
1910		}
1911		if nn != 10 {
1912			return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+
1913				"wanted 10", nn)
1914		}
1915
1916		group := XInfoStreamGroup{}
1917
1918		for f := 0; f < 5; f++ {
1919			key, err := rd.ReadString()
1920			if err != nil {
1921				return nil, err
1922			}
1923
1924			switch key {
1925			case "name":
1926				group.Name, err = rd.ReadString()
1927			case "last-delivered-id":
1928				group.LastDeliveredID, err = rd.ReadString()
1929			case "pel-count":
1930				group.PelCount, err = rd.ReadIntReply()
1931			case "pending":
1932				group.Pending, err = readXInfoStreamGroupPending(rd)
1933			case "consumers":
1934				group.Consumers, err = readXInfoStreamConsumers(rd)
1935			default:
1936				return nil, fmt.Errorf("redis: unexpected content %s "+
1937					"in XINFO STREAM reply", key)
1938			}
1939
1940			if err != nil {
1941				return nil, err
1942			}
1943		}
1944
1945		groups = append(groups, group)
1946	}
1947
1948	return groups, nil
1949}
1950
1951func readXInfoStreamGroupPending(rd *proto.Reader) ([]XInfoStreamGroupPending, error) {
1952	n, err := rd.ReadArrayLen()
1953	if err != nil {
1954		return nil, err
1955	}
1956
1957	pending := make([]XInfoStreamGroupPending, 0, n)
1958
1959	for i := 0; i < n; i++ {
1960		nn, err := rd.ReadArrayLen()
1961		if err != nil {
1962			return nil, err
1963		}
1964		if nn != 4 {
1965			return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+
1966				"wanted 4", nn)
1967		}
1968
1969		p := XInfoStreamGroupPending{}
1970
1971		p.ID, err = rd.ReadString()
1972		if err != nil {
1973			return nil, err
1974		}
1975
1976		p.Consumer, err = rd.ReadString()
1977		if err != nil {
1978			return nil, err
1979		}
1980
1981		delivery, err := rd.ReadIntReply()
1982		if err != nil {
1983			return nil, err
1984		}
1985		p.DeliveryTime = time.Unix(delivery/1000, delivery%1000*int64(time.Millisecond))
1986
1987		p.DeliveryCount, err = rd.ReadIntReply()
1988		if err != nil {
1989			return nil, err
1990		}
1991
1992		pending = append(pending, p)
1993	}
1994
1995	return pending, nil
1996}
1997
1998func readXInfoStreamConsumers(rd *proto.Reader) ([]XInfoStreamConsumer, error) {
1999	n, err := rd.ReadArrayLen()
2000	if err != nil {
2001		return nil, err
2002	}
2003
2004	consumers := make([]XInfoStreamConsumer, 0, n)
2005
2006	for i := 0; i < n; i++ {
2007		nn, err := rd.ReadArrayLen()
2008		if err != nil {
2009			return nil, err
2010		}
2011		if nn != 8 {
2012			return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+
2013				"wanted 8", nn)
2014		}
2015
2016		c := XInfoStreamConsumer{}
2017
2018		for f := 0; f < 4; f++ {
2019			cKey, err := rd.ReadString()
2020			if err != nil {
2021				return nil, err
2022			}
2023
2024			switch cKey {
2025			case "name":
2026				c.Name, err = rd.ReadString()
2027			case "seen-time":
2028				seen, err := rd.ReadIntReply()
2029				if err != nil {
2030					return nil, err
2031				}
2032				c.SeenTime = time.Unix(seen/1000, seen%1000*int64(time.Millisecond))
2033			case "pel-count":
2034				c.PelCount, err = rd.ReadIntReply()
2035			case "pending":
2036				pendingNumber, err := rd.ReadArrayLen()
2037				if err != nil {
2038					return nil, err
2039				}
2040
2041				c.Pending = make([]XInfoStreamConsumerPending, 0, pendingNumber)
2042
2043				for pn := 0; pn < pendingNumber; pn++ {
2044					nn, err := rd.ReadArrayLen()
2045					if err != nil {
2046						return nil, err
2047					}
2048					if nn != 3 {
2049						return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM reply,"+
2050							"wanted 3", nn)
2051					}
2052
2053					p := XInfoStreamConsumerPending{}
2054
2055					p.ID, err = rd.ReadString()
2056					if err != nil {
2057						return nil, err
2058					}
2059
2060					delivery, err := rd.ReadIntReply()
2061					if err != nil {
2062						return nil, err
2063					}
2064					p.DeliveryTime = time.Unix(delivery/1000, delivery%1000*int64(time.Millisecond))
2065
2066					p.DeliveryCount, err = rd.ReadIntReply()
2067					if err != nil {
2068						return nil, err
2069					}
2070
2071					c.Pending = append(c.Pending, p)
2072				}
2073			default:
2074				return nil, fmt.Errorf("redis: unexpected content %s "+
2075					"in XINFO STREAM reply", cKey)
2076			}
2077			if err != nil {
2078				return nil, err
2079			}
2080		}
2081		consumers = append(consumers, c)
2082	}
2083
2084	return consumers, nil
2085}
2086
2087//------------------------------------------------------------------------------
2088
2089type ZSliceCmd struct {
2090	baseCmd
2091
2092	val []Z
2093}
2094
2095var _ Cmder = (*ZSliceCmd)(nil)
2096
2097func NewZSliceCmd(ctx context.Context, args ...interface{}) *ZSliceCmd {
2098	return &ZSliceCmd{
2099		baseCmd: baseCmd{
2100			ctx:  ctx,
2101			args: args,
2102		},
2103	}
2104}
2105
2106func (cmd *ZSliceCmd) Val() []Z {
2107	return cmd.val
2108}
2109
2110func (cmd *ZSliceCmd) Result() ([]Z, error) {
2111	return cmd.val, cmd.err
2112}
2113
2114func (cmd *ZSliceCmd) String() string {
2115	return cmdString(cmd, cmd.val)
2116}
2117
2118func (cmd *ZSliceCmd) readReply(rd *proto.Reader) error {
2119	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2120		cmd.val = make([]Z, n/2)
2121		for i := 0; i < len(cmd.val); i++ {
2122			member, err := rd.ReadString()
2123			if err != nil {
2124				return nil, err
2125			}
2126
2127			score, err := rd.ReadFloatReply()
2128			if err != nil {
2129				return nil, err
2130			}
2131
2132			cmd.val[i] = Z{
2133				Member: member,
2134				Score:  score,
2135			}
2136		}
2137		return nil, nil
2138	})
2139	return err
2140}
2141
2142//------------------------------------------------------------------------------
2143
2144type ZWithKeyCmd struct {
2145	baseCmd
2146
2147	val *ZWithKey
2148}
2149
2150var _ Cmder = (*ZWithKeyCmd)(nil)
2151
2152func NewZWithKeyCmd(ctx context.Context, args ...interface{}) *ZWithKeyCmd {
2153	return &ZWithKeyCmd{
2154		baseCmd: baseCmd{
2155			ctx:  ctx,
2156			args: args,
2157		},
2158	}
2159}
2160
2161func (cmd *ZWithKeyCmd) Val() *ZWithKey {
2162	return cmd.val
2163}
2164
2165func (cmd *ZWithKeyCmd) Result() (*ZWithKey, error) {
2166	return cmd.Val(), cmd.Err()
2167}
2168
2169func (cmd *ZWithKeyCmd) String() string {
2170	return cmdString(cmd, cmd.val)
2171}
2172
2173func (cmd *ZWithKeyCmd) readReply(rd *proto.Reader) error {
2174	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2175		if n != 3 {
2176			return nil, fmt.Errorf("got %d elements, expected 3", n)
2177		}
2178
2179		cmd.val = &ZWithKey{}
2180		var err error
2181
2182		cmd.val.Key, err = rd.ReadString()
2183		if err != nil {
2184			return nil, err
2185		}
2186
2187		cmd.val.Member, err = rd.ReadString()
2188		if err != nil {
2189			return nil, err
2190		}
2191
2192		cmd.val.Score, err = rd.ReadFloatReply()
2193		if err != nil {
2194			return nil, err
2195		}
2196
2197		return nil, nil
2198	})
2199	return err
2200}
2201
2202//------------------------------------------------------------------------------
2203
2204type ScanCmd struct {
2205	baseCmd
2206
2207	page   []string
2208	cursor uint64
2209
2210	process cmdable
2211}
2212
2213var _ Cmder = (*ScanCmd)(nil)
2214
2215func NewScanCmd(ctx context.Context, process cmdable, args ...interface{}) *ScanCmd {
2216	return &ScanCmd{
2217		baseCmd: baseCmd{
2218			ctx:  ctx,
2219			args: args,
2220		},
2221		process: process,
2222	}
2223}
2224
2225func (cmd *ScanCmd) Val() (keys []string, cursor uint64) {
2226	return cmd.page, cmd.cursor
2227}
2228
2229func (cmd *ScanCmd) Result() (keys []string, cursor uint64, err error) {
2230	return cmd.page, cmd.cursor, cmd.err
2231}
2232
2233func (cmd *ScanCmd) String() string {
2234	return cmdString(cmd, cmd.page)
2235}
2236
2237func (cmd *ScanCmd) readReply(rd *proto.Reader) (err error) {
2238	cmd.page, cmd.cursor, err = rd.ReadScanReply()
2239	return err
2240}
2241
2242// Iterator creates a new ScanIterator.
2243func (cmd *ScanCmd) Iterator() *ScanIterator {
2244	return &ScanIterator{
2245		cmd: cmd,
2246	}
2247}
2248
2249//------------------------------------------------------------------------------
2250
2251type ClusterNode struct {
2252	ID   string
2253	Addr string
2254}
2255
2256type ClusterSlot struct {
2257	Start int
2258	End   int
2259	Nodes []ClusterNode
2260}
2261
2262type ClusterSlotsCmd struct {
2263	baseCmd
2264
2265	val []ClusterSlot
2266}
2267
2268var _ Cmder = (*ClusterSlotsCmd)(nil)
2269
2270func NewClusterSlotsCmd(ctx context.Context, args ...interface{}) *ClusterSlotsCmd {
2271	return &ClusterSlotsCmd{
2272		baseCmd: baseCmd{
2273			ctx:  ctx,
2274			args: args,
2275		},
2276	}
2277}
2278
2279func (cmd *ClusterSlotsCmd) Val() []ClusterSlot {
2280	return cmd.val
2281}
2282
2283func (cmd *ClusterSlotsCmd) Result() ([]ClusterSlot, error) {
2284	return cmd.Val(), cmd.Err()
2285}
2286
2287func (cmd *ClusterSlotsCmd) String() string {
2288	return cmdString(cmd, cmd.val)
2289}
2290
2291func (cmd *ClusterSlotsCmd) readReply(rd *proto.Reader) error {
2292	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2293		cmd.val = make([]ClusterSlot, n)
2294		for i := 0; i < len(cmd.val); i++ {
2295			n, err := rd.ReadArrayLen()
2296			if err != nil {
2297				return nil, err
2298			}
2299			if n < 2 {
2300				err := fmt.Errorf("redis: got %d elements in cluster info, expected at least 2", n)
2301				return nil, err
2302			}
2303
2304			start, err := rd.ReadIntReply()
2305			if err != nil {
2306				return nil, err
2307			}
2308
2309			end, err := rd.ReadIntReply()
2310			if err != nil {
2311				return nil, err
2312			}
2313
2314			nodes := make([]ClusterNode, n-2)
2315			for j := 0; j < len(nodes); j++ {
2316				n, err := rd.ReadArrayLen()
2317				if err != nil {
2318					return nil, err
2319				}
2320				if n != 2 && n != 3 {
2321					err := fmt.Errorf("got %d elements in cluster info address, expected 2 or 3", n)
2322					return nil, err
2323				}
2324
2325				ip, err := rd.ReadString()
2326				if err != nil {
2327					return nil, err
2328				}
2329
2330				port, err := rd.ReadString()
2331				if err != nil {
2332					return nil, err
2333				}
2334
2335				nodes[j].Addr = net.JoinHostPort(ip, port)
2336
2337				if n == 3 {
2338					id, err := rd.ReadString()
2339					if err != nil {
2340						return nil, err
2341					}
2342					nodes[j].ID = id
2343				}
2344			}
2345
2346			cmd.val[i] = ClusterSlot{
2347				Start: int(start),
2348				End:   int(end),
2349				Nodes: nodes,
2350			}
2351		}
2352		return nil, nil
2353	})
2354	return err
2355}
2356
2357//------------------------------------------------------------------------------
2358
2359// GeoLocation is used with GeoAdd to add geospatial location.
2360type GeoLocation struct {
2361	Name                      string
2362	Longitude, Latitude, Dist float64
2363	GeoHash                   int64
2364}
2365
2366// GeoRadiusQuery is used with GeoRadius to query geospatial index.
2367type GeoRadiusQuery struct {
2368	Radius float64
2369	// Can be m, km, ft, or mi. Default is km.
2370	Unit        string
2371	WithCoord   bool
2372	WithDist    bool
2373	WithGeoHash bool
2374	Count       int
2375	// Can be ASC or DESC. Default is no sort order.
2376	Sort      string
2377	Store     string
2378	StoreDist string
2379}
2380
2381type GeoLocationCmd struct {
2382	baseCmd
2383
2384	q         *GeoRadiusQuery
2385	locations []GeoLocation
2386}
2387
2388var _ Cmder = (*GeoLocationCmd)(nil)
2389
2390func NewGeoLocationCmd(ctx context.Context, q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd {
2391	return &GeoLocationCmd{
2392		baseCmd: baseCmd{
2393			ctx:  ctx,
2394			args: geoLocationArgs(q, args...),
2395		},
2396		q: q,
2397	}
2398}
2399
2400func geoLocationArgs(q *GeoRadiusQuery, args ...interface{}) []interface{} {
2401	args = append(args, q.Radius)
2402	if q.Unit != "" {
2403		args = append(args, q.Unit)
2404	} else {
2405		args = append(args, "km")
2406	}
2407	if q.WithCoord {
2408		args = append(args, "withcoord")
2409	}
2410	if q.WithDist {
2411		args = append(args, "withdist")
2412	}
2413	if q.WithGeoHash {
2414		args = append(args, "withhash")
2415	}
2416	if q.Count > 0 {
2417		args = append(args, "count", q.Count)
2418	}
2419	if q.Sort != "" {
2420		args = append(args, q.Sort)
2421	}
2422	if q.Store != "" {
2423		args = append(args, "store")
2424		args = append(args, q.Store)
2425	}
2426	if q.StoreDist != "" {
2427		args = append(args, "storedist")
2428		args = append(args, q.StoreDist)
2429	}
2430	return args
2431}
2432
2433func (cmd *GeoLocationCmd) Val() []GeoLocation {
2434	return cmd.locations
2435}
2436
2437func (cmd *GeoLocationCmd) Result() ([]GeoLocation, error) {
2438	return cmd.locations, cmd.err
2439}
2440
2441func (cmd *GeoLocationCmd) String() string {
2442	return cmdString(cmd, cmd.locations)
2443}
2444
2445func (cmd *GeoLocationCmd) readReply(rd *proto.Reader) error {
2446	v, err := rd.ReadArrayReply(newGeoLocationSliceParser(cmd.q))
2447	if err != nil {
2448		return err
2449	}
2450	cmd.locations = v.([]GeoLocation)
2451	return nil
2452}
2453
2454func newGeoLocationSliceParser(q *GeoRadiusQuery) proto.MultiBulkParse {
2455	return func(rd *proto.Reader, n int64) (interface{}, error) {
2456		locs := make([]GeoLocation, 0, n)
2457		for i := int64(0); i < n; i++ {
2458			v, err := rd.ReadReply(newGeoLocationParser(q))
2459			if err != nil {
2460				return nil, err
2461			}
2462			switch vv := v.(type) {
2463			case string:
2464				locs = append(locs, GeoLocation{
2465					Name: vv,
2466				})
2467			case *GeoLocation:
2468				// TODO: avoid copying
2469				locs = append(locs, *vv)
2470			default:
2471				return nil, fmt.Errorf("got %T, expected string or *GeoLocation", v)
2472			}
2473		}
2474		return locs, nil
2475	}
2476}
2477
2478func newGeoLocationParser(q *GeoRadiusQuery) proto.MultiBulkParse {
2479	return func(rd *proto.Reader, n int64) (interface{}, error) {
2480		var loc GeoLocation
2481		var err error
2482
2483		loc.Name, err = rd.ReadString()
2484		if err != nil {
2485			return nil, err
2486		}
2487		if q.WithDist {
2488			loc.Dist, err = rd.ReadFloatReply()
2489			if err != nil {
2490				return nil, err
2491			}
2492		}
2493		if q.WithGeoHash {
2494			loc.GeoHash, err = rd.ReadIntReply()
2495			if err != nil {
2496				return nil, err
2497			}
2498		}
2499		if q.WithCoord {
2500			n, err := rd.ReadArrayLen()
2501			if err != nil {
2502				return nil, err
2503			}
2504			if n != 2 {
2505				return nil, fmt.Errorf("got %d coordinates, expected 2", n)
2506			}
2507
2508			loc.Longitude, err = rd.ReadFloatReply()
2509			if err != nil {
2510				return nil, err
2511			}
2512			loc.Latitude, err = rd.ReadFloatReply()
2513			if err != nil {
2514				return nil, err
2515			}
2516		}
2517
2518		return &loc, nil
2519	}
2520}
2521
2522//------------------------------------------------------------------------------
2523
2524type GeoPos struct {
2525	Longitude, Latitude float64
2526}
2527
2528type GeoPosCmd struct {
2529	baseCmd
2530
2531	val []*GeoPos
2532}
2533
2534var _ Cmder = (*GeoPosCmd)(nil)
2535
2536func NewGeoPosCmd(ctx context.Context, args ...interface{}) *GeoPosCmd {
2537	return &GeoPosCmd{
2538		baseCmd: baseCmd{
2539			ctx:  ctx,
2540			args: args,
2541		},
2542	}
2543}
2544
2545func (cmd *GeoPosCmd) Val() []*GeoPos {
2546	return cmd.val
2547}
2548
2549func (cmd *GeoPosCmd) Result() ([]*GeoPos, error) {
2550	return cmd.Val(), cmd.Err()
2551}
2552
2553func (cmd *GeoPosCmd) String() string {
2554	return cmdString(cmd, cmd.val)
2555}
2556
2557func (cmd *GeoPosCmd) readReply(rd *proto.Reader) error {
2558	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2559		cmd.val = make([]*GeoPos, n)
2560		for i := 0; i < len(cmd.val); i++ {
2561			i := i
2562			_, err := rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2563				longitude, err := rd.ReadFloatReply()
2564				if err != nil {
2565					return nil, err
2566				}
2567
2568				latitude, err := rd.ReadFloatReply()
2569				if err != nil {
2570					return nil, err
2571				}
2572
2573				cmd.val[i] = &GeoPos{
2574					Longitude: longitude,
2575					Latitude:  latitude,
2576				}
2577				return nil, nil
2578			})
2579			if err != nil {
2580				if err == Nil {
2581					cmd.val[i] = nil
2582					continue
2583				}
2584				return nil, err
2585			}
2586		}
2587		return nil, nil
2588	})
2589	return err
2590}
2591
2592//------------------------------------------------------------------------------
2593
2594type CommandInfo struct {
2595	Name        string
2596	Arity       int8
2597	Flags       []string
2598	ACLFlags    []string
2599	FirstKeyPos int8
2600	LastKeyPos  int8
2601	StepCount   int8
2602	ReadOnly    bool
2603}
2604
2605type CommandsInfoCmd struct {
2606	baseCmd
2607
2608	val map[string]*CommandInfo
2609}
2610
2611var _ Cmder = (*CommandsInfoCmd)(nil)
2612
2613func NewCommandsInfoCmd(ctx context.Context, args ...interface{}) *CommandsInfoCmd {
2614	return &CommandsInfoCmd{
2615		baseCmd: baseCmd{
2616			ctx:  ctx,
2617			args: args,
2618		},
2619	}
2620}
2621
2622func (cmd *CommandsInfoCmd) Val() map[string]*CommandInfo {
2623	return cmd.val
2624}
2625
2626func (cmd *CommandsInfoCmd) Result() (map[string]*CommandInfo, error) {
2627	return cmd.Val(), cmd.Err()
2628}
2629
2630func (cmd *CommandsInfoCmd) String() string {
2631	return cmdString(cmd, cmd.val)
2632}
2633
2634func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error {
2635	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2636		cmd.val = make(map[string]*CommandInfo, n)
2637		for i := int64(0); i < n; i++ {
2638			v, err := rd.ReadReply(commandInfoParser)
2639			if err != nil {
2640				return nil, err
2641			}
2642			vv := v.(*CommandInfo)
2643			cmd.val[vv.Name] = vv
2644		}
2645		return nil, nil
2646	})
2647	return err
2648}
2649
2650func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
2651	const numArgRedis5 = 6
2652	const numArgRedis6 = 7
2653
2654	switch n {
2655	case numArgRedis5, numArgRedis6:
2656		// continue
2657	default:
2658		return nil, fmt.Errorf("redis: got %d elements in COMMAND reply, wanted 7", n)
2659	}
2660
2661	var cmd CommandInfo
2662	var err error
2663
2664	cmd.Name, err = rd.ReadString()
2665	if err != nil {
2666		return nil, err
2667	}
2668
2669	arity, err := rd.ReadIntReply()
2670	if err != nil {
2671		return nil, err
2672	}
2673	cmd.Arity = int8(arity)
2674
2675	_, err = rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2676		cmd.Flags = make([]string, n)
2677		for i := 0; i < len(cmd.Flags); i++ {
2678			switch s, err := rd.ReadString(); {
2679			case err == Nil:
2680				cmd.Flags[i] = ""
2681			case err != nil:
2682				return nil, err
2683			default:
2684				cmd.Flags[i] = s
2685			}
2686		}
2687		return nil, nil
2688	})
2689	if err != nil {
2690		return nil, err
2691	}
2692
2693	firstKeyPos, err := rd.ReadIntReply()
2694	if err != nil {
2695		return nil, err
2696	}
2697	cmd.FirstKeyPos = int8(firstKeyPos)
2698
2699	lastKeyPos, err := rd.ReadIntReply()
2700	if err != nil {
2701		return nil, err
2702	}
2703	cmd.LastKeyPos = int8(lastKeyPos)
2704
2705	stepCount, err := rd.ReadIntReply()
2706	if err != nil {
2707		return nil, err
2708	}
2709	cmd.StepCount = int8(stepCount)
2710
2711	for _, flag := range cmd.Flags {
2712		if flag == "readonly" {
2713			cmd.ReadOnly = true
2714			break
2715		}
2716	}
2717
2718	if n == numArgRedis5 {
2719		return &cmd, nil
2720	}
2721
2722	_, err = rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2723		cmd.ACLFlags = make([]string, n)
2724		for i := 0; i < len(cmd.ACLFlags); i++ {
2725			switch s, err := rd.ReadString(); {
2726			case err == Nil:
2727				cmd.ACLFlags[i] = ""
2728			case err != nil:
2729				return nil, err
2730			default:
2731				cmd.ACLFlags[i] = s
2732			}
2733		}
2734		return nil, nil
2735	})
2736	if err != nil {
2737		return nil, err
2738	}
2739
2740	return &cmd, nil
2741}
2742
2743//------------------------------------------------------------------------------
2744
2745type cmdsInfoCache struct {
2746	fn func(ctx context.Context) (map[string]*CommandInfo, error)
2747
2748	once internal.Once
2749	cmds map[string]*CommandInfo
2750}
2751
2752func newCmdsInfoCache(fn func(ctx context.Context) (map[string]*CommandInfo, error)) *cmdsInfoCache {
2753	return &cmdsInfoCache{
2754		fn: fn,
2755	}
2756}
2757
2758func (c *cmdsInfoCache) Get(ctx context.Context) (map[string]*CommandInfo, error) {
2759	err := c.once.Do(func() error {
2760		cmds, err := c.fn(ctx)
2761		if err != nil {
2762			return err
2763		}
2764
2765		// Extensions have cmd names in upper case. Convert them to lower case.
2766		for k, v := range cmds {
2767			lower := internal.ToLower(k)
2768			if lower != k {
2769				cmds[lower] = v
2770			}
2771		}
2772
2773		c.cmds = cmds
2774		return nil
2775	})
2776	return c.cmds, err
2777}
2778
2779//------------------------------------------------------------------------------
2780
2781type SlowLog struct {
2782	ID       int64
2783	Time     time.Time
2784	Duration time.Duration
2785	Args     []string
2786	// These are also optional fields emitted only by Redis 4.0 or greater:
2787	// https://redis.io/commands/slowlog#output-format
2788	ClientAddr string
2789	ClientName string
2790}
2791
2792type SlowLogCmd struct {
2793	baseCmd
2794
2795	val []SlowLog
2796}
2797
2798var _ Cmder = (*SlowLogCmd)(nil)
2799
2800func NewSlowLogCmd(ctx context.Context, args ...interface{}) *SlowLogCmd {
2801	return &SlowLogCmd{
2802		baseCmd: baseCmd{
2803			ctx:  ctx,
2804			args: args,
2805		},
2806	}
2807}
2808
2809func (cmd *SlowLogCmd) Val() []SlowLog {
2810	return cmd.val
2811}
2812
2813func (cmd *SlowLogCmd) Result() ([]SlowLog, error) {
2814	return cmd.Val(), cmd.Err()
2815}
2816
2817func (cmd *SlowLogCmd) String() string {
2818	return cmdString(cmd, cmd.val)
2819}
2820
2821func (cmd *SlowLogCmd) readReply(rd *proto.Reader) error {
2822	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2823		cmd.val = make([]SlowLog, n)
2824		for i := 0; i < len(cmd.val); i++ {
2825			n, err := rd.ReadArrayLen()
2826			if err != nil {
2827				return nil, err
2828			}
2829			if n < 4 {
2830				err := fmt.Errorf("redis: got %d elements in slowlog get, expected at least 4", n)
2831				return nil, err
2832			}
2833
2834			id, err := rd.ReadIntReply()
2835			if err != nil {
2836				return nil, err
2837			}
2838
2839			createdAt, err := rd.ReadIntReply()
2840			if err != nil {
2841				return nil, err
2842			}
2843			createdAtTime := time.Unix(createdAt, 0)
2844
2845			costs, err := rd.ReadIntReply()
2846			if err != nil {
2847				return nil, err
2848			}
2849			costsDuration := time.Duration(costs) * time.Microsecond
2850
2851			cmdLen, err := rd.ReadArrayLen()
2852			if err != nil {
2853				return nil, err
2854			}
2855			if cmdLen < 1 {
2856				err := fmt.Errorf("redis: got %d elements commands reply in slowlog get, expected at least 1", cmdLen)
2857				return nil, err
2858			}
2859
2860			cmdString := make([]string, cmdLen)
2861			for i := 0; i < cmdLen; i++ {
2862				cmdString[i], err = rd.ReadString()
2863				if err != nil {
2864					return nil, err
2865				}
2866			}
2867
2868			var address, name string
2869			for i := 4; i < n; i++ {
2870				str, err := rd.ReadString()
2871				if err != nil {
2872					return nil, err
2873				}
2874				if i == 4 {
2875					address = str
2876				} else if i == 5 {
2877					name = str
2878				}
2879			}
2880
2881			cmd.val[i] = SlowLog{
2882				ID:         id,
2883				Time:       createdAtTime,
2884				Duration:   costsDuration,
2885				Args:       cmdString,
2886				ClientAddr: address,
2887				ClientName: name,
2888			}
2889		}
2890		return nil, nil
2891	})
2892	return err
2893}
2894