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 XAutoClaimCmd struct {
1505	baseCmd
1506
1507	start string
1508	val   []XMessage
1509}
1510
1511var _ Cmder = (*XAutoClaimCmd)(nil)
1512
1513func NewXAutoClaimCmd(ctx context.Context, args ...interface{}) *XAutoClaimCmd {
1514	return &XAutoClaimCmd{
1515		baseCmd: baseCmd{
1516			ctx:  ctx,
1517			args: args,
1518		},
1519	}
1520}
1521
1522func (cmd *XAutoClaimCmd) Val() (messages []XMessage, start string) {
1523	return cmd.val, cmd.start
1524}
1525
1526func (cmd *XAutoClaimCmd) Result() (messages []XMessage, start string, err error) {
1527	return cmd.val, cmd.start, cmd.err
1528}
1529
1530func (cmd *XAutoClaimCmd) String() string {
1531	return cmdString(cmd, cmd.val)
1532}
1533
1534func (cmd *XAutoClaimCmd) readReply(rd *proto.Reader) error {
1535	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1536		if n != 2 {
1537			return nil, fmt.Errorf("got %d, wanted 2", n)
1538		}
1539		var err error
1540
1541		cmd.start, err = rd.ReadString()
1542		if err != nil {
1543			return nil, err
1544		}
1545
1546		cmd.val, err = readXMessageSlice(rd)
1547		if err != nil {
1548			return nil, err
1549		}
1550
1551		return nil, nil
1552	})
1553	return err
1554}
1555
1556//------------------------------------------------------------------------------
1557
1558type XAutoClaimJustIDCmd struct {
1559	baseCmd
1560
1561	start string
1562	val   []string
1563}
1564
1565var _ Cmder = (*XAutoClaimJustIDCmd)(nil)
1566
1567func NewXAutoClaimJustIDCmd(ctx context.Context, args ...interface{}) *XAutoClaimJustIDCmd {
1568	return &XAutoClaimJustIDCmd{
1569		baseCmd: baseCmd{
1570			ctx:  ctx,
1571			args: args,
1572		},
1573	}
1574}
1575
1576func (cmd *XAutoClaimJustIDCmd) Val() (ids []string, start string) {
1577	return cmd.val, cmd.start
1578}
1579
1580func (cmd *XAutoClaimJustIDCmd) Result() (ids []string, start string, err error) {
1581	return cmd.val, cmd.start, cmd.err
1582}
1583
1584func (cmd *XAutoClaimJustIDCmd) String() string {
1585	return cmdString(cmd, cmd.val)
1586}
1587
1588func (cmd *XAutoClaimJustIDCmd) readReply(rd *proto.Reader) error {
1589	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
1590		if n != 2 {
1591			return nil, fmt.Errorf("got %d, wanted 2", n)
1592		}
1593		var err error
1594
1595		cmd.start, err = rd.ReadString()
1596		if err != nil {
1597			return nil, err
1598		}
1599
1600		nn, err := rd.ReadArrayLen()
1601		if err != nil {
1602			return nil, err
1603		}
1604
1605		cmd.val = make([]string, nn)
1606		for i := 0; i < nn; i++ {
1607			cmd.val[i], err = rd.ReadString()
1608			if err != nil {
1609				return nil, err
1610			}
1611		}
1612
1613		return nil, nil
1614	})
1615	return err
1616}
1617
1618//------------------------------------------------------------------------------
1619
1620type XInfoConsumersCmd struct {
1621	baseCmd
1622	val []XInfoConsumer
1623}
1624
1625type XInfoConsumer struct {
1626	Name    string
1627	Pending int64
1628	Idle    int64
1629}
1630
1631var _ Cmder = (*XInfoConsumersCmd)(nil)
1632
1633func NewXInfoConsumersCmd(ctx context.Context, stream string, group string) *XInfoConsumersCmd {
1634	return &XInfoConsumersCmd{
1635		baseCmd: baseCmd{
1636			ctx:  ctx,
1637			args: []interface{}{"xinfo", "consumers", stream, group},
1638		},
1639	}
1640}
1641
1642func (cmd *XInfoConsumersCmd) Val() []XInfoConsumer {
1643	return cmd.val
1644}
1645
1646func (cmd *XInfoConsumersCmd) Result() ([]XInfoConsumer, error) {
1647	return cmd.val, cmd.err
1648}
1649
1650func (cmd *XInfoConsumersCmd) String() string {
1651	return cmdString(cmd, cmd.val)
1652}
1653
1654func (cmd *XInfoConsumersCmd) readReply(rd *proto.Reader) error {
1655	n, err := rd.ReadArrayLen()
1656	if err != nil {
1657		return err
1658	}
1659
1660	cmd.val = make([]XInfoConsumer, n)
1661
1662	for i := 0; i < n; i++ {
1663		cmd.val[i], err = readXConsumerInfo(rd)
1664		if err != nil {
1665			return err
1666		}
1667	}
1668
1669	return nil
1670}
1671
1672func readXConsumerInfo(rd *proto.Reader) (XInfoConsumer, error) {
1673	var consumer XInfoConsumer
1674
1675	n, err := rd.ReadArrayLen()
1676	if err != nil {
1677		return consumer, err
1678	}
1679	if n != 6 {
1680		return consumer, fmt.Errorf("redis: got %d elements in XINFO CONSUMERS reply, wanted 6", n)
1681	}
1682
1683	for i := 0; i < 3; i++ {
1684		key, err := rd.ReadString()
1685		if err != nil {
1686			return consumer, err
1687		}
1688
1689		val, err := rd.ReadString()
1690		if err != nil {
1691			return consumer, err
1692		}
1693
1694		switch key {
1695		case "name":
1696			consumer.Name = val
1697		case "pending":
1698			consumer.Pending, err = strconv.ParseInt(val, 0, 64)
1699			if err != nil {
1700				return consumer, err
1701			}
1702		case "idle":
1703			consumer.Idle, err = strconv.ParseInt(val, 0, 64)
1704			if err != nil {
1705				return consumer, err
1706			}
1707		default:
1708			return consumer, fmt.Errorf("redis: unexpected content %s in XINFO CONSUMERS reply", key)
1709		}
1710	}
1711
1712	return consumer, nil
1713}
1714
1715//------------------------------------------------------------------------------
1716
1717type XInfoGroupsCmd struct {
1718	baseCmd
1719	val []XInfoGroup
1720}
1721
1722type XInfoGroup struct {
1723	Name            string
1724	Consumers       int64
1725	Pending         int64
1726	LastDeliveredID string
1727}
1728
1729var _ Cmder = (*XInfoGroupsCmd)(nil)
1730
1731func NewXInfoGroupsCmd(ctx context.Context, stream string) *XInfoGroupsCmd {
1732	return &XInfoGroupsCmd{
1733		baseCmd: baseCmd{
1734			ctx:  ctx,
1735			args: []interface{}{"xinfo", "groups", stream},
1736		},
1737	}
1738}
1739
1740func (cmd *XInfoGroupsCmd) Val() []XInfoGroup {
1741	return cmd.val
1742}
1743
1744func (cmd *XInfoGroupsCmd) Result() ([]XInfoGroup, error) {
1745	return cmd.val, cmd.err
1746}
1747
1748func (cmd *XInfoGroupsCmd) String() string {
1749	return cmdString(cmd, cmd.val)
1750}
1751
1752func (cmd *XInfoGroupsCmd) readReply(rd *proto.Reader) error {
1753	n, err := rd.ReadArrayLen()
1754	if err != nil {
1755		return err
1756	}
1757
1758	cmd.val = make([]XInfoGroup, n)
1759
1760	for i := 0; i < n; i++ {
1761		cmd.val[i], err = readXGroupInfo(rd)
1762		if err != nil {
1763			return err
1764		}
1765	}
1766
1767	return nil
1768}
1769
1770func readXGroupInfo(rd *proto.Reader) (XInfoGroup, error) {
1771	var group XInfoGroup
1772
1773	n, err := rd.ReadArrayLen()
1774	if err != nil {
1775		return group, err
1776	}
1777	if n != 8 {
1778		return group, fmt.Errorf("redis: got %d elements in XINFO GROUPS reply, wanted 8", n)
1779	}
1780
1781	for i := 0; i < 4; i++ {
1782		key, err := rd.ReadString()
1783		if err != nil {
1784			return group, err
1785		}
1786
1787		val, err := rd.ReadString()
1788		if err != nil {
1789			return group, err
1790		}
1791
1792		switch key {
1793		case "name":
1794			group.Name = val
1795		case "consumers":
1796			group.Consumers, err = strconv.ParseInt(val, 0, 64)
1797			if err != nil {
1798				return group, err
1799			}
1800		case "pending":
1801			group.Pending, err = strconv.ParseInt(val, 0, 64)
1802			if err != nil {
1803				return group, err
1804			}
1805		case "last-delivered-id":
1806			group.LastDeliveredID = val
1807		default:
1808			return group, fmt.Errorf("redis: unexpected content %s in XINFO GROUPS reply", key)
1809		}
1810	}
1811
1812	return group, nil
1813}
1814
1815//------------------------------------------------------------------------------
1816
1817type XInfoStreamCmd struct {
1818	baseCmd
1819	val *XInfoStream
1820}
1821
1822type XInfoStream struct {
1823	Length          int64
1824	RadixTreeKeys   int64
1825	RadixTreeNodes  int64
1826	Groups          int64
1827	LastGeneratedID string
1828	FirstEntry      XMessage
1829	LastEntry       XMessage
1830}
1831
1832var _ Cmder = (*XInfoStreamCmd)(nil)
1833
1834func NewXInfoStreamCmd(ctx context.Context, stream string) *XInfoStreamCmd {
1835	return &XInfoStreamCmd{
1836		baseCmd: baseCmd{
1837			ctx:  ctx,
1838			args: []interface{}{"xinfo", "stream", stream},
1839		},
1840	}
1841}
1842
1843func (cmd *XInfoStreamCmd) Val() *XInfoStream {
1844	return cmd.val
1845}
1846
1847func (cmd *XInfoStreamCmd) Result() (*XInfoStream, error) {
1848	return cmd.val, cmd.err
1849}
1850
1851func (cmd *XInfoStreamCmd) String() string {
1852	return cmdString(cmd, cmd.val)
1853}
1854
1855func (cmd *XInfoStreamCmd) readReply(rd *proto.Reader) error {
1856	v, err := rd.ReadReply(xStreamInfoParser)
1857	if err != nil {
1858		return err
1859	}
1860	cmd.val = v.(*XInfoStream)
1861	return nil
1862}
1863
1864func xStreamInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
1865	if n != 14 {
1866		return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM reply,"+
1867			"wanted 14", n)
1868	}
1869	var info XInfoStream
1870	for i := 0; i < 7; i++ {
1871		key, err := rd.ReadString()
1872		if err != nil {
1873			return nil, err
1874		}
1875		switch key {
1876		case "length":
1877			info.Length, err = rd.ReadIntReply()
1878		case "radix-tree-keys":
1879			info.RadixTreeKeys, err = rd.ReadIntReply()
1880		case "radix-tree-nodes":
1881			info.RadixTreeNodes, err = rd.ReadIntReply()
1882		case "groups":
1883			info.Groups, err = rd.ReadIntReply()
1884		case "last-generated-id":
1885			info.LastGeneratedID, err = rd.ReadString()
1886		case "first-entry":
1887			info.FirstEntry, err = readXMessage(rd)
1888			if err == Nil {
1889				err = nil
1890			}
1891		case "last-entry":
1892			info.LastEntry, err = readXMessage(rd)
1893			if err == Nil {
1894				err = nil
1895			}
1896		default:
1897			return nil, fmt.Errorf("redis: unexpected content %s "+
1898				"in XINFO STREAM reply", key)
1899		}
1900		if err != nil {
1901			return nil, err
1902		}
1903	}
1904	return &info, nil
1905}
1906
1907//------------------------------------------------------------------------------
1908
1909type XInfoStreamFullCmd struct {
1910	baseCmd
1911	val *XInfoStreamFull
1912}
1913
1914type XInfoStreamFull struct {
1915	Length          int64
1916	RadixTreeKeys   int64
1917	RadixTreeNodes  int64
1918	LastGeneratedID string
1919	Entries         []XMessage
1920	Groups          []XInfoStreamGroup
1921}
1922
1923type XInfoStreamGroup struct {
1924	Name            string
1925	LastDeliveredID string
1926	PelCount        int64
1927	Pending         []XInfoStreamGroupPending
1928	Consumers       []XInfoStreamConsumer
1929}
1930
1931type XInfoStreamGroupPending struct {
1932	ID            string
1933	Consumer      string
1934	DeliveryTime  time.Time
1935	DeliveryCount int64
1936}
1937
1938type XInfoStreamConsumer struct {
1939	Name     string
1940	SeenTime time.Time
1941	PelCount int64
1942	Pending  []XInfoStreamConsumerPending
1943}
1944
1945type XInfoStreamConsumerPending struct {
1946	ID            string
1947	DeliveryTime  time.Time
1948	DeliveryCount int64
1949}
1950
1951var _ Cmder = (*XInfoStreamFullCmd)(nil)
1952
1953func NewXInfoStreamFullCmd(ctx context.Context, args ...interface{}) *XInfoStreamFullCmd {
1954	return &XInfoStreamFullCmd{
1955		baseCmd: baseCmd{
1956			ctx:  ctx,
1957			args: args,
1958		},
1959	}
1960}
1961
1962func (cmd *XInfoStreamFullCmd) Val() *XInfoStreamFull {
1963	return cmd.val
1964}
1965
1966func (cmd *XInfoStreamFullCmd) Result() (*XInfoStreamFull, error) {
1967	return cmd.val, cmd.err
1968}
1969
1970func (cmd *XInfoStreamFullCmd) String() string {
1971	return cmdString(cmd, cmd.val)
1972}
1973
1974func (cmd *XInfoStreamFullCmd) readReply(rd *proto.Reader) error {
1975	n, err := rd.ReadArrayLen()
1976	if err != nil {
1977		return err
1978	}
1979	if n != 12 {
1980		return fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+
1981			"wanted 12", n)
1982	}
1983
1984	cmd.val = &XInfoStreamFull{}
1985
1986	for i := 0; i < 6; i++ {
1987		key, err := rd.ReadString()
1988		if err != nil {
1989			return err
1990		}
1991
1992		switch key {
1993		case "length":
1994			cmd.val.Length, err = rd.ReadIntReply()
1995		case "radix-tree-keys":
1996			cmd.val.RadixTreeKeys, err = rd.ReadIntReply()
1997		case "radix-tree-nodes":
1998			cmd.val.RadixTreeNodes, err = rd.ReadIntReply()
1999		case "last-generated-id":
2000			cmd.val.LastGeneratedID, err = rd.ReadString()
2001		case "entries":
2002			cmd.val.Entries, err = readXMessageSlice(rd)
2003		case "groups":
2004			cmd.val.Groups, err = readStreamGroups(rd)
2005		default:
2006			return fmt.Errorf("redis: unexpected content %s "+
2007				"in XINFO STREAM reply", key)
2008		}
2009		if err != nil {
2010			return err
2011		}
2012	}
2013	return nil
2014}
2015
2016func readStreamGroups(rd *proto.Reader) ([]XInfoStreamGroup, error) {
2017	n, err := rd.ReadArrayLen()
2018	if err != nil {
2019		return nil, err
2020	}
2021	groups := make([]XInfoStreamGroup, 0, n)
2022	for i := 0; i < n; i++ {
2023		nn, err := rd.ReadArrayLen()
2024		if err != nil {
2025			return nil, err
2026		}
2027		if nn != 10 {
2028			return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+
2029				"wanted 10", nn)
2030		}
2031
2032		group := XInfoStreamGroup{}
2033
2034		for f := 0; f < 5; f++ {
2035			key, err := rd.ReadString()
2036			if err != nil {
2037				return nil, err
2038			}
2039
2040			switch key {
2041			case "name":
2042				group.Name, err = rd.ReadString()
2043			case "last-delivered-id":
2044				group.LastDeliveredID, err = rd.ReadString()
2045			case "pel-count":
2046				group.PelCount, err = rd.ReadIntReply()
2047			case "pending":
2048				group.Pending, err = readXInfoStreamGroupPending(rd)
2049			case "consumers":
2050				group.Consumers, err = readXInfoStreamConsumers(rd)
2051			default:
2052				return nil, fmt.Errorf("redis: unexpected content %s "+
2053					"in XINFO STREAM reply", key)
2054			}
2055
2056			if err != nil {
2057				return nil, err
2058			}
2059		}
2060
2061		groups = append(groups, group)
2062	}
2063
2064	return groups, nil
2065}
2066
2067func readXInfoStreamGroupPending(rd *proto.Reader) ([]XInfoStreamGroupPending, error) {
2068	n, err := rd.ReadArrayLen()
2069	if err != nil {
2070		return nil, err
2071	}
2072
2073	pending := make([]XInfoStreamGroupPending, 0, n)
2074
2075	for i := 0; i < n; i++ {
2076		nn, err := rd.ReadArrayLen()
2077		if err != nil {
2078			return nil, err
2079		}
2080		if nn != 4 {
2081			return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+
2082				"wanted 4", nn)
2083		}
2084
2085		p := XInfoStreamGroupPending{}
2086
2087		p.ID, err = rd.ReadString()
2088		if err != nil {
2089			return nil, err
2090		}
2091
2092		p.Consumer, err = rd.ReadString()
2093		if err != nil {
2094			return nil, err
2095		}
2096
2097		delivery, err := rd.ReadIntReply()
2098		if err != nil {
2099			return nil, err
2100		}
2101		p.DeliveryTime = time.Unix(delivery/1000, delivery%1000*int64(time.Millisecond))
2102
2103		p.DeliveryCount, err = rd.ReadIntReply()
2104		if err != nil {
2105			return nil, err
2106		}
2107
2108		pending = append(pending, p)
2109	}
2110
2111	return pending, nil
2112}
2113
2114func readXInfoStreamConsumers(rd *proto.Reader) ([]XInfoStreamConsumer, error) {
2115	n, err := rd.ReadArrayLen()
2116	if err != nil {
2117		return nil, err
2118	}
2119
2120	consumers := make([]XInfoStreamConsumer, 0, n)
2121
2122	for i := 0; i < n; i++ {
2123		nn, err := rd.ReadArrayLen()
2124		if err != nil {
2125			return nil, err
2126		}
2127		if nn != 8 {
2128			return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM FULL reply,"+
2129				"wanted 8", nn)
2130		}
2131
2132		c := XInfoStreamConsumer{}
2133
2134		for f := 0; f < 4; f++ {
2135			cKey, err := rd.ReadString()
2136			if err != nil {
2137				return nil, err
2138			}
2139
2140			switch cKey {
2141			case "name":
2142				c.Name, err = rd.ReadString()
2143			case "seen-time":
2144				seen, err := rd.ReadIntReply()
2145				if err != nil {
2146					return nil, err
2147				}
2148				c.SeenTime = time.Unix(seen/1000, seen%1000*int64(time.Millisecond))
2149			case "pel-count":
2150				c.PelCount, err = rd.ReadIntReply()
2151			case "pending":
2152				pendingNumber, err := rd.ReadArrayLen()
2153				if err != nil {
2154					return nil, err
2155				}
2156
2157				c.Pending = make([]XInfoStreamConsumerPending, 0, pendingNumber)
2158
2159				for pn := 0; pn < pendingNumber; pn++ {
2160					nn, err := rd.ReadArrayLen()
2161					if err != nil {
2162						return nil, err
2163					}
2164					if nn != 3 {
2165						return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM reply,"+
2166							"wanted 3", nn)
2167					}
2168
2169					p := XInfoStreamConsumerPending{}
2170
2171					p.ID, err = rd.ReadString()
2172					if err != nil {
2173						return nil, err
2174					}
2175
2176					delivery, err := rd.ReadIntReply()
2177					if err != nil {
2178						return nil, err
2179					}
2180					p.DeliveryTime = time.Unix(delivery/1000, delivery%1000*int64(time.Millisecond))
2181
2182					p.DeliveryCount, err = rd.ReadIntReply()
2183					if err != nil {
2184						return nil, err
2185					}
2186
2187					c.Pending = append(c.Pending, p)
2188				}
2189			default:
2190				return nil, fmt.Errorf("redis: unexpected content %s "+
2191					"in XINFO STREAM reply", cKey)
2192			}
2193			if err != nil {
2194				return nil, err
2195			}
2196		}
2197		consumers = append(consumers, c)
2198	}
2199
2200	return consumers, nil
2201}
2202
2203//------------------------------------------------------------------------------
2204
2205type ZSliceCmd struct {
2206	baseCmd
2207
2208	val []Z
2209}
2210
2211var _ Cmder = (*ZSliceCmd)(nil)
2212
2213func NewZSliceCmd(ctx context.Context, args ...interface{}) *ZSliceCmd {
2214	return &ZSliceCmd{
2215		baseCmd: baseCmd{
2216			ctx:  ctx,
2217			args: args,
2218		},
2219	}
2220}
2221
2222func (cmd *ZSliceCmd) Val() []Z {
2223	return cmd.val
2224}
2225
2226func (cmd *ZSliceCmd) Result() ([]Z, error) {
2227	return cmd.val, cmd.err
2228}
2229
2230func (cmd *ZSliceCmd) String() string {
2231	return cmdString(cmd, cmd.val)
2232}
2233
2234func (cmd *ZSliceCmd) readReply(rd *proto.Reader) error {
2235	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2236		cmd.val = make([]Z, n/2)
2237		for i := 0; i < len(cmd.val); i++ {
2238			member, err := rd.ReadString()
2239			if err != nil {
2240				return nil, err
2241			}
2242
2243			score, err := rd.ReadFloatReply()
2244			if err != nil {
2245				return nil, err
2246			}
2247
2248			cmd.val[i] = Z{
2249				Member: member,
2250				Score:  score,
2251			}
2252		}
2253		return nil, nil
2254	})
2255	return err
2256}
2257
2258//------------------------------------------------------------------------------
2259
2260type ZWithKeyCmd struct {
2261	baseCmd
2262
2263	val *ZWithKey
2264}
2265
2266var _ Cmder = (*ZWithKeyCmd)(nil)
2267
2268func NewZWithKeyCmd(ctx context.Context, args ...interface{}) *ZWithKeyCmd {
2269	return &ZWithKeyCmd{
2270		baseCmd: baseCmd{
2271			ctx:  ctx,
2272			args: args,
2273		},
2274	}
2275}
2276
2277func (cmd *ZWithKeyCmd) Val() *ZWithKey {
2278	return cmd.val
2279}
2280
2281func (cmd *ZWithKeyCmd) Result() (*ZWithKey, error) {
2282	return cmd.Val(), cmd.Err()
2283}
2284
2285func (cmd *ZWithKeyCmd) String() string {
2286	return cmdString(cmd, cmd.val)
2287}
2288
2289func (cmd *ZWithKeyCmd) readReply(rd *proto.Reader) error {
2290	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2291		if n != 3 {
2292			return nil, fmt.Errorf("got %d elements, expected 3", n)
2293		}
2294
2295		cmd.val = &ZWithKey{}
2296		var err error
2297
2298		cmd.val.Key, err = rd.ReadString()
2299		if err != nil {
2300			return nil, err
2301		}
2302
2303		cmd.val.Member, err = rd.ReadString()
2304		if err != nil {
2305			return nil, err
2306		}
2307
2308		cmd.val.Score, err = rd.ReadFloatReply()
2309		if err != nil {
2310			return nil, err
2311		}
2312
2313		return nil, nil
2314	})
2315	return err
2316}
2317
2318//------------------------------------------------------------------------------
2319
2320type ScanCmd struct {
2321	baseCmd
2322
2323	page   []string
2324	cursor uint64
2325
2326	process cmdable
2327}
2328
2329var _ Cmder = (*ScanCmd)(nil)
2330
2331func NewScanCmd(ctx context.Context, process cmdable, args ...interface{}) *ScanCmd {
2332	return &ScanCmd{
2333		baseCmd: baseCmd{
2334			ctx:  ctx,
2335			args: args,
2336		},
2337		process: process,
2338	}
2339}
2340
2341func (cmd *ScanCmd) Val() (keys []string, cursor uint64) {
2342	return cmd.page, cmd.cursor
2343}
2344
2345func (cmd *ScanCmd) Result() (keys []string, cursor uint64, err error) {
2346	return cmd.page, cmd.cursor, cmd.err
2347}
2348
2349func (cmd *ScanCmd) String() string {
2350	return cmdString(cmd, cmd.page)
2351}
2352
2353func (cmd *ScanCmd) readReply(rd *proto.Reader) (err error) {
2354	cmd.page, cmd.cursor, err = rd.ReadScanReply()
2355	return err
2356}
2357
2358// Iterator creates a new ScanIterator.
2359func (cmd *ScanCmd) Iterator() *ScanIterator {
2360	return &ScanIterator{
2361		cmd: cmd,
2362	}
2363}
2364
2365//------------------------------------------------------------------------------
2366
2367type ClusterNode struct {
2368	ID   string
2369	Addr string
2370}
2371
2372type ClusterSlot struct {
2373	Start int
2374	End   int
2375	Nodes []ClusterNode
2376}
2377
2378type ClusterSlotsCmd struct {
2379	baseCmd
2380
2381	val []ClusterSlot
2382}
2383
2384var _ Cmder = (*ClusterSlotsCmd)(nil)
2385
2386func NewClusterSlotsCmd(ctx context.Context, args ...interface{}) *ClusterSlotsCmd {
2387	return &ClusterSlotsCmd{
2388		baseCmd: baseCmd{
2389			ctx:  ctx,
2390			args: args,
2391		},
2392	}
2393}
2394
2395func (cmd *ClusterSlotsCmd) Val() []ClusterSlot {
2396	return cmd.val
2397}
2398
2399func (cmd *ClusterSlotsCmd) Result() ([]ClusterSlot, error) {
2400	return cmd.Val(), cmd.Err()
2401}
2402
2403func (cmd *ClusterSlotsCmd) String() string {
2404	return cmdString(cmd, cmd.val)
2405}
2406
2407func (cmd *ClusterSlotsCmd) readReply(rd *proto.Reader) error {
2408	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2409		cmd.val = make([]ClusterSlot, n)
2410		for i := 0; i < len(cmd.val); i++ {
2411			n, err := rd.ReadArrayLen()
2412			if err != nil {
2413				return nil, err
2414			}
2415			if n < 2 {
2416				err := fmt.Errorf("redis: got %d elements in cluster info, expected at least 2", n)
2417				return nil, err
2418			}
2419
2420			start, err := rd.ReadIntReply()
2421			if err != nil {
2422				return nil, err
2423			}
2424
2425			end, err := rd.ReadIntReply()
2426			if err != nil {
2427				return nil, err
2428			}
2429
2430			nodes := make([]ClusterNode, n-2)
2431			for j := 0; j < len(nodes); j++ {
2432				n, err := rd.ReadArrayLen()
2433				if err != nil {
2434					return nil, err
2435				}
2436				if n != 2 && n != 3 {
2437					err := fmt.Errorf("got %d elements in cluster info address, expected 2 or 3", n)
2438					return nil, err
2439				}
2440
2441				ip, err := rd.ReadString()
2442				if err != nil {
2443					return nil, err
2444				}
2445
2446				port, err := rd.ReadString()
2447				if err != nil {
2448					return nil, err
2449				}
2450
2451				nodes[j].Addr = net.JoinHostPort(ip, port)
2452
2453				if n == 3 {
2454					id, err := rd.ReadString()
2455					if err != nil {
2456						return nil, err
2457					}
2458					nodes[j].ID = id
2459				}
2460			}
2461
2462			cmd.val[i] = ClusterSlot{
2463				Start: int(start),
2464				End:   int(end),
2465				Nodes: nodes,
2466			}
2467		}
2468		return nil, nil
2469	})
2470	return err
2471}
2472
2473//------------------------------------------------------------------------------
2474
2475// GeoLocation is used with GeoAdd to add geospatial location.
2476type GeoLocation struct {
2477	Name                      string
2478	Longitude, Latitude, Dist float64
2479	GeoHash                   int64
2480}
2481
2482// GeoRadiusQuery is used with GeoRadius to query geospatial index.
2483type GeoRadiusQuery struct {
2484	Radius float64
2485	// Can be m, km, ft, or mi. Default is km.
2486	Unit        string
2487	WithCoord   bool
2488	WithDist    bool
2489	WithGeoHash bool
2490	Count       int
2491	// Can be ASC or DESC. Default is no sort order.
2492	Sort      string
2493	Store     string
2494	StoreDist string
2495}
2496
2497type GeoLocationCmd struct {
2498	baseCmd
2499
2500	q         *GeoRadiusQuery
2501	locations []GeoLocation
2502}
2503
2504var _ Cmder = (*GeoLocationCmd)(nil)
2505
2506func NewGeoLocationCmd(ctx context.Context, q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd {
2507	return &GeoLocationCmd{
2508		baseCmd: baseCmd{
2509			ctx:  ctx,
2510			args: geoLocationArgs(q, args...),
2511		},
2512		q: q,
2513	}
2514}
2515
2516func geoLocationArgs(q *GeoRadiusQuery, args ...interface{}) []interface{} {
2517	args = append(args, q.Radius)
2518	if q.Unit != "" {
2519		args = append(args, q.Unit)
2520	} else {
2521		args = append(args, "km")
2522	}
2523	if q.WithCoord {
2524		args = append(args, "withcoord")
2525	}
2526	if q.WithDist {
2527		args = append(args, "withdist")
2528	}
2529	if q.WithGeoHash {
2530		args = append(args, "withhash")
2531	}
2532	if q.Count > 0 {
2533		args = append(args, "count", q.Count)
2534	}
2535	if q.Sort != "" {
2536		args = append(args, q.Sort)
2537	}
2538	if q.Store != "" {
2539		args = append(args, "store")
2540		args = append(args, q.Store)
2541	}
2542	if q.StoreDist != "" {
2543		args = append(args, "storedist")
2544		args = append(args, q.StoreDist)
2545	}
2546	return args
2547}
2548
2549func (cmd *GeoLocationCmd) Val() []GeoLocation {
2550	return cmd.locations
2551}
2552
2553func (cmd *GeoLocationCmd) Result() ([]GeoLocation, error) {
2554	return cmd.locations, cmd.err
2555}
2556
2557func (cmd *GeoLocationCmd) String() string {
2558	return cmdString(cmd, cmd.locations)
2559}
2560
2561func (cmd *GeoLocationCmd) readReply(rd *proto.Reader) error {
2562	v, err := rd.ReadArrayReply(newGeoLocationSliceParser(cmd.q))
2563	if err != nil {
2564		return err
2565	}
2566	cmd.locations = v.([]GeoLocation)
2567	return nil
2568}
2569
2570func newGeoLocationSliceParser(q *GeoRadiusQuery) proto.MultiBulkParse {
2571	return func(rd *proto.Reader, n int64) (interface{}, error) {
2572		locs := make([]GeoLocation, 0, n)
2573		for i := int64(0); i < n; i++ {
2574			v, err := rd.ReadReply(newGeoLocationParser(q))
2575			if err != nil {
2576				return nil, err
2577			}
2578			switch vv := v.(type) {
2579			case string:
2580				locs = append(locs, GeoLocation{
2581					Name: vv,
2582				})
2583			case *GeoLocation:
2584				// TODO: avoid copying
2585				locs = append(locs, *vv)
2586			default:
2587				return nil, fmt.Errorf("got %T, expected string or *GeoLocation", v)
2588			}
2589		}
2590		return locs, nil
2591	}
2592}
2593
2594func newGeoLocationParser(q *GeoRadiusQuery) proto.MultiBulkParse {
2595	return func(rd *proto.Reader, n int64) (interface{}, error) {
2596		var loc GeoLocation
2597		var err error
2598
2599		loc.Name, err = rd.ReadString()
2600		if err != nil {
2601			return nil, err
2602		}
2603		if q.WithDist {
2604			loc.Dist, err = rd.ReadFloatReply()
2605			if err != nil {
2606				return nil, err
2607			}
2608		}
2609		if q.WithGeoHash {
2610			loc.GeoHash, err = rd.ReadIntReply()
2611			if err != nil {
2612				return nil, err
2613			}
2614		}
2615		if q.WithCoord {
2616			n, err := rd.ReadArrayLen()
2617			if err != nil {
2618				return nil, err
2619			}
2620			if n != 2 {
2621				return nil, fmt.Errorf("got %d coordinates, expected 2", n)
2622			}
2623
2624			loc.Longitude, err = rd.ReadFloatReply()
2625			if err != nil {
2626				return nil, err
2627			}
2628			loc.Latitude, err = rd.ReadFloatReply()
2629			if err != nil {
2630				return nil, err
2631			}
2632		}
2633
2634		return &loc, nil
2635	}
2636}
2637
2638//------------------------------------------------------------------------------
2639
2640type GeoPos struct {
2641	Longitude, Latitude float64
2642}
2643
2644type GeoPosCmd struct {
2645	baseCmd
2646
2647	val []*GeoPos
2648}
2649
2650var _ Cmder = (*GeoPosCmd)(nil)
2651
2652func NewGeoPosCmd(ctx context.Context, args ...interface{}) *GeoPosCmd {
2653	return &GeoPosCmd{
2654		baseCmd: baseCmd{
2655			ctx:  ctx,
2656			args: args,
2657		},
2658	}
2659}
2660
2661func (cmd *GeoPosCmd) Val() []*GeoPos {
2662	return cmd.val
2663}
2664
2665func (cmd *GeoPosCmd) Result() ([]*GeoPos, error) {
2666	return cmd.Val(), cmd.Err()
2667}
2668
2669func (cmd *GeoPosCmd) String() string {
2670	return cmdString(cmd, cmd.val)
2671}
2672
2673func (cmd *GeoPosCmd) readReply(rd *proto.Reader) error {
2674	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2675		cmd.val = make([]*GeoPos, n)
2676		for i := 0; i < len(cmd.val); i++ {
2677			i := i
2678			_, err := rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2679				longitude, err := rd.ReadFloatReply()
2680				if err != nil {
2681					return nil, err
2682				}
2683
2684				latitude, err := rd.ReadFloatReply()
2685				if err != nil {
2686					return nil, err
2687				}
2688
2689				cmd.val[i] = &GeoPos{
2690					Longitude: longitude,
2691					Latitude:  latitude,
2692				}
2693				return nil, nil
2694			})
2695			if err != nil {
2696				if err == Nil {
2697					cmd.val[i] = nil
2698					continue
2699				}
2700				return nil, err
2701			}
2702		}
2703		return nil, nil
2704	})
2705	return err
2706}
2707
2708//------------------------------------------------------------------------------
2709
2710type CommandInfo struct {
2711	Name        string
2712	Arity       int8
2713	Flags       []string
2714	ACLFlags    []string
2715	FirstKeyPos int8
2716	LastKeyPos  int8
2717	StepCount   int8
2718	ReadOnly    bool
2719}
2720
2721type CommandsInfoCmd struct {
2722	baseCmd
2723
2724	val map[string]*CommandInfo
2725}
2726
2727var _ Cmder = (*CommandsInfoCmd)(nil)
2728
2729func NewCommandsInfoCmd(ctx context.Context, args ...interface{}) *CommandsInfoCmd {
2730	return &CommandsInfoCmd{
2731		baseCmd: baseCmd{
2732			ctx:  ctx,
2733			args: args,
2734		},
2735	}
2736}
2737
2738func (cmd *CommandsInfoCmd) Val() map[string]*CommandInfo {
2739	return cmd.val
2740}
2741
2742func (cmd *CommandsInfoCmd) Result() (map[string]*CommandInfo, error) {
2743	return cmd.Val(), cmd.Err()
2744}
2745
2746func (cmd *CommandsInfoCmd) String() string {
2747	return cmdString(cmd, cmd.val)
2748}
2749
2750func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error {
2751	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2752		cmd.val = make(map[string]*CommandInfo, n)
2753		for i := int64(0); i < n; i++ {
2754			v, err := rd.ReadReply(commandInfoParser)
2755			if err != nil {
2756				return nil, err
2757			}
2758			vv := v.(*CommandInfo)
2759			cmd.val[vv.Name] = vv
2760		}
2761		return nil, nil
2762	})
2763	return err
2764}
2765
2766func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
2767	const numArgRedis5 = 6
2768	const numArgRedis6 = 7
2769
2770	switch n {
2771	case numArgRedis5, numArgRedis6:
2772		// continue
2773	default:
2774		return nil, fmt.Errorf("redis: got %d elements in COMMAND reply, wanted 7", n)
2775	}
2776
2777	var cmd CommandInfo
2778	var err error
2779
2780	cmd.Name, err = rd.ReadString()
2781	if err != nil {
2782		return nil, err
2783	}
2784
2785	arity, err := rd.ReadIntReply()
2786	if err != nil {
2787		return nil, err
2788	}
2789	cmd.Arity = int8(arity)
2790
2791	_, err = rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2792		cmd.Flags = make([]string, n)
2793		for i := 0; i < len(cmd.Flags); i++ {
2794			switch s, err := rd.ReadString(); {
2795			case err == Nil:
2796				cmd.Flags[i] = ""
2797			case err != nil:
2798				return nil, err
2799			default:
2800				cmd.Flags[i] = s
2801			}
2802		}
2803		return nil, nil
2804	})
2805	if err != nil {
2806		return nil, err
2807	}
2808
2809	firstKeyPos, err := rd.ReadIntReply()
2810	if err != nil {
2811		return nil, err
2812	}
2813	cmd.FirstKeyPos = int8(firstKeyPos)
2814
2815	lastKeyPos, err := rd.ReadIntReply()
2816	if err != nil {
2817		return nil, err
2818	}
2819	cmd.LastKeyPos = int8(lastKeyPos)
2820
2821	stepCount, err := rd.ReadIntReply()
2822	if err != nil {
2823		return nil, err
2824	}
2825	cmd.StepCount = int8(stepCount)
2826
2827	for _, flag := range cmd.Flags {
2828		if flag == "readonly" {
2829			cmd.ReadOnly = true
2830			break
2831		}
2832	}
2833
2834	if n == numArgRedis5 {
2835		return &cmd, nil
2836	}
2837
2838	_, err = rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2839		cmd.ACLFlags = make([]string, n)
2840		for i := 0; i < len(cmd.ACLFlags); i++ {
2841			switch s, err := rd.ReadString(); {
2842			case err == Nil:
2843				cmd.ACLFlags[i] = ""
2844			case err != nil:
2845				return nil, err
2846			default:
2847				cmd.ACLFlags[i] = s
2848			}
2849		}
2850		return nil, nil
2851	})
2852	if err != nil {
2853		return nil, err
2854	}
2855
2856	return &cmd, nil
2857}
2858
2859//------------------------------------------------------------------------------
2860
2861type cmdsInfoCache struct {
2862	fn func(ctx context.Context) (map[string]*CommandInfo, error)
2863
2864	once internal.Once
2865	cmds map[string]*CommandInfo
2866}
2867
2868func newCmdsInfoCache(fn func(ctx context.Context) (map[string]*CommandInfo, error)) *cmdsInfoCache {
2869	return &cmdsInfoCache{
2870		fn: fn,
2871	}
2872}
2873
2874func (c *cmdsInfoCache) Get(ctx context.Context) (map[string]*CommandInfo, error) {
2875	err := c.once.Do(func() error {
2876		cmds, err := c.fn(ctx)
2877		if err != nil {
2878			return err
2879		}
2880
2881		// Extensions have cmd names in upper case. Convert them to lower case.
2882		for k, v := range cmds {
2883			lower := internal.ToLower(k)
2884			if lower != k {
2885				cmds[lower] = v
2886			}
2887		}
2888
2889		c.cmds = cmds
2890		return nil
2891	})
2892	return c.cmds, err
2893}
2894
2895//------------------------------------------------------------------------------
2896
2897type SlowLog struct {
2898	ID       int64
2899	Time     time.Time
2900	Duration time.Duration
2901	Args     []string
2902	// These are also optional fields emitted only by Redis 4.0 or greater:
2903	// https://redis.io/commands/slowlog#output-format
2904	ClientAddr string
2905	ClientName string
2906}
2907
2908type SlowLogCmd struct {
2909	baseCmd
2910
2911	val []SlowLog
2912}
2913
2914var _ Cmder = (*SlowLogCmd)(nil)
2915
2916func NewSlowLogCmd(ctx context.Context, args ...interface{}) *SlowLogCmd {
2917	return &SlowLogCmd{
2918		baseCmd: baseCmd{
2919			ctx:  ctx,
2920			args: args,
2921		},
2922	}
2923}
2924
2925func (cmd *SlowLogCmd) Val() []SlowLog {
2926	return cmd.val
2927}
2928
2929func (cmd *SlowLogCmd) Result() ([]SlowLog, error) {
2930	return cmd.Val(), cmd.Err()
2931}
2932
2933func (cmd *SlowLogCmd) String() string {
2934	return cmdString(cmd, cmd.val)
2935}
2936
2937func (cmd *SlowLogCmd) readReply(rd *proto.Reader) error {
2938	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
2939		cmd.val = make([]SlowLog, n)
2940		for i := 0; i < len(cmd.val); i++ {
2941			n, err := rd.ReadArrayLen()
2942			if err != nil {
2943				return nil, err
2944			}
2945			if n < 4 {
2946				err := fmt.Errorf("redis: got %d elements in slowlog get, expected at least 4", n)
2947				return nil, err
2948			}
2949
2950			id, err := rd.ReadIntReply()
2951			if err != nil {
2952				return nil, err
2953			}
2954
2955			createdAt, err := rd.ReadIntReply()
2956			if err != nil {
2957				return nil, err
2958			}
2959			createdAtTime := time.Unix(createdAt, 0)
2960
2961			costs, err := rd.ReadIntReply()
2962			if err != nil {
2963				return nil, err
2964			}
2965			costsDuration := time.Duration(costs) * time.Microsecond
2966
2967			cmdLen, err := rd.ReadArrayLen()
2968			if err != nil {
2969				return nil, err
2970			}
2971			if cmdLen < 1 {
2972				err := fmt.Errorf("redis: got %d elements commands reply in slowlog get, expected at least 1", cmdLen)
2973				return nil, err
2974			}
2975
2976			cmdString := make([]string, cmdLen)
2977			for i := 0; i < cmdLen; i++ {
2978				cmdString[i], err = rd.ReadString()
2979				if err != nil {
2980					return nil, err
2981				}
2982			}
2983
2984			var address, name string
2985			for i := 4; i < n; i++ {
2986				str, err := rd.ReadString()
2987				if err != nil {
2988					return nil, err
2989				}
2990				if i == 4 {
2991					address = str
2992				} else if i == 5 {
2993					name = str
2994				}
2995			}
2996
2997			cmd.val[i] = SlowLog{
2998				ID:         id,
2999				Time:       createdAtTime,
3000				Duration:   costsDuration,
3001				Args:       cmdString,
3002				ClientAddr: address,
3003				ClientName: name,
3004			}
3005		}
3006		return nil, nil
3007	})
3008	return err
3009}
3010