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