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