1package redis 2 3import ( 4 "bytes" 5 "fmt" 6 "strconv" 7 "strings" 8 "time" 9 10 "gopkg.in/redis.v5/internal" 11 "gopkg.in/redis.v5/internal/pool" 12 "gopkg.in/redis.v5/internal/proto" 13) 14 15var ( 16 _ Cmder = (*Cmd)(nil) 17 _ Cmder = (*SliceCmd)(nil) 18 _ Cmder = (*StatusCmd)(nil) 19 _ Cmder = (*IntCmd)(nil) 20 _ Cmder = (*DurationCmd)(nil) 21 _ Cmder = (*BoolCmd)(nil) 22 _ Cmder = (*StringCmd)(nil) 23 _ Cmder = (*FloatCmd)(nil) 24 _ Cmder = (*StringSliceCmd)(nil) 25 _ Cmder = (*BoolSliceCmd)(nil) 26 _ Cmder = (*StringStringMapCmd)(nil) 27 _ Cmder = (*StringIntMapCmd)(nil) 28 _ Cmder = (*ZSliceCmd)(nil) 29 _ Cmder = (*ScanCmd)(nil) 30 _ Cmder = (*ClusterSlotsCmd)(nil) 31) 32 33type Cmder interface { 34 args() []interface{} 35 arg(int) string 36 name() string 37 38 readReply(*pool.Conn) error 39 setErr(error) 40 41 readTimeout() *time.Duration 42 43 Err() error 44 fmt.Stringer 45} 46 47func setCmdsErr(cmds []Cmder, e error) { 48 for _, cmd := range cmds { 49 cmd.setErr(e) 50 } 51} 52 53func writeCmd(cn *pool.Conn, cmds ...Cmder) error { 54 cn.Wb.Reset() 55 for _, cmd := range cmds { 56 if err := cn.Wb.Append(cmd.args()); err != nil { 57 return err 58 } 59 } 60 61 _, err := cn.Write(cn.Wb.Bytes()) 62 return err 63} 64 65func cmdString(cmd Cmder, val interface{}) string { 66 var ss []string 67 for _, arg := range cmd.args() { 68 ss = append(ss, fmt.Sprint(arg)) 69 } 70 s := strings.Join(ss, " ") 71 if err := cmd.Err(); err != nil { 72 return s + ": " + err.Error() 73 } 74 if val != nil { 75 switch vv := val.(type) { 76 case []byte: 77 return s + ": " + string(vv) 78 default: 79 return s + ": " + fmt.Sprint(val) 80 } 81 } 82 return s 83 84} 85 86func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int { 87 switch cmd.name() { 88 case "eval", "evalsha": 89 if cmd.arg(2) != "0" { 90 return 3 91 } else { 92 return -1 93 } 94 } 95 if info == nil { 96 internal.Logf("info for cmd=%s not found", cmd.name()) 97 return -1 98 } 99 return int(info.FirstKeyPos) 100} 101 102//------------------------------------------------------------------------------ 103 104type baseCmd struct { 105 _args []interface{} 106 err error 107 108 _readTimeout *time.Duration 109} 110 111func (cmd *baseCmd) Err() error { 112 if cmd.err != nil { 113 return cmd.err 114 } 115 return nil 116} 117 118func (cmd *baseCmd) args() []interface{} { 119 return cmd._args 120} 121 122func (cmd *baseCmd) arg(pos int) string { 123 if pos < 0 || pos >= len(cmd._args) { 124 return "" 125 } 126 s, _ := cmd._args[pos].(string) 127 return s 128} 129 130func (cmd *baseCmd) name() string { 131 if len(cmd._args) > 0 { 132 // Cmd name must be lower cased. 133 s := internal.ToLower(cmd.arg(0)) 134 cmd._args[0] = s 135 return s 136 } 137 return "" 138} 139 140func (cmd *baseCmd) readTimeout() *time.Duration { 141 return cmd._readTimeout 142} 143 144func (cmd *baseCmd) setReadTimeout(d time.Duration) { 145 cmd._readTimeout = &d 146} 147 148func (cmd *baseCmd) setErr(e error) { 149 cmd.err = e 150} 151 152func newBaseCmd(args []interface{}) baseCmd { 153 if len(args) > 0 { 154 // Cmd name is expected to be in lower case. 155 args[0] = internal.ToLower(args[0].(string)) 156 } 157 return baseCmd{_args: args} 158} 159 160//------------------------------------------------------------------------------ 161 162type Cmd struct { 163 baseCmd 164 165 val interface{} 166} 167 168func NewCmd(args ...interface{}) *Cmd { 169 return &Cmd{ 170 baseCmd: baseCmd{_args: args}, 171 } 172} 173 174func (cmd *Cmd) Val() interface{} { 175 return cmd.val 176} 177 178func (cmd *Cmd) Result() (interface{}, error) { 179 return cmd.val, cmd.err 180} 181 182func (cmd *Cmd) String() string { 183 return cmdString(cmd, cmd.val) 184} 185 186func (cmd *Cmd) readReply(cn *pool.Conn) error { 187 cmd.val, cmd.err = cn.Rd.ReadReply(sliceParser) 188 if cmd.err != nil { 189 return cmd.err 190 } 191 if b, ok := cmd.val.([]byte); ok { 192 // Bytes must be copied, because underlying memory is reused. 193 cmd.val = string(b) 194 } 195 return nil 196} 197 198//------------------------------------------------------------------------------ 199 200type SliceCmd struct { 201 baseCmd 202 203 val []interface{} 204} 205 206func NewSliceCmd(args ...interface{}) *SliceCmd { 207 return &SliceCmd{ 208 baseCmd: baseCmd{_args: args}, 209 } 210} 211 212func (cmd *SliceCmd) Val() []interface{} { 213 return cmd.val 214} 215 216func (cmd *SliceCmd) Result() ([]interface{}, error) { 217 return cmd.val, cmd.err 218} 219 220func (cmd *SliceCmd) String() string { 221 return cmdString(cmd, cmd.val) 222} 223 224func (cmd *SliceCmd) readReply(cn *pool.Conn) error { 225 var v interface{} 226 v, cmd.err = cn.Rd.ReadArrayReply(sliceParser) 227 if cmd.err != nil { 228 return cmd.err 229 } 230 cmd.val = v.([]interface{}) 231 return nil 232} 233 234//------------------------------------------------------------------------------ 235 236type StatusCmd struct { 237 baseCmd 238 239 val string 240} 241 242func NewStatusCmd(args ...interface{}) *StatusCmd { 243 return &StatusCmd{ 244 baseCmd: baseCmd{_args: args}, 245 } 246} 247 248func (cmd *StatusCmd) Val() string { 249 return cmd.val 250} 251 252func (cmd *StatusCmd) Result() (string, error) { 253 return cmd.val, cmd.err 254} 255 256func (cmd *StatusCmd) String() string { 257 return cmdString(cmd, cmd.val) 258} 259 260func (cmd *StatusCmd) readReply(cn *pool.Conn) error { 261 cmd.val, cmd.err = cn.Rd.ReadStringReply() 262 return cmd.err 263} 264 265//------------------------------------------------------------------------------ 266 267type IntCmd struct { 268 baseCmd 269 270 val int64 271} 272 273func NewIntCmd(args ...interface{}) *IntCmd { 274 return &IntCmd{ 275 baseCmd: baseCmd{_args: args}, 276 } 277} 278 279func (cmd *IntCmd) Val() int64 { 280 return cmd.val 281} 282 283func (cmd *IntCmd) Result() (int64, error) { 284 return cmd.val, cmd.err 285} 286 287func (cmd *IntCmd) String() string { 288 return cmdString(cmd, cmd.val) 289} 290 291func (cmd *IntCmd) readReply(cn *pool.Conn) error { 292 cmd.val, cmd.err = cn.Rd.ReadIntReply() 293 return cmd.err 294} 295 296//------------------------------------------------------------------------------ 297 298type DurationCmd struct { 299 baseCmd 300 301 val time.Duration 302 precision time.Duration 303} 304 305func NewDurationCmd(precision time.Duration, args ...interface{}) *DurationCmd { 306 return &DurationCmd{ 307 baseCmd: baseCmd{_args: args}, 308 precision: precision, 309 } 310} 311 312func (cmd *DurationCmd) Val() time.Duration { 313 return cmd.val 314} 315 316func (cmd *DurationCmd) Result() (time.Duration, error) { 317 return cmd.val, cmd.err 318} 319 320func (cmd *DurationCmd) String() string { 321 return cmdString(cmd, cmd.val) 322} 323 324func (cmd *DurationCmd) readReply(cn *pool.Conn) error { 325 var n int64 326 n, cmd.err = cn.Rd.ReadIntReply() 327 if cmd.err != nil { 328 return cmd.err 329 } 330 cmd.val = time.Duration(n) * cmd.precision 331 return nil 332} 333 334//------------------------------------------------------------------------------ 335 336type TimeCmd struct { 337 baseCmd 338 339 val time.Time 340} 341 342func NewTimeCmd(args ...interface{}) *TimeCmd { 343 return &TimeCmd{ 344 baseCmd: baseCmd{_args: args}, 345 } 346} 347 348func (cmd *TimeCmd) Val() time.Time { 349 return cmd.val 350} 351 352func (cmd *TimeCmd) Result() (time.Time, error) { 353 return cmd.val, cmd.err 354} 355 356func (cmd *TimeCmd) String() string { 357 return cmdString(cmd, cmd.val) 358} 359 360func (cmd *TimeCmd) readReply(cn *pool.Conn) error { 361 var v interface{} 362 v, cmd.err = cn.Rd.ReadArrayReply(timeParser) 363 if cmd.err != nil { 364 return cmd.err 365 } 366 cmd.val = v.(time.Time) 367 return nil 368} 369 370//------------------------------------------------------------------------------ 371 372type BoolCmd struct { 373 baseCmd 374 375 val bool 376} 377 378func NewBoolCmd(args ...interface{}) *BoolCmd { 379 return &BoolCmd{ 380 baseCmd: baseCmd{_args: args}, 381 } 382} 383 384func (cmd *BoolCmd) Val() bool { 385 return cmd.val 386} 387 388func (cmd *BoolCmd) Result() (bool, error) { 389 return cmd.val, cmd.err 390} 391 392func (cmd *BoolCmd) String() string { 393 return cmdString(cmd, cmd.val) 394} 395 396var ok = []byte("OK") 397 398func (cmd *BoolCmd) readReply(cn *pool.Conn) error { 399 var v interface{} 400 v, cmd.err = cn.Rd.ReadReply(nil) 401 // `SET key value NX` returns nil when key already exists. But 402 // `SETNX key value` returns bool (0/1). So convert nil to bool. 403 // TODO: is this okay? 404 if cmd.err == Nil { 405 cmd.val = false 406 cmd.err = nil 407 return nil 408 } 409 if cmd.err != nil { 410 return cmd.err 411 } 412 switch v := v.(type) { 413 case int64: 414 cmd.val = v == 1 415 return nil 416 case []byte: 417 cmd.val = bytes.Equal(v, ok) 418 return nil 419 default: 420 cmd.err = fmt.Errorf("got %T, wanted int64 or string", v) 421 return cmd.err 422 } 423} 424 425//------------------------------------------------------------------------------ 426 427type StringCmd struct { 428 baseCmd 429 430 val []byte 431} 432 433func NewStringCmd(args ...interface{}) *StringCmd { 434 return &StringCmd{ 435 baseCmd: baseCmd{_args: args}, 436 } 437} 438 439func (cmd *StringCmd) Val() string { 440 return internal.BytesToString(cmd.val) 441} 442 443func (cmd *StringCmd) Result() (string, error) { 444 return cmd.Val(), cmd.err 445} 446 447func (cmd *StringCmd) Bytes() ([]byte, error) { 448 return cmd.val, cmd.err 449} 450 451func (cmd *StringCmd) Int64() (int64, error) { 452 if cmd.err != nil { 453 return 0, cmd.err 454 } 455 return strconv.ParseInt(cmd.Val(), 10, 64) 456} 457 458func (cmd *StringCmd) Uint64() (uint64, error) { 459 if cmd.err != nil { 460 return 0, cmd.err 461 } 462 return strconv.ParseUint(cmd.Val(), 10, 64) 463} 464 465func (cmd *StringCmd) Float64() (float64, error) { 466 if cmd.err != nil { 467 return 0, cmd.err 468 } 469 return strconv.ParseFloat(cmd.Val(), 64) 470} 471 472func (cmd *StringCmd) Scan(val interface{}) error { 473 if cmd.err != nil { 474 return cmd.err 475 } 476 return proto.Scan(cmd.val, val) 477} 478 479func (cmd *StringCmd) String() string { 480 return cmdString(cmd, cmd.val) 481} 482 483func (cmd *StringCmd) readReply(cn *pool.Conn) error { 484 cmd.val, cmd.err = cn.Rd.ReadBytesReply() 485 return cmd.err 486} 487 488//------------------------------------------------------------------------------ 489 490type FloatCmd struct { 491 baseCmd 492 493 val float64 494} 495 496func NewFloatCmd(args ...interface{}) *FloatCmd { 497 return &FloatCmd{ 498 baseCmd: baseCmd{_args: args}, 499 } 500} 501 502func (cmd *FloatCmd) Val() float64 { 503 return cmd.val 504} 505 506func (cmd *FloatCmd) Result() (float64, error) { 507 return cmd.Val(), cmd.Err() 508} 509 510func (cmd *FloatCmd) String() string { 511 return cmdString(cmd, cmd.val) 512} 513 514func (cmd *FloatCmd) readReply(cn *pool.Conn) error { 515 cmd.val, cmd.err = cn.Rd.ReadFloatReply() 516 return cmd.err 517} 518 519//------------------------------------------------------------------------------ 520 521type StringSliceCmd struct { 522 baseCmd 523 524 val []string 525} 526 527func NewStringSliceCmd(args ...interface{}) *StringSliceCmd { 528 return &StringSliceCmd{ 529 baseCmd: baseCmd{_args: args}, 530 } 531} 532 533func (cmd *StringSliceCmd) Val() []string { 534 return cmd.val 535} 536 537func (cmd *StringSliceCmd) Result() ([]string, error) { 538 return cmd.Val(), cmd.Err() 539} 540 541func (cmd *StringSliceCmd) String() string { 542 return cmdString(cmd, cmd.val) 543} 544 545func (cmd *StringSliceCmd) ScanSlice(container interface{}) error { 546 return proto.ScanSlice(cmd.Val(), container) 547} 548 549func (cmd *StringSliceCmd) readReply(cn *pool.Conn) error { 550 var v interface{} 551 v, cmd.err = cn.Rd.ReadArrayReply(stringSliceParser) 552 if cmd.err != nil { 553 return cmd.err 554 } 555 cmd.val = v.([]string) 556 return nil 557} 558 559//------------------------------------------------------------------------------ 560 561type BoolSliceCmd struct { 562 baseCmd 563 564 val []bool 565} 566 567func NewBoolSliceCmd(args ...interface{}) *BoolSliceCmd { 568 return &BoolSliceCmd{ 569 baseCmd: baseCmd{_args: args}, 570 } 571} 572 573func (cmd *BoolSliceCmd) Val() []bool { 574 return cmd.val 575} 576 577func (cmd *BoolSliceCmd) Result() ([]bool, error) { 578 return cmd.val, cmd.err 579} 580 581func (cmd *BoolSliceCmd) String() string { 582 return cmdString(cmd, cmd.val) 583} 584 585func (cmd *BoolSliceCmd) readReply(cn *pool.Conn) error { 586 var v interface{} 587 v, cmd.err = cn.Rd.ReadArrayReply(boolSliceParser) 588 if cmd.err != nil { 589 return cmd.err 590 } 591 cmd.val = v.([]bool) 592 return nil 593} 594 595//------------------------------------------------------------------------------ 596 597type StringStringMapCmd struct { 598 baseCmd 599 600 val map[string]string 601} 602 603func NewStringStringMapCmd(args ...interface{}) *StringStringMapCmd { 604 return &StringStringMapCmd{ 605 baseCmd: baseCmd{_args: args}, 606 } 607} 608 609func (cmd *StringStringMapCmd) Val() map[string]string { 610 return cmd.val 611} 612 613func (cmd *StringStringMapCmd) Result() (map[string]string, error) { 614 return cmd.val, cmd.err 615} 616 617func (cmd *StringStringMapCmd) String() string { 618 return cmdString(cmd, cmd.val) 619} 620 621func (cmd *StringStringMapCmd) readReply(cn *pool.Conn) error { 622 var v interface{} 623 v, cmd.err = cn.Rd.ReadArrayReply(stringStringMapParser) 624 if cmd.err != nil { 625 return cmd.err 626 } 627 cmd.val = v.(map[string]string) 628 return nil 629} 630 631//------------------------------------------------------------------------------ 632 633type StringIntMapCmd struct { 634 baseCmd 635 636 val map[string]int64 637} 638 639func NewStringIntMapCmd(args ...interface{}) *StringIntMapCmd { 640 return &StringIntMapCmd{ 641 baseCmd: baseCmd{_args: args}, 642 } 643} 644 645func (cmd *StringIntMapCmd) Val() map[string]int64 { 646 return cmd.val 647} 648 649func (cmd *StringIntMapCmd) Result() (map[string]int64, error) { 650 return cmd.val, cmd.err 651} 652 653func (cmd *StringIntMapCmd) String() string { 654 return cmdString(cmd, cmd.val) 655} 656 657func (cmd *StringIntMapCmd) readReply(cn *pool.Conn) error { 658 var v interface{} 659 v, cmd.err = cn.Rd.ReadArrayReply(stringIntMapParser) 660 if cmd.err != nil { 661 return cmd.err 662 } 663 cmd.val = v.(map[string]int64) 664 return nil 665} 666 667//------------------------------------------------------------------------------ 668 669type ZSliceCmd struct { 670 baseCmd 671 672 val []Z 673} 674 675func NewZSliceCmd(args ...interface{}) *ZSliceCmd { 676 return &ZSliceCmd{ 677 baseCmd: baseCmd{_args: args}, 678 } 679} 680 681func (cmd *ZSliceCmd) Val() []Z { 682 return cmd.val 683} 684 685func (cmd *ZSliceCmd) Result() ([]Z, error) { 686 return cmd.val, cmd.err 687} 688 689func (cmd *ZSliceCmd) String() string { 690 return cmdString(cmd, cmd.val) 691} 692 693func (cmd *ZSliceCmd) readReply(cn *pool.Conn) error { 694 var v interface{} 695 v, cmd.err = cn.Rd.ReadArrayReply(zSliceParser) 696 if cmd.err != nil { 697 return cmd.err 698 } 699 cmd.val = v.([]Z) 700 return nil 701} 702 703//------------------------------------------------------------------------------ 704 705type ScanCmd struct { 706 baseCmd 707 708 page []string 709 cursor uint64 710 711 process func(cmd Cmder) error 712} 713 714func NewScanCmd(process func(cmd Cmder) error, args ...interface{}) *ScanCmd { 715 return &ScanCmd{ 716 baseCmd: baseCmd{_args: args}, 717 process: process, 718 } 719} 720 721func (cmd *ScanCmd) Val() (keys []string, cursor uint64) { 722 return cmd.page, cmd.cursor 723} 724 725func (cmd *ScanCmd) Result() (keys []string, cursor uint64, err error) { 726 return cmd.page, cmd.cursor, cmd.err 727} 728 729func (cmd *ScanCmd) String() string { 730 return cmdString(cmd, cmd.page) 731} 732 733func (cmd *ScanCmd) readReply(cn *pool.Conn) error { 734 cmd.page, cmd.cursor, cmd.err = cn.Rd.ReadScanReply() 735 return cmd.err 736} 737 738// Iterator creates a new ScanIterator. 739func (cmd *ScanCmd) Iterator() *ScanIterator { 740 return &ScanIterator{ 741 cmd: cmd, 742 } 743} 744 745//------------------------------------------------------------------------------ 746 747type ClusterNode struct { 748 Id string 749 Addr string 750} 751 752type ClusterSlot struct { 753 Start int 754 End int 755 Nodes []ClusterNode 756} 757 758type ClusterSlotsCmd struct { 759 baseCmd 760 761 val []ClusterSlot 762} 763 764func NewClusterSlotsCmd(args ...interface{}) *ClusterSlotsCmd { 765 return &ClusterSlotsCmd{ 766 baseCmd: baseCmd{_args: args}, 767 } 768} 769 770func (cmd *ClusterSlotsCmd) Val() []ClusterSlot { 771 return cmd.val 772} 773 774func (cmd *ClusterSlotsCmd) Result() ([]ClusterSlot, error) { 775 return cmd.Val(), cmd.Err() 776} 777 778func (cmd *ClusterSlotsCmd) String() string { 779 return cmdString(cmd, cmd.val) 780} 781 782func (cmd *ClusterSlotsCmd) readReply(cn *pool.Conn) error { 783 var v interface{} 784 v, cmd.err = cn.Rd.ReadArrayReply(clusterSlotsParser) 785 if cmd.err != nil { 786 return cmd.err 787 } 788 cmd.val = v.([]ClusterSlot) 789 return nil 790} 791 792//------------------------------------------------------------------------------ 793 794// GeoLocation is used with GeoAdd to add geospatial location. 795type GeoLocation struct { 796 Name string 797 Longitude, Latitude, Dist float64 798 GeoHash int64 799} 800 801// GeoRadiusQuery is used with GeoRadius to query geospatial index. 802type GeoRadiusQuery struct { 803 Radius float64 804 // Can be m, km, ft, or mi. Default is km. 805 Unit string 806 WithCoord bool 807 WithDist bool 808 WithGeoHash bool 809 Count int 810 // Can be ASC or DESC. Default is no sort order. 811 Sort string 812} 813 814type GeoLocationCmd struct { 815 baseCmd 816 817 q *GeoRadiusQuery 818 locations []GeoLocation 819} 820 821func NewGeoLocationCmd(q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd { 822 args = append(args, q.Radius) 823 if q.Unit != "" { 824 args = append(args, q.Unit) 825 } else { 826 args = append(args, "km") 827 } 828 if q.WithCoord { 829 args = append(args, "WITHCOORD") 830 } 831 if q.WithDist { 832 args = append(args, "WITHDIST") 833 } 834 if q.WithGeoHash { 835 args = append(args, "WITHHASH") 836 } 837 if q.Count > 0 { 838 args = append(args, "COUNT", q.Count) 839 } 840 if q.Sort != "" { 841 args = append(args, q.Sort) 842 } 843 cmd := newBaseCmd(args) 844 return &GeoLocationCmd{ 845 baseCmd: cmd, 846 q: q, 847 } 848} 849 850func (cmd *GeoLocationCmd) Val() []GeoLocation { 851 return cmd.locations 852} 853 854func (cmd *GeoLocationCmd) Result() ([]GeoLocation, error) { 855 return cmd.locations, cmd.err 856} 857 858func (cmd *GeoLocationCmd) String() string { 859 return cmdString(cmd, cmd.locations) 860} 861 862func (cmd *GeoLocationCmd) readReply(cn *pool.Conn) error { 863 var v interface{} 864 v, cmd.err = cn.Rd.ReadArrayReply(newGeoLocationSliceParser(cmd.q)) 865 if cmd.err != nil { 866 return cmd.err 867 } 868 cmd.locations = v.([]GeoLocation) 869 return nil 870} 871 872//------------------------------------------------------------------------------ 873 874type GeoPos struct { 875 Longitude, Latitude float64 876} 877 878type GeoPosCmd struct { 879 baseCmd 880 881 positions []*GeoPos 882} 883 884func NewGeoPosCmd(args ...interface{}) *GeoPosCmd { 885 return &GeoPosCmd{ 886 baseCmd: baseCmd{_args: args}, 887 } 888} 889 890func (cmd *GeoPosCmd) Val() []*GeoPos { 891 return cmd.positions 892} 893 894func (cmd *GeoPosCmd) Result() ([]*GeoPos, error) { 895 return cmd.Val(), cmd.Err() 896} 897 898func (cmd *GeoPosCmd) String() string { 899 return cmdString(cmd, cmd.positions) 900} 901 902func (cmd *GeoPosCmd) readReply(cn *pool.Conn) error { 903 var v interface{} 904 v, cmd.err = cn.Rd.ReadArrayReply(geoPosSliceParser) 905 if cmd.err != nil { 906 return cmd.err 907 } 908 cmd.positions = v.([]*GeoPos) 909 return nil 910} 911 912//------------------------------------------------------------------------------ 913 914type CommandInfo struct { 915 Name string 916 Arity int8 917 Flags []string 918 FirstKeyPos int8 919 LastKeyPos int8 920 StepCount int8 921 ReadOnly bool 922} 923 924type CommandsInfoCmd struct { 925 baseCmd 926 927 val map[string]*CommandInfo 928} 929 930func NewCommandsInfoCmd(args ...interface{}) *CommandsInfoCmd { 931 return &CommandsInfoCmd{ 932 baseCmd: baseCmd{_args: args}, 933 } 934} 935 936func (cmd *CommandsInfoCmd) Val() map[string]*CommandInfo { 937 return cmd.val 938} 939 940func (cmd *CommandsInfoCmd) Result() (map[string]*CommandInfo, error) { 941 return cmd.Val(), cmd.Err() 942} 943 944func (cmd *CommandsInfoCmd) String() string { 945 return cmdString(cmd, cmd.val) 946} 947 948func (cmd *CommandsInfoCmd) readReply(cn *pool.Conn) error { 949 var v interface{} 950 v, cmd.err = cn.Rd.ReadArrayReply(commandInfoSliceParser) 951 if cmd.err != nil { 952 return cmd.err 953 } 954 cmd.val = v.(map[string]*CommandInfo) 955 return nil 956} 957