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