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