1package redis
2
3import (
4	"errors"
5	"io"
6	"time"
7
8	"github.com/go-redis/redis/v7/internal"
9)
10
11func usePrecise(dur time.Duration) bool {
12	return dur < time.Second || dur%time.Second != 0
13}
14
15func formatMs(dur time.Duration) int64 {
16	if dur > 0 && dur < time.Millisecond {
17		internal.Logger.Printf(
18			"specified duration is %s, but minimal supported value is %s",
19			dur, time.Millisecond,
20		)
21	}
22	return int64(dur / time.Millisecond)
23}
24
25func formatSec(dur time.Duration) int64 {
26	if dur > 0 && dur < time.Second {
27		internal.Logger.Printf(
28			"specified duration is %s, but minimal supported value is %s",
29			dur, time.Second,
30		)
31	}
32	return int64(dur / time.Second)
33}
34
35func appendArgs(dst, src []interface{}) []interface{} {
36	if len(src) == 1 {
37		switch v := src[0].(type) {
38		case []string:
39			for _, s := range v {
40				dst = append(dst, s)
41			}
42			return dst
43		case map[string]interface{}:
44			for k, v := range v {
45				dst = append(dst, k, v)
46			}
47			return dst
48		}
49	}
50
51	dst = append(dst, src...)
52	return dst
53}
54
55type Cmdable interface {
56	Pipeline() Pipeliner
57	Pipelined(fn func(Pipeliner) error) ([]Cmder, error)
58
59	TxPipelined(fn func(Pipeliner) error) ([]Cmder, error)
60	TxPipeline() Pipeliner
61
62	Command() *CommandsInfoCmd
63	ClientGetName() *StringCmd
64	Echo(message interface{}) *StringCmd
65	Ping() *StatusCmd
66	Quit() *StatusCmd
67	Del(keys ...string) *IntCmd
68	Unlink(keys ...string) *IntCmd
69	Dump(key string) *StringCmd
70	Exists(keys ...string) *IntCmd
71	Expire(key string, expiration time.Duration) *BoolCmd
72	ExpireAt(key string, tm time.Time) *BoolCmd
73	Keys(pattern string) *StringSliceCmd
74	Migrate(host, port, key string, db int, timeout time.Duration) *StatusCmd
75	Move(key string, db int) *BoolCmd
76	ObjectRefCount(key string) *IntCmd
77	ObjectEncoding(key string) *StringCmd
78	ObjectIdleTime(key string) *DurationCmd
79	Persist(key string) *BoolCmd
80	PExpire(key string, expiration time.Duration) *BoolCmd
81	PExpireAt(key string, tm time.Time) *BoolCmd
82	PTTL(key string) *DurationCmd
83	RandomKey() *StringCmd
84	Rename(key, newkey string) *StatusCmd
85	RenameNX(key, newkey string) *BoolCmd
86	Restore(key string, ttl time.Duration, value string) *StatusCmd
87	RestoreReplace(key string, ttl time.Duration, value string) *StatusCmd
88	Sort(key string, sort *Sort) *StringSliceCmd
89	SortStore(key, store string, sort *Sort) *IntCmd
90	SortInterfaces(key string, sort *Sort) *SliceCmd
91	Touch(keys ...string) *IntCmd
92	TTL(key string) *DurationCmd
93	Type(key string) *StatusCmd
94	Scan(cursor uint64, match string, count int64) *ScanCmd
95	SScan(key string, cursor uint64, match string, count int64) *ScanCmd
96	HScan(key string, cursor uint64, match string, count int64) *ScanCmd
97	ZScan(key string, cursor uint64, match string, count int64) *ScanCmd
98	Append(key, value string) *IntCmd
99	BitCount(key string, bitCount *BitCount) *IntCmd
100	BitOpAnd(destKey string, keys ...string) *IntCmd
101	BitOpOr(destKey string, keys ...string) *IntCmd
102	BitOpXor(destKey string, keys ...string) *IntCmd
103	BitOpNot(destKey string, key string) *IntCmd
104	BitPos(key string, bit int64, pos ...int64) *IntCmd
105	BitField(key string, args ...interface{}) *IntSliceCmd
106	Decr(key string) *IntCmd
107	DecrBy(key string, decrement int64) *IntCmd
108	Get(key string) *StringCmd
109	GetBit(key string, offset int64) *IntCmd
110	GetRange(key string, start, end int64) *StringCmd
111	GetSet(key string, value interface{}) *StringCmd
112	Incr(key string) *IntCmd
113	IncrBy(key string, value int64) *IntCmd
114	IncrByFloat(key string, value float64) *FloatCmd
115	MGet(keys ...string) *SliceCmd
116	MSet(values ...interface{}) *StatusCmd
117	MSetNX(values ...interface{}) *BoolCmd
118	Set(key string, value interface{}, expiration time.Duration) *StatusCmd
119	SetBit(key string, offset int64, value int) *IntCmd
120	SetNX(key string, value interface{}, expiration time.Duration) *BoolCmd
121	SetXX(key string, value interface{}, expiration time.Duration) *BoolCmd
122	SetRange(key string, offset int64, value string) *IntCmd
123	StrLen(key string) *IntCmd
124	HDel(key string, fields ...string) *IntCmd
125	HExists(key, field string) *BoolCmd
126	HGet(key, field string) *StringCmd
127	HGetAll(key string) *StringStringMapCmd
128	HIncrBy(key, field string, incr int64) *IntCmd
129	HIncrByFloat(key, field string, incr float64) *FloatCmd
130	HKeys(key string) *StringSliceCmd
131	HLen(key string) *IntCmd
132	HMGet(key string, fields ...string) *SliceCmd
133	HSet(key string, values ...interface{}) *IntCmd
134	HMSet(key string, values ...interface{}) *BoolCmd
135	HSetNX(key, field string, value interface{}) *BoolCmd
136	HVals(key string) *StringSliceCmd
137	BLPop(timeout time.Duration, keys ...string) *StringSliceCmd
138	BRPop(timeout time.Duration, keys ...string) *StringSliceCmd
139	BRPopLPush(source, destination string, timeout time.Duration) *StringCmd
140	LIndex(key string, index int64) *StringCmd
141	LInsert(key, op string, pivot, value interface{}) *IntCmd
142	LInsertBefore(key string, pivot, value interface{}) *IntCmd
143	LInsertAfter(key string, pivot, value interface{}) *IntCmd
144	LLen(key string) *IntCmd
145	LPop(key string) *StringCmd
146	LPush(key string, values ...interface{}) *IntCmd
147	LPushX(key string, values ...interface{}) *IntCmd
148	LRange(key string, start, stop int64) *StringSliceCmd
149	LRem(key string, count int64, value interface{}) *IntCmd
150	LSet(key string, index int64, value interface{}) *StatusCmd
151	LTrim(key string, start, stop int64) *StatusCmd
152	RPop(key string) *StringCmd
153	RPopLPush(source, destination string) *StringCmd
154	RPush(key string, values ...interface{}) *IntCmd
155	RPushX(key string, values ...interface{}) *IntCmd
156	SAdd(key string, members ...interface{}) *IntCmd
157	SCard(key string) *IntCmd
158	SDiff(keys ...string) *StringSliceCmd
159	SDiffStore(destination string, keys ...string) *IntCmd
160	SInter(keys ...string) *StringSliceCmd
161	SInterStore(destination string, keys ...string) *IntCmd
162	SIsMember(key string, member interface{}) *BoolCmd
163	SMembers(key string) *StringSliceCmd
164	SMembersMap(key string) *StringStructMapCmd
165	SMove(source, destination string, member interface{}) *BoolCmd
166	SPop(key string) *StringCmd
167	SPopN(key string, count int64) *StringSliceCmd
168	SRandMember(key string) *StringCmd
169	SRandMemberN(key string, count int64) *StringSliceCmd
170	SRem(key string, members ...interface{}) *IntCmd
171	SUnion(keys ...string) *StringSliceCmd
172	SUnionStore(destination string, keys ...string) *IntCmd
173	XAdd(a *XAddArgs) *StringCmd
174	XDel(stream string, ids ...string) *IntCmd
175	XLen(stream string) *IntCmd
176	XRange(stream, start, stop string) *XMessageSliceCmd
177	XRangeN(stream, start, stop string, count int64) *XMessageSliceCmd
178	XRevRange(stream string, start, stop string) *XMessageSliceCmd
179	XRevRangeN(stream string, start, stop string, count int64) *XMessageSliceCmd
180	XRead(a *XReadArgs) *XStreamSliceCmd
181	XReadStreams(streams ...string) *XStreamSliceCmd
182	XGroupCreate(stream, group, start string) *StatusCmd
183	XGroupCreateMkStream(stream, group, start string) *StatusCmd
184	XGroupSetID(stream, group, start string) *StatusCmd
185	XGroupDestroy(stream, group string) *IntCmd
186	XGroupDelConsumer(stream, group, consumer string) *IntCmd
187	XReadGroup(a *XReadGroupArgs) *XStreamSliceCmd
188	XAck(stream, group string, ids ...string) *IntCmd
189	XPending(stream, group string) *XPendingCmd
190	XPendingExt(a *XPendingExtArgs) *XPendingExtCmd
191	XClaim(a *XClaimArgs) *XMessageSliceCmd
192	XClaimJustID(a *XClaimArgs) *StringSliceCmd
193	XTrim(key string, maxLen int64) *IntCmd
194	XTrimApprox(key string, maxLen int64) *IntCmd
195	XInfoGroups(key string) *XInfoGroupsCmd
196	BZPopMax(timeout time.Duration, keys ...string) *ZWithKeyCmd
197	BZPopMin(timeout time.Duration, keys ...string) *ZWithKeyCmd
198	ZAdd(key string, members ...*Z) *IntCmd
199	ZAddNX(key string, members ...*Z) *IntCmd
200	ZAddXX(key string, members ...*Z) *IntCmd
201	ZAddCh(key string, members ...*Z) *IntCmd
202	ZAddNXCh(key string, members ...*Z) *IntCmd
203	ZAddXXCh(key string, members ...*Z) *IntCmd
204	ZIncr(key string, member *Z) *FloatCmd
205	ZIncrNX(key string, member *Z) *FloatCmd
206	ZIncrXX(key string, member *Z) *FloatCmd
207	ZCard(key string) *IntCmd
208	ZCount(key, min, max string) *IntCmd
209	ZLexCount(key, min, max string) *IntCmd
210	ZIncrBy(key string, increment float64, member string) *FloatCmd
211	ZInterStore(destination string, store *ZStore) *IntCmd
212	ZPopMax(key string, count ...int64) *ZSliceCmd
213	ZPopMin(key string, count ...int64) *ZSliceCmd
214	ZRange(key string, start, stop int64) *StringSliceCmd
215	ZRangeWithScores(key string, start, stop int64) *ZSliceCmd
216	ZRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd
217	ZRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd
218	ZRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd
219	ZRank(key, member string) *IntCmd
220	ZRem(key string, members ...interface{}) *IntCmd
221	ZRemRangeByRank(key string, start, stop int64) *IntCmd
222	ZRemRangeByScore(key, min, max string) *IntCmd
223	ZRemRangeByLex(key, min, max string) *IntCmd
224	ZRevRange(key string, start, stop int64) *StringSliceCmd
225	ZRevRangeWithScores(key string, start, stop int64) *ZSliceCmd
226	ZRevRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd
227	ZRevRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd
228	ZRevRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd
229	ZRevRank(key, member string) *IntCmd
230	ZScore(key, member string) *FloatCmd
231	ZUnionStore(dest string, store *ZStore) *IntCmd
232	PFAdd(key string, els ...interface{}) *IntCmd
233	PFCount(keys ...string) *IntCmd
234	PFMerge(dest string, keys ...string) *StatusCmd
235	BgRewriteAOF() *StatusCmd
236	BgSave() *StatusCmd
237	ClientKill(ipPort string) *StatusCmd
238	ClientKillByFilter(keys ...string) *IntCmd
239	ClientList() *StringCmd
240	ClientPause(dur time.Duration) *BoolCmd
241	ClientID() *IntCmd
242	ConfigGet(parameter string) *SliceCmd
243	ConfigResetStat() *StatusCmd
244	ConfigSet(parameter, value string) *StatusCmd
245	ConfigRewrite() *StatusCmd
246	DBSize() *IntCmd
247	FlushAll() *StatusCmd
248	FlushAllAsync() *StatusCmd
249	FlushDB() *StatusCmd
250	FlushDBAsync() *StatusCmd
251	Info(section ...string) *StringCmd
252	LastSave() *IntCmd
253	Save() *StatusCmd
254	Shutdown() *StatusCmd
255	ShutdownSave() *StatusCmd
256	ShutdownNoSave() *StatusCmd
257	SlaveOf(host, port string) *StatusCmd
258	Time() *TimeCmd
259	Eval(script string, keys []string, args ...interface{}) *Cmd
260	EvalSha(sha1 string, keys []string, args ...interface{}) *Cmd
261	ScriptExists(hashes ...string) *BoolSliceCmd
262	ScriptFlush() *StatusCmd
263	ScriptKill() *StatusCmd
264	ScriptLoad(script string) *StringCmd
265	DebugObject(key string) *StringCmd
266	Publish(channel string, message interface{}) *IntCmd
267	PubSubChannels(pattern string) *StringSliceCmd
268	PubSubNumSub(channels ...string) *StringIntMapCmd
269	PubSubNumPat() *IntCmd
270	ClusterSlots() *ClusterSlotsCmd
271	ClusterNodes() *StringCmd
272	ClusterMeet(host, port string) *StatusCmd
273	ClusterForget(nodeID string) *StatusCmd
274	ClusterReplicate(nodeID string) *StatusCmd
275	ClusterResetSoft() *StatusCmd
276	ClusterResetHard() *StatusCmd
277	ClusterInfo() *StringCmd
278	ClusterKeySlot(key string) *IntCmd
279	ClusterGetKeysInSlot(slot int, count int) *StringSliceCmd
280	ClusterCountFailureReports(nodeID string) *IntCmd
281	ClusterCountKeysInSlot(slot int) *IntCmd
282	ClusterDelSlots(slots ...int) *StatusCmd
283	ClusterDelSlotsRange(min, max int) *StatusCmd
284	ClusterSaveConfig() *StatusCmd
285	ClusterSlaves(nodeID string) *StringSliceCmd
286	ClusterFailover() *StatusCmd
287	ClusterAddSlots(slots ...int) *StatusCmd
288	ClusterAddSlotsRange(min, max int) *StatusCmd
289	GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd
290	GeoPos(key string, members ...string) *GeoPosCmd
291	GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd
292	GeoRadiusStore(key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd
293	GeoRadiusByMember(key, member string, query *GeoRadiusQuery) *GeoLocationCmd
294	GeoRadiusByMemberStore(key, member string, query *GeoRadiusQuery) *IntCmd
295	GeoDist(key string, member1, member2, unit string) *FloatCmd
296	GeoHash(key string, members ...string) *StringSliceCmd
297	ReadOnly() *StatusCmd
298	ReadWrite() *StatusCmd
299	MemoryUsage(key string, samples ...int) *IntCmd
300}
301
302type StatefulCmdable interface {
303	Cmdable
304	Auth(password string) *StatusCmd
305	Select(index int) *StatusCmd
306	SwapDB(index1, index2 int) *StatusCmd
307	ClientSetName(name string) *BoolCmd
308}
309
310var _ Cmdable = (*Client)(nil)
311var _ Cmdable = (*Tx)(nil)
312var _ Cmdable = (*Ring)(nil)
313var _ Cmdable = (*ClusterClient)(nil)
314
315type cmdable func(cmd Cmder) error
316
317type statefulCmdable func(cmd Cmder) error
318
319//------------------------------------------------------------------------------
320
321func (c statefulCmdable) Auth(password string) *StatusCmd {
322	cmd := NewStatusCmd("auth", password)
323	_ = c(cmd)
324	return cmd
325}
326
327func (c cmdable) Echo(message interface{}) *StringCmd {
328	cmd := NewStringCmd("echo", message)
329	_ = c(cmd)
330	return cmd
331}
332
333func (c cmdable) Ping() *StatusCmd {
334	cmd := NewStatusCmd("ping")
335	_ = c(cmd)
336	return cmd
337}
338
339func (c cmdable) Wait(numSlaves int, timeout time.Duration) *IntCmd {
340	cmd := NewIntCmd("wait", numSlaves, int(timeout/time.Millisecond))
341	_ = c(cmd)
342	return cmd
343}
344
345func (c cmdable) Quit() *StatusCmd {
346	panic("not implemented")
347}
348
349func (c statefulCmdable) Select(index int) *StatusCmd {
350	cmd := NewStatusCmd("select", index)
351	_ = c(cmd)
352	return cmd
353}
354
355func (c statefulCmdable) SwapDB(index1, index2 int) *StatusCmd {
356	cmd := NewStatusCmd("swapdb", index1, index2)
357	_ = c(cmd)
358	return cmd
359}
360
361//------------------------------------------------------------------------------
362
363func (c cmdable) Command() *CommandsInfoCmd {
364	cmd := NewCommandsInfoCmd("command")
365	_ = c(cmd)
366	return cmd
367}
368
369func (c cmdable) Del(keys ...string) *IntCmd {
370	args := make([]interface{}, 1+len(keys))
371	args[0] = "del"
372	for i, key := range keys {
373		args[1+i] = key
374	}
375	cmd := NewIntCmd(args...)
376	_ = c(cmd)
377	return cmd
378}
379
380func (c cmdable) Unlink(keys ...string) *IntCmd {
381	args := make([]interface{}, 1+len(keys))
382	args[0] = "unlink"
383	for i, key := range keys {
384		args[1+i] = key
385	}
386	cmd := NewIntCmd(args...)
387	_ = c(cmd)
388	return cmd
389}
390
391func (c cmdable) Dump(key string) *StringCmd {
392	cmd := NewStringCmd("dump", key)
393	_ = c(cmd)
394	return cmd
395}
396
397func (c cmdable) Exists(keys ...string) *IntCmd {
398	args := make([]interface{}, 1+len(keys))
399	args[0] = "exists"
400	for i, key := range keys {
401		args[1+i] = key
402	}
403	cmd := NewIntCmd(args...)
404	_ = c(cmd)
405	return cmd
406}
407
408func (c cmdable) Expire(key string, expiration time.Duration) *BoolCmd {
409	cmd := NewBoolCmd("expire", key, formatSec(expiration))
410	_ = c(cmd)
411	return cmd
412}
413
414func (c cmdable) ExpireAt(key string, tm time.Time) *BoolCmd {
415	cmd := NewBoolCmd("expireat", key, tm.Unix())
416	_ = c(cmd)
417	return cmd
418}
419
420func (c cmdable) Keys(pattern string) *StringSliceCmd {
421	cmd := NewStringSliceCmd("keys", pattern)
422	_ = c(cmd)
423	return cmd
424}
425
426func (c cmdable) Migrate(host, port, key string, db int, timeout time.Duration) *StatusCmd {
427	cmd := NewStatusCmd(
428		"migrate",
429		host,
430		port,
431		key,
432		db,
433		formatMs(timeout),
434	)
435	cmd.setReadTimeout(timeout)
436	_ = c(cmd)
437	return cmd
438}
439
440func (c cmdable) Move(key string, db int) *BoolCmd {
441	cmd := NewBoolCmd("move", key, db)
442	_ = c(cmd)
443	return cmd
444}
445
446func (c cmdable) ObjectRefCount(key string) *IntCmd {
447	cmd := NewIntCmd("object", "refcount", key)
448	_ = c(cmd)
449	return cmd
450}
451
452func (c cmdable) ObjectEncoding(key string) *StringCmd {
453	cmd := NewStringCmd("object", "encoding", key)
454	_ = c(cmd)
455	return cmd
456}
457
458func (c cmdable) ObjectIdleTime(key string) *DurationCmd {
459	cmd := NewDurationCmd(time.Second, "object", "idletime", key)
460	_ = c(cmd)
461	return cmd
462}
463
464func (c cmdable) Persist(key string) *BoolCmd {
465	cmd := NewBoolCmd("persist", key)
466	_ = c(cmd)
467	return cmd
468}
469
470func (c cmdable) PExpire(key string, expiration time.Duration) *BoolCmd {
471	cmd := NewBoolCmd("pexpire", key, formatMs(expiration))
472	_ = c(cmd)
473	return cmd
474}
475
476func (c cmdable) PExpireAt(key string, tm time.Time) *BoolCmd {
477	cmd := NewBoolCmd(
478		"pexpireat",
479		key,
480		tm.UnixNano()/int64(time.Millisecond),
481	)
482	_ = c(cmd)
483	return cmd
484}
485
486func (c cmdable) PTTL(key string) *DurationCmd {
487	cmd := NewDurationCmd(time.Millisecond, "pttl", key)
488	_ = c(cmd)
489	return cmd
490}
491
492func (c cmdable) RandomKey() *StringCmd {
493	cmd := NewStringCmd("randomkey")
494	_ = c(cmd)
495	return cmd
496}
497
498func (c cmdable) Rename(key, newkey string) *StatusCmd {
499	cmd := NewStatusCmd("rename", key, newkey)
500	_ = c(cmd)
501	return cmd
502}
503
504func (c cmdable) RenameNX(key, newkey string) *BoolCmd {
505	cmd := NewBoolCmd("renamenx", key, newkey)
506	_ = c(cmd)
507	return cmd
508}
509
510func (c cmdable) Restore(key string, ttl time.Duration, value string) *StatusCmd {
511	cmd := NewStatusCmd(
512		"restore",
513		key,
514		formatMs(ttl),
515		value,
516	)
517	_ = c(cmd)
518	return cmd
519}
520
521func (c cmdable) RestoreReplace(key string, ttl time.Duration, value string) *StatusCmd {
522	cmd := NewStatusCmd(
523		"restore",
524		key,
525		formatMs(ttl),
526		value,
527		"replace",
528	)
529	_ = c(cmd)
530	return cmd
531}
532
533type Sort struct {
534	By            string
535	Offset, Count int64
536	Get           []string
537	Order         string
538	Alpha         bool
539}
540
541func (sort *Sort) args(key string) []interface{} {
542	args := []interface{}{"sort", key}
543	if sort.By != "" {
544		args = append(args, "by", sort.By)
545	}
546	if sort.Offset != 0 || sort.Count != 0 {
547		args = append(args, "limit", sort.Offset, sort.Count)
548	}
549	for _, get := range sort.Get {
550		args = append(args, "get", get)
551	}
552	if sort.Order != "" {
553		args = append(args, sort.Order)
554	}
555	if sort.Alpha {
556		args = append(args, "alpha")
557	}
558	return args
559}
560
561func (c cmdable) Sort(key string, sort *Sort) *StringSliceCmd {
562	cmd := NewStringSliceCmd(sort.args(key)...)
563	_ = c(cmd)
564	return cmd
565}
566
567func (c cmdable) SortStore(key, store string, sort *Sort) *IntCmd {
568	args := sort.args(key)
569	if store != "" {
570		args = append(args, "store", store)
571	}
572	cmd := NewIntCmd(args...)
573	_ = c(cmd)
574	return cmd
575}
576
577func (c cmdable) SortInterfaces(key string, sort *Sort) *SliceCmd {
578	cmd := NewSliceCmd(sort.args(key)...)
579	_ = c(cmd)
580	return cmd
581}
582
583func (c cmdable) Touch(keys ...string) *IntCmd {
584	args := make([]interface{}, len(keys)+1)
585	args[0] = "touch"
586	for i, key := range keys {
587		args[i+1] = key
588	}
589	cmd := NewIntCmd(args...)
590	_ = c(cmd)
591	return cmd
592}
593
594func (c cmdable) TTL(key string) *DurationCmd {
595	cmd := NewDurationCmd(time.Second, "ttl", key)
596	_ = c(cmd)
597	return cmd
598}
599
600func (c cmdable) Type(key string) *StatusCmd {
601	cmd := NewStatusCmd("type", key)
602	_ = c(cmd)
603	return cmd
604}
605
606func (c cmdable) Scan(cursor uint64, match string, count int64) *ScanCmd {
607	args := []interface{}{"scan", cursor}
608	if match != "" {
609		args = append(args, "match", match)
610	}
611	if count > 0 {
612		args = append(args, "count", count)
613	}
614	cmd := NewScanCmd(c, args...)
615	_ = c(cmd)
616	return cmd
617}
618
619func (c cmdable) SScan(key string, cursor uint64, match string, count int64) *ScanCmd {
620	args := []interface{}{"sscan", key, cursor}
621	if match != "" {
622		args = append(args, "match", match)
623	}
624	if count > 0 {
625		args = append(args, "count", count)
626	}
627	cmd := NewScanCmd(c, args...)
628	_ = c(cmd)
629	return cmd
630}
631
632func (c cmdable) HScan(key string, cursor uint64, match string, count int64) *ScanCmd {
633	args := []interface{}{"hscan", key, cursor}
634	if match != "" {
635		args = append(args, "match", match)
636	}
637	if count > 0 {
638		args = append(args, "count", count)
639	}
640	cmd := NewScanCmd(c, args...)
641	_ = c(cmd)
642	return cmd
643}
644
645func (c cmdable) ZScan(key string, cursor uint64, match string, count int64) *ScanCmd {
646	args := []interface{}{"zscan", key, cursor}
647	if match != "" {
648		args = append(args, "match", match)
649	}
650	if count > 0 {
651		args = append(args, "count", count)
652	}
653	cmd := NewScanCmd(c, args...)
654	_ = c(cmd)
655	return cmd
656}
657
658//------------------------------------------------------------------------------
659
660func (c cmdable) Append(key, value string) *IntCmd {
661	cmd := NewIntCmd("append", key, value)
662	_ = c(cmd)
663	return cmd
664}
665
666type BitCount struct {
667	Start, End int64
668}
669
670func (c cmdable) BitCount(key string, bitCount *BitCount) *IntCmd {
671	args := []interface{}{"bitcount", key}
672	if bitCount != nil {
673		args = append(
674			args,
675			bitCount.Start,
676			bitCount.End,
677		)
678	}
679	cmd := NewIntCmd(args...)
680	_ = c(cmd)
681	return cmd
682}
683
684func (c cmdable) bitOp(op, destKey string, keys ...string) *IntCmd {
685	args := make([]interface{}, 3+len(keys))
686	args[0] = "bitop"
687	args[1] = op
688	args[2] = destKey
689	for i, key := range keys {
690		args[3+i] = key
691	}
692	cmd := NewIntCmd(args...)
693	_ = c(cmd)
694	return cmd
695}
696
697func (c cmdable) BitOpAnd(destKey string, keys ...string) *IntCmd {
698	return c.bitOp("and", destKey, keys...)
699}
700
701func (c cmdable) BitOpOr(destKey string, keys ...string) *IntCmd {
702	return c.bitOp("or", destKey, keys...)
703}
704
705func (c cmdable) BitOpXor(destKey string, keys ...string) *IntCmd {
706	return c.bitOp("xor", destKey, keys...)
707}
708
709func (c cmdable) BitOpNot(destKey string, key string) *IntCmd {
710	return c.bitOp("not", destKey, key)
711}
712
713func (c cmdable) BitPos(key string, bit int64, pos ...int64) *IntCmd {
714	args := make([]interface{}, 3+len(pos))
715	args[0] = "bitpos"
716	args[1] = key
717	args[2] = bit
718	switch len(pos) {
719	case 0:
720	case 1:
721		args[3] = pos[0]
722	case 2:
723		args[3] = pos[0]
724		args[4] = pos[1]
725	default:
726		panic("too many arguments")
727	}
728	cmd := NewIntCmd(args...)
729	_ = c(cmd)
730	return cmd
731}
732
733func (c cmdable) BitField(key string, args ...interface{}) *IntSliceCmd {
734	a := make([]interface{}, 0, 2+len(args))
735	a = append(a, "bitfield")
736	a = append(a, key)
737	a = append(a, args...)
738	cmd := NewIntSliceCmd(a...)
739	_ = c(cmd)
740	return cmd
741}
742
743func (c cmdable) Decr(key string) *IntCmd {
744	cmd := NewIntCmd("decr", key)
745	_ = c(cmd)
746	return cmd
747}
748
749func (c cmdable) DecrBy(key string, decrement int64) *IntCmd {
750	cmd := NewIntCmd("decrby", key, decrement)
751	_ = c(cmd)
752	return cmd
753}
754
755// Redis `GET key` command. It returns redis.Nil error when key does not exist.
756func (c cmdable) Get(key string) *StringCmd {
757	cmd := NewStringCmd("get", key)
758	_ = c(cmd)
759	return cmd
760}
761
762func (c cmdable) GetBit(key string, offset int64) *IntCmd {
763	cmd := NewIntCmd("getbit", key, offset)
764	_ = c(cmd)
765	return cmd
766}
767
768func (c cmdable) GetRange(key string, start, end int64) *StringCmd {
769	cmd := NewStringCmd("getrange", key, start, end)
770	_ = c(cmd)
771	return cmd
772}
773
774func (c cmdable) GetSet(key string, value interface{}) *StringCmd {
775	cmd := NewStringCmd("getset", key, value)
776	_ = c(cmd)
777	return cmd
778}
779
780func (c cmdable) Incr(key string) *IntCmd {
781	cmd := NewIntCmd("incr", key)
782	_ = c(cmd)
783	return cmd
784}
785
786func (c cmdable) IncrBy(key string, value int64) *IntCmd {
787	cmd := NewIntCmd("incrby", key, value)
788	_ = c(cmd)
789	return cmd
790}
791
792func (c cmdable) IncrByFloat(key string, value float64) *FloatCmd {
793	cmd := NewFloatCmd("incrbyfloat", key, value)
794	_ = c(cmd)
795	return cmd
796}
797
798func (c cmdable) MGet(keys ...string) *SliceCmd {
799	args := make([]interface{}, 1+len(keys))
800	args[0] = "mget"
801	for i, key := range keys {
802		args[1+i] = key
803	}
804	cmd := NewSliceCmd(args...)
805	_ = c(cmd)
806	return cmd
807}
808
809// MSet is like Set but accepts multiple values:
810//   - MSet("key1", "value1", "key2", "value2")
811//   - MSet([]string{"key1", "value1", "key2", "value2"})
812//   - MSet(map[string]interface{}{"key1": "value1", "key2": "value2"})
813func (c cmdable) MSet(values ...interface{}) *StatusCmd {
814	args := make([]interface{}, 1, 1+len(values))
815	args[0] = "mset"
816	args = appendArgs(args, values)
817	cmd := NewStatusCmd(args...)
818	_ = c(cmd)
819	return cmd
820}
821
822// MSetNX is like SetNX but accepts multiple values:
823//   - MSetNX("key1", "value1", "key2", "value2")
824//   - MSetNX([]string{"key1", "value1", "key2", "value2"})
825//   - MSetNX(map[string]interface{}{"key1": "value1", "key2": "value2"})
826func (c cmdable) MSetNX(values ...interface{}) *BoolCmd {
827	args := make([]interface{}, 1, 1+len(values))
828	args[0] = "msetnx"
829	args = appendArgs(args, values)
830	cmd := NewBoolCmd(args...)
831	_ = c(cmd)
832	return cmd
833}
834
835// Redis `SET key value [expiration]` command.
836//
837// Use expiration for `SETEX`-like behavior.
838// Zero expiration means the key has no expiration time.
839func (c cmdable) Set(key string, value interface{}, expiration time.Duration) *StatusCmd {
840	args := make([]interface{}, 3, 5)
841	args[0] = "set"
842	args[1] = key
843	args[2] = value
844	if expiration > 0 {
845		if usePrecise(expiration) {
846			args = append(args, "px", formatMs(expiration))
847		} else {
848			args = append(args, "ex", formatSec(expiration))
849		}
850	}
851	cmd := NewStatusCmd(args...)
852	_ = c(cmd)
853	return cmd
854}
855
856func (c cmdable) SetBit(key string, offset int64, value int) *IntCmd {
857	cmd := NewIntCmd(
858		"setbit",
859		key,
860		offset,
861		value,
862	)
863	_ = c(cmd)
864	return cmd
865}
866
867// Redis `SET key value [expiration] NX` command.
868//
869// Zero expiration means the key has no expiration time.
870func (c cmdable) SetNX(key string, value interface{}, expiration time.Duration) *BoolCmd {
871	var cmd *BoolCmd
872	if expiration == 0 {
873		// Use old `SETNX` to support old Redis versions.
874		cmd = NewBoolCmd("setnx", key, value)
875	} else {
876		if usePrecise(expiration) {
877			cmd = NewBoolCmd("set", key, value, "px", formatMs(expiration), "nx")
878		} else {
879			cmd = NewBoolCmd("set", key, value, "ex", formatSec(expiration), "nx")
880		}
881	}
882	_ = c(cmd)
883	return cmd
884}
885
886// Redis `SET key value [expiration] XX` command.
887//
888// Zero expiration means the key has no expiration time.
889func (c cmdable) SetXX(key string, value interface{}, expiration time.Duration) *BoolCmd {
890	var cmd *BoolCmd
891	if expiration == 0 {
892		cmd = NewBoolCmd("set", key, value, "xx")
893	} else {
894		if usePrecise(expiration) {
895			cmd = NewBoolCmd("set", key, value, "px", formatMs(expiration), "xx")
896		} else {
897			cmd = NewBoolCmd("set", key, value, "ex", formatSec(expiration), "xx")
898		}
899	}
900	_ = c(cmd)
901	return cmd
902}
903
904func (c cmdable) SetRange(key string, offset int64, value string) *IntCmd {
905	cmd := NewIntCmd("setrange", key, offset, value)
906	_ = c(cmd)
907	return cmd
908}
909
910func (c cmdable) StrLen(key string) *IntCmd {
911	cmd := NewIntCmd("strlen", key)
912	_ = c(cmd)
913	return cmd
914}
915
916//------------------------------------------------------------------------------
917
918func (c cmdable) HDel(key string, fields ...string) *IntCmd {
919	args := make([]interface{}, 2+len(fields))
920	args[0] = "hdel"
921	args[1] = key
922	for i, field := range fields {
923		args[2+i] = field
924	}
925	cmd := NewIntCmd(args...)
926	_ = c(cmd)
927	return cmd
928}
929
930func (c cmdable) HExists(key, field string) *BoolCmd {
931	cmd := NewBoolCmd("hexists", key, field)
932	_ = c(cmd)
933	return cmd
934}
935
936func (c cmdable) HGet(key, field string) *StringCmd {
937	cmd := NewStringCmd("hget", key, field)
938	_ = c(cmd)
939	return cmd
940}
941
942func (c cmdable) HGetAll(key string) *StringStringMapCmd {
943	cmd := NewStringStringMapCmd("hgetall", key)
944	_ = c(cmd)
945	return cmd
946}
947
948func (c cmdable) HIncrBy(key, field string, incr int64) *IntCmd {
949	cmd := NewIntCmd("hincrby", key, field, incr)
950	_ = c(cmd)
951	return cmd
952}
953
954func (c cmdable) HIncrByFloat(key, field string, incr float64) *FloatCmd {
955	cmd := NewFloatCmd("hincrbyfloat", key, field, incr)
956	_ = c(cmd)
957	return cmd
958}
959
960func (c cmdable) HKeys(key string) *StringSliceCmd {
961	cmd := NewStringSliceCmd("hkeys", key)
962	_ = c(cmd)
963	return cmd
964}
965
966func (c cmdable) HLen(key string) *IntCmd {
967	cmd := NewIntCmd("hlen", key)
968	_ = c(cmd)
969	return cmd
970}
971
972// HMGet returns the values for the specified fields in the hash stored at key.
973// It returns an interface{} to distinguish between empty string and nil value.
974func (c cmdable) HMGet(key string, fields ...string) *SliceCmd {
975	args := make([]interface{}, 2+len(fields))
976	args[0] = "hmget"
977	args[1] = key
978	for i, field := range fields {
979		args[2+i] = field
980	}
981	cmd := NewSliceCmd(args...)
982	_ = c(cmd)
983	return cmd
984}
985
986// HSet accepts values in following formats:
987//   - HMSet("myhash", "key1", "value1", "key2", "value2")
988//   - HMSet("myhash", []string{"key1", "value1", "key2", "value2"})
989//   - HMSet("myhash", map[string]interface{}{"key1": "value1", "key2": "value2"})
990//
991// Note that it requires Redis v4 for multiple field/value pairs support.
992func (c cmdable) HSet(key string, values ...interface{}) *IntCmd {
993	args := make([]interface{}, 2, 2+len(values))
994	args[0] = "hset"
995	args[1] = key
996	args = appendArgs(args, values)
997	cmd := NewIntCmd(args...)
998	_ = c(cmd)
999	return cmd
1000}
1001
1002// HMSet is a deprecated version of HSet left for compatibility with Redis 3.
1003func (c cmdable) HMSet(key string, values ...interface{}) *BoolCmd {
1004	args := make([]interface{}, 2, 2+len(values))
1005	args[0] = "hmset"
1006	args[1] = key
1007	args = appendArgs(args, values)
1008	cmd := NewBoolCmd(args...)
1009	_ = c(cmd)
1010	return cmd
1011}
1012
1013func (c cmdable) HSetNX(key, field string, value interface{}) *BoolCmd {
1014	cmd := NewBoolCmd("hsetnx", key, field, value)
1015	_ = c(cmd)
1016	return cmd
1017}
1018
1019func (c cmdable) HVals(key string) *StringSliceCmd {
1020	cmd := NewStringSliceCmd("hvals", key)
1021	_ = c(cmd)
1022	return cmd
1023}
1024
1025//------------------------------------------------------------------------------
1026
1027func (c cmdable) BLPop(timeout time.Duration, keys ...string) *StringSliceCmd {
1028	args := make([]interface{}, 1+len(keys)+1)
1029	args[0] = "blpop"
1030	for i, key := range keys {
1031		args[1+i] = key
1032	}
1033	args[len(args)-1] = formatSec(timeout)
1034	cmd := NewStringSliceCmd(args...)
1035	cmd.setReadTimeout(timeout)
1036	_ = c(cmd)
1037	return cmd
1038}
1039
1040func (c cmdable) BRPop(timeout time.Duration, keys ...string) *StringSliceCmd {
1041	args := make([]interface{}, 1+len(keys)+1)
1042	args[0] = "brpop"
1043	for i, key := range keys {
1044		args[1+i] = key
1045	}
1046	args[len(keys)+1] = formatSec(timeout)
1047	cmd := NewStringSliceCmd(args...)
1048	cmd.setReadTimeout(timeout)
1049	_ = c(cmd)
1050	return cmd
1051}
1052
1053func (c cmdable) BRPopLPush(source, destination string, timeout time.Duration) *StringCmd {
1054	cmd := NewStringCmd(
1055		"brpoplpush",
1056		source,
1057		destination,
1058		formatSec(timeout),
1059	)
1060	cmd.setReadTimeout(timeout)
1061	_ = c(cmd)
1062	return cmd
1063}
1064
1065func (c cmdable) LIndex(key string, index int64) *StringCmd {
1066	cmd := NewStringCmd("lindex", key, index)
1067	_ = c(cmd)
1068	return cmd
1069}
1070
1071func (c cmdable) LInsert(key, op string, pivot, value interface{}) *IntCmd {
1072	cmd := NewIntCmd("linsert", key, op, pivot, value)
1073	_ = c(cmd)
1074	return cmd
1075}
1076
1077func (c cmdable) LInsertBefore(key string, pivot, value interface{}) *IntCmd {
1078	cmd := NewIntCmd("linsert", key, "before", pivot, value)
1079	_ = c(cmd)
1080	return cmd
1081}
1082
1083func (c cmdable) LInsertAfter(key string, pivot, value interface{}) *IntCmd {
1084	cmd := NewIntCmd("linsert", key, "after", pivot, value)
1085	_ = c(cmd)
1086	return cmd
1087}
1088
1089func (c cmdable) LLen(key string) *IntCmd {
1090	cmd := NewIntCmd("llen", key)
1091	_ = c(cmd)
1092	return cmd
1093}
1094
1095func (c cmdable) LPop(key string) *StringCmd {
1096	cmd := NewStringCmd("lpop", key)
1097	_ = c(cmd)
1098	return cmd
1099}
1100
1101func (c cmdable) LPush(key string, values ...interface{}) *IntCmd {
1102	args := make([]interface{}, 2, 2+len(values))
1103	args[0] = "lpush"
1104	args[1] = key
1105	args = appendArgs(args, values)
1106	cmd := NewIntCmd(args...)
1107	_ = c(cmd)
1108	return cmd
1109}
1110
1111func (c cmdable) LPushX(key string, values ...interface{}) *IntCmd {
1112	args := make([]interface{}, 2, 2+len(values))
1113	args[0] = "lpushx"
1114	args[1] = key
1115	args = appendArgs(args, values)
1116	cmd := NewIntCmd(args...)
1117	_ = c(cmd)
1118	return cmd
1119}
1120
1121func (c cmdable) LRange(key string, start, stop int64) *StringSliceCmd {
1122	cmd := NewStringSliceCmd(
1123		"lrange",
1124		key,
1125		start,
1126		stop,
1127	)
1128	_ = c(cmd)
1129	return cmd
1130}
1131
1132func (c cmdable) LRem(key string, count int64, value interface{}) *IntCmd {
1133	cmd := NewIntCmd("lrem", key, count, value)
1134	_ = c(cmd)
1135	return cmd
1136}
1137
1138func (c cmdable) LSet(key string, index int64, value interface{}) *StatusCmd {
1139	cmd := NewStatusCmd("lset", key, index, value)
1140	_ = c(cmd)
1141	return cmd
1142}
1143
1144func (c cmdable) LTrim(key string, start, stop int64) *StatusCmd {
1145	cmd := NewStatusCmd(
1146		"ltrim",
1147		key,
1148		start,
1149		stop,
1150	)
1151	_ = c(cmd)
1152	return cmd
1153}
1154
1155func (c cmdable) RPop(key string) *StringCmd {
1156	cmd := NewStringCmd("rpop", key)
1157	_ = c(cmd)
1158	return cmd
1159}
1160
1161func (c cmdable) RPopLPush(source, destination string) *StringCmd {
1162	cmd := NewStringCmd("rpoplpush", source, destination)
1163	_ = c(cmd)
1164	return cmd
1165}
1166
1167func (c cmdable) RPush(key string, values ...interface{}) *IntCmd {
1168	args := make([]interface{}, 2, 2+len(values))
1169	args[0] = "rpush"
1170	args[1] = key
1171	args = appendArgs(args, values)
1172	cmd := NewIntCmd(args...)
1173	_ = c(cmd)
1174	return cmd
1175}
1176
1177func (c cmdable) RPushX(key string, values ...interface{}) *IntCmd {
1178	args := make([]interface{}, 2, 2+len(values))
1179	args[0] = "rpushx"
1180	args[1] = key
1181	args = appendArgs(args, values)
1182	cmd := NewIntCmd(args...)
1183	_ = c(cmd)
1184	return cmd
1185}
1186
1187//------------------------------------------------------------------------------
1188
1189func (c cmdable) SAdd(key string, members ...interface{}) *IntCmd {
1190	args := make([]interface{}, 2, 2+len(members))
1191	args[0] = "sadd"
1192	args[1] = key
1193	args = appendArgs(args, members)
1194	cmd := NewIntCmd(args...)
1195	_ = c(cmd)
1196	return cmd
1197}
1198
1199func (c cmdable) SCard(key string) *IntCmd {
1200	cmd := NewIntCmd("scard", key)
1201	_ = c(cmd)
1202	return cmd
1203}
1204
1205func (c cmdable) SDiff(keys ...string) *StringSliceCmd {
1206	args := make([]interface{}, 1+len(keys))
1207	args[0] = "sdiff"
1208	for i, key := range keys {
1209		args[1+i] = key
1210	}
1211	cmd := NewStringSliceCmd(args...)
1212	_ = c(cmd)
1213	return cmd
1214}
1215
1216func (c cmdable) SDiffStore(destination string, keys ...string) *IntCmd {
1217	args := make([]interface{}, 2+len(keys))
1218	args[0] = "sdiffstore"
1219	args[1] = destination
1220	for i, key := range keys {
1221		args[2+i] = key
1222	}
1223	cmd := NewIntCmd(args...)
1224	_ = c(cmd)
1225	return cmd
1226}
1227
1228func (c cmdable) SInter(keys ...string) *StringSliceCmd {
1229	args := make([]interface{}, 1+len(keys))
1230	args[0] = "sinter"
1231	for i, key := range keys {
1232		args[1+i] = key
1233	}
1234	cmd := NewStringSliceCmd(args...)
1235	_ = c(cmd)
1236	return cmd
1237}
1238
1239func (c cmdable) SInterStore(destination string, keys ...string) *IntCmd {
1240	args := make([]interface{}, 2+len(keys))
1241	args[0] = "sinterstore"
1242	args[1] = destination
1243	for i, key := range keys {
1244		args[2+i] = key
1245	}
1246	cmd := NewIntCmd(args...)
1247	_ = c(cmd)
1248	return cmd
1249}
1250
1251func (c cmdable) SIsMember(key string, member interface{}) *BoolCmd {
1252	cmd := NewBoolCmd("sismember", key, member)
1253	_ = c(cmd)
1254	return cmd
1255}
1256
1257// Redis `SMEMBERS key` command output as a slice
1258func (c cmdable) SMembers(key string) *StringSliceCmd {
1259	cmd := NewStringSliceCmd("smembers", key)
1260	_ = c(cmd)
1261	return cmd
1262}
1263
1264// Redis `SMEMBERS key` command output as a map
1265func (c cmdable) SMembersMap(key string) *StringStructMapCmd {
1266	cmd := NewStringStructMapCmd("smembers", key)
1267	_ = c(cmd)
1268	return cmd
1269}
1270
1271func (c cmdable) SMove(source, destination string, member interface{}) *BoolCmd {
1272	cmd := NewBoolCmd("smove", source, destination, member)
1273	_ = c(cmd)
1274	return cmd
1275}
1276
1277// Redis `SPOP key` command.
1278func (c cmdable) SPop(key string) *StringCmd {
1279	cmd := NewStringCmd("spop", key)
1280	_ = c(cmd)
1281	return cmd
1282}
1283
1284// Redis `SPOP key count` command.
1285func (c cmdable) SPopN(key string, count int64) *StringSliceCmd {
1286	cmd := NewStringSliceCmd("spop", key, count)
1287	_ = c(cmd)
1288	return cmd
1289}
1290
1291// Redis `SRANDMEMBER key` command.
1292func (c cmdable) SRandMember(key string) *StringCmd {
1293	cmd := NewStringCmd("srandmember", key)
1294	_ = c(cmd)
1295	return cmd
1296}
1297
1298// Redis `SRANDMEMBER key count` command.
1299func (c cmdable) SRandMemberN(key string, count int64) *StringSliceCmd {
1300	cmd := NewStringSliceCmd("srandmember", key, count)
1301	_ = c(cmd)
1302	return cmd
1303}
1304
1305func (c cmdable) SRem(key string, members ...interface{}) *IntCmd {
1306	args := make([]interface{}, 2, 2+len(members))
1307	args[0] = "srem"
1308	args[1] = key
1309	args = appendArgs(args, members)
1310	cmd := NewIntCmd(args...)
1311	_ = c(cmd)
1312	return cmd
1313}
1314
1315func (c cmdable) SUnion(keys ...string) *StringSliceCmd {
1316	args := make([]interface{}, 1+len(keys))
1317	args[0] = "sunion"
1318	for i, key := range keys {
1319		args[1+i] = key
1320	}
1321	cmd := NewStringSliceCmd(args...)
1322	_ = c(cmd)
1323	return cmd
1324}
1325
1326func (c cmdable) SUnionStore(destination string, keys ...string) *IntCmd {
1327	args := make([]interface{}, 2+len(keys))
1328	args[0] = "sunionstore"
1329	args[1] = destination
1330	for i, key := range keys {
1331		args[2+i] = key
1332	}
1333	cmd := NewIntCmd(args...)
1334	_ = c(cmd)
1335	return cmd
1336}
1337
1338//------------------------------------------------------------------------------
1339
1340type XAddArgs struct {
1341	Stream       string
1342	MaxLen       int64 // MAXLEN N
1343	MaxLenApprox int64 // MAXLEN ~ N
1344	ID           string
1345	Values       map[string]interface{}
1346}
1347
1348func (c cmdable) XAdd(a *XAddArgs) *StringCmd {
1349	args := make([]interface{}, 0, 6+len(a.Values)*2)
1350	args = append(args, "xadd")
1351	args = append(args, a.Stream)
1352	if a.MaxLen > 0 {
1353		args = append(args, "maxlen", a.MaxLen)
1354	} else if a.MaxLenApprox > 0 {
1355		args = append(args, "maxlen", "~", a.MaxLenApprox)
1356	}
1357	if a.ID != "" {
1358		args = append(args, a.ID)
1359	} else {
1360		args = append(args, "*")
1361	}
1362	for k, v := range a.Values {
1363		args = append(args, k)
1364		args = append(args, v)
1365	}
1366
1367	cmd := NewStringCmd(args...)
1368	_ = c(cmd)
1369	return cmd
1370}
1371
1372func (c cmdable) XDel(stream string, ids ...string) *IntCmd {
1373	args := []interface{}{"xdel", stream}
1374	for _, id := range ids {
1375		args = append(args, id)
1376	}
1377	cmd := NewIntCmd(args...)
1378	_ = c(cmd)
1379	return cmd
1380}
1381
1382func (c cmdable) XLen(stream string) *IntCmd {
1383	cmd := NewIntCmd("xlen", stream)
1384	_ = c(cmd)
1385	return cmd
1386}
1387
1388func (c cmdable) XRange(stream, start, stop string) *XMessageSliceCmd {
1389	cmd := NewXMessageSliceCmd("xrange", stream, start, stop)
1390	_ = c(cmd)
1391	return cmd
1392}
1393
1394func (c cmdable) XRangeN(stream, start, stop string, count int64) *XMessageSliceCmd {
1395	cmd := NewXMessageSliceCmd("xrange", stream, start, stop, "count", count)
1396	_ = c(cmd)
1397	return cmd
1398}
1399
1400func (c cmdable) XRevRange(stream, start, stop string) *XMessageSliceCmd {
1401	cmd := NewXMessageSliceCmd("xrevrange", stream, start, stop)
1402	_ = c(cmd)
1403	return cmd
1404}
1405
1406func (c cmdable) XRevRangeN(stream, start, stop string, count int64) *XMessageSliceCmd {
1407	cmd := NewXMessageSliceCmd("xrevrange", stream, start, stop, "count", count)
1408	_ = c(cmd)
1409	return cmd
1410}
1411
1412type XReadArgs struct {
1413	Streams []string // list of streams and ids, e.g. stream1 stream2 id1 id2
1414	Count   int64
1415	Block   time.Duration
1416}
1417
1418func (c cmdable) XRead(a *XReadArgs) *XStreamSliceCmd {
1419	args := make([]interface{}, 0, 5+len(a.Streams))
1420	args = append(args, "xread")
1421	if a.Count > 0 {
1422		args = append(args, "count")
1423		args = append(args, a.Count)
1424	}
1425	if a.Block >= 0 {
1426		args = append(args, "block")
1427		args = append(args, int64(a.Block/time.Millisecond))
1428	}
1429
1430	args = append(args, "streams")
1431	for _, s := range a.Streams {
1432		args = append(args, s)
1433	}
1434
1435	cmd := NewXStreamSliceCmd(args...)
1436	if a.Block >= 0 {
1437		cmd.setReadTimeout(a.Block)
1438	}
1439	_ = c(cmd)
1440	return cmd
1441}
1442
1443func (c cmdable) XReadStreams(streams ...string) *XStreamSliceCmd {
1444	return c.XRead(&XReadArgs{
1445		Streams: streams,
1446		Block:   -1,
1447	})
1448}
1449
1450func (c cmdable) XGroupCreate(stream, group, start string) *StatusCmd {
1451	cmd := NewStatusCmd("xgroup", "create", stream, group, start)
1452	_ = c(cmd)
1453	return cmd
1454}
1455
1456func (c cmdable) XGroupCreateMkStream(stream, group, start string) *StatusCmd {
1457	cmd := NewStatusCmd("xgroup", "create", stream, group, start, "mkstream")
1458	_ = c(cmd)
1459	return cmd
1460}
1461
1462func (c cmdable) XGroupSetID(stream, group, start string) *StatusCmd {
1463	cmd := NewStatusCmd("xgroup", "setid", stream, group, start)
1464	_ = c(cmd)
1465	return cmd
1466}
1467
1468func (c cmdable) XGroupDestroy(stream, group string) *IntCmd {
1469	cmd := NewIntCmd("xgroup", "destroy", stream, group)
1470	_ = c(cmd)
1471	return cmd
1472}
1473
1474func (c cmdable) XGroupDelConsumer(stream, group, consumer string) *IntCmd {
1475	cmd := NewIntCmd("xgroup", "delconsumer", stream, group, consumer)
1476	_ = c(cmd)
1477	return cmd
1478}
1479
1480type XReadGroupArgs struct {
1481	Group    string
1482	Consumer string
1483	Streams  []string // list of streams and ids, e.g. stream1 stream2 id1 id2
1484	Count    int64
1485	Block    time.Duration
1486	NoAck    bool
1487}
1488
1489func (c cmdable) XReadGroup(a *XReadGroupArgs) *XStreamSliceCmd {
1490	args := make([]interface{}, 0, 8+len(a.Streams))
1491	args = append(args, "xreadgroup", "group", a.Group, a.Consumer)
1492	if a.Count > 0 {
1493		args = append(args, "count", a.Count)
1494	}
1495	if a.Block >= 0 {
1496		args = append(args, "block", int64(a.Block/time.Millisecond))
1497	}
1498	if a.NoAck {
1499		args = append(args, "noack")
1500	}
1501	args = append(args, "streams")
1502	for _, s := range a.Streams {
1503		args = append(args, s)
1504	}
1505
1506	cmd := NewXStreamSliceCmd(args...)
1507	if a.Block >= 0 {
1508		cmd.setReadTimeout(a.Block)
1509	}
1510	_ = c(cmd)
1511	return cmd
1512}
1513
1514func (c cmdable) XAck(stream, group string, ids ...string) *IntCmd {
1515	args := []interface{}{"xack", stream, group}
1516	for _, id := range ids {
1517		args = append(args, id)
1518	}
1519	cmd := NewIntCmd(args...)
1520	_ = c(cmd)
1521	return cmd
1522}
1523
1524func (c cmdable) XPending(stream, group string) *XPendingCmd {
1525	cmd := NewXPendingCmd("xpending", stream, group)
1526	_ = c(cmd)
1527	return cmd
1528}
1529
1530type XPendingExtArgs struct {
1531	Stream   string
1532	Group    string
1533	Start    string
1534	End      string
1535	Count    int64
1536	Consumer string
1537}
1538
1539func (c cmdable) XPendingExt(a *XPendingExtArgs) *XPendingExtCmd {
1540	args := make([]interface{}, 0, 7)
1541	args = append(args, "xpending", a.Stream, a.Group, a.Start, a.End, a.Count)
1542	if a.Consumer != "" {
1543		args = append(args, a.Consumer)
1544	}
1545	cmd := NewXPendingExtCmd(args...)
1546	_ = c(cmd)
1547	return cmd
1548}
1549
1550type XClaimArgs struct {
1551	Stream   string
1552	Group    string
1553	Consumer string
1554	MinIdle  time.Duration
1555	Messages []string
1556}
1557
1558func (c cmdable) XClaim(a *XClaimArgs) *XMessageSliceCmd {
1559	args := xClaimArgs(a)
1560	cmd := NewXMessageSliceCmd(args...)
1561	_ = c(cmd)
1562	return cmd
1563}
1564
1565func (c cmdable) XClaimJustID(a *XClaimArgs) *StringSliceCmd {
1566	args := xClaimArgs(a)
1567	args = append(args, "justid")
1568	cmd := NewStringSliceCmd(args...)
1569	_ = c(cmd)
1570	return cmd
1571}
1572
1573func xClaimArgs(a *XClaimArgs) []interface{} {
1574	args := make([]interface{}, 0, 4+len(a.Messages))
1575	args = append(args,
1576		"xclaim",
1577		a.Stream,
1578		a.Group, a.Consumer,
1579		int64(a.MinIdle/time.Millisecond))
1580	for _, id := range a.Messages {
1581		args = append(args, id)
1582	}
1583	return args
1584}
1585
1586func (c cmdable) XTrim(key string, maxLen int64) *IntCmd {
1587	cmd := NewIntCmd("xtrim", key, "maxlen", maxLen)
1588	_ = c(cmd)
1589	return cmd
1590}
1591
1592func (c cmdable) XTrimApprox(key string, maxLen int64) *IntCmd {
1593	cmd := NewIntCmd("xtrim", key, "maxlen", "~", maxLen)
1594	_ = c(cmd)
1595	return cmd
1596}
1597
1598func (c cmdable) XInfoGroups(key string) *XInfoGroupsCmd {
1599	cmd := NewXInfoGroupsCmd(key)
1600	_ = c(cmd)
1601	return cmd
1602}
1603
1604//------------------------------------------------------------------------------
1605
1606// Z represents sorted set member.
1607type Z struct {
1608	Score  float64
1609	Member interface{}
1610}
1611
1612// ZWithKey represents sorted set member including the name of the key where it was popped.
1613type ZWithKey struct {
1614	Z
1615	Key string
1616}
1617
1618// ZStore is used as an arg to ZInterStore and ZUnionStore.
1619type ZStore struct {
1620	Keys    []string
1621	Weights []float64
1622	// Can be SUM, MIN or MAX.
1623	Aggregate string
1624}
1625
1626// Redis `BZPOPMAX key [key ...] timeout` command.
1627func (c cmdable) BZPopMax(timeout time.Duration, keys ...string) *ZWithKeyCmd {
1628	args := make([]interface{}, 1+len(keys)+1)
1629	args[0] = "bzpopmax"
1630	for i, key := range keys {
1631		args[1+i] = key
1632	}
1633	args[len(args)-1] = formatSec(timeout)
1634	cmd := NewZWithKeyCmd(args...)
1635	cmd.setReadTimeout(timeout)
1636	_ = c(cmd)
1637	return cmd
1638}
1639
1640// Redis `BZPOPMIN key [key ...] timeout` command.
1641func (c cmdable) BZPopMin(timeout time.Duration, keys ...string) *ZWithKeyCmd {
1642	args := make([]interface{}, 1+len(keys)+1)
1643	args[0] = "bzpopmin"
1644	for i, key := range keys {
1645		args[1+i] = key
1646	}
1647	args[len(args)-1] = formatSec(timeout)
1648	cmd := NewZWithKeyCmd(args...)
1649	cmd.setReadTimeout(timeout)
1650	_ = c(cmd)
1651	return cmd
1652}
1653
1654func (c cmdable) zAdd(a []interface{}, n int, members ...*Z) *IntCmd {
1655	for i, m := range members {
1656		a[n+2*i] = m.Score
1657		a[n+2*i+1] = m.Member
1658	}
1659	cmd := NewIntCmd(a...)
1660	_ = c(cmd)
1661	return cmd
1662}
1663
1664// Redis `ZADD key score member [score member ...]` command.
1665func (c cmdable) ZAdd(key string, members ...*Z) *IntCmd {
1666	const n = 2
1667	a := make([]interface{}, n+2*len(members))
1668	a[0], a[1] = "zadd", key
1669	return c.zAdd(a, n, members...)
1670}
1671
1672// Redis `ZADD key NX score member [score member ...]` command.
1673func (c cmdable) ZAddNX(key string, members ...*Z) *IntCmd {
1674	const n = 3
1675	a := make([]interface{}, n+2*len(members))
1676	a[0], a[1], a[2] = "zadd", key, "nx"
1677	return c.zAdd(a, n, members...)
1678}
1679
1680// Redis `ZADD key XX score member [score member ...]` command.
1681func (c cmdable) ZAddXX(key string, members ...*Z) *IntCmd {
1682	const n = 3
1683	a := make([]interface{}, n+2*len(members))
1684	a[0], a[1], a[2] = "zadd", key, "xx"
1685	return c.zAdd(a, n, members...)
1686}
1687
1688// Redis `ZADD key CH score member [score member ...]` command.
1689func (c cmdable) ZAddCh(key string, members ...*Z) *IntCmd {
1690	const n = 3
1691	a := make([]interface{}, n+2*len(members))
1692	a[0], a[1], a[2] = "zadd", key, "ch"
1693	return c.zAdd(a, n, members...)
1694}
1695
1696// Redis `ZADD key NX CH score member [score member ...]` command.
1697func (c cmdable) ZAddNXCh(key string, members ...*Z) *IntCmd {
1698	const n = 4
1699	a := make([]interface{}, n+2*len(members))
1700	a[0], a[1], a[2], a[3] = "zadd", key, "nx", "ch"
1701	return c.zAdd(a, n, members...)
1702}
1703
1704// Redis `ZADD key XX CH score member [score member ...]` command.
1705func (c cmdable) ZAddXXCh(key string, members ...*Z) *IntCmd {
1706	const n = 4
1707	a := make([]interface{}, n+2*len(members))
1708	a[0], a[1], a[2], a[3] = "zadd", key, "xx", "ch"
1709	return c.zAdd(a, n, members...)
1710}
1711
1712func (c cmdable) zIncr(a []interface{}, n int, members ...*Z) *FloatCmd {
1713	for i, m := range members {
1714		a[n+2*i] = m.Score
1715		a[n+2*i+1] = m.Member
1716	}
1717	cmd := NewFloatCmd(a...)
1718	_ = c(cmd)
1719	return cmd
1720}
1721
1722// Redis `ZADD key INCR score member` command.
1723func (c cmdable) ZIncr(key string, member *Z) *FloatCmd {
1724	const n = 3
1725	a := make([]interface{}, n+2)
1726	a[0], a[1], a[2] = "zadd", key, "incr"
1727	return c.zIncr(a, n, member)
1728}
1729
1730// Redis `ZADD key NX INCR score member` command.
1731func (c cmdable) ZIncrNX(key string, member *Z) *FloatCmd {
1732	const n = 4
1733	a := make([]interface{}, n+2)
1734	a[0], a[1], a[2], a[3] = "zadd", key, "incr", "nx"
1735	return c.zIncr(a, n, member)
1736}
1737
1738// Redis `ZADD key XX INCR score member` command.
1739func (c cmdable) ZIncrXX(key string, member *Z) *FloatCmd {
1740	const n = 4
1741	a := make([]interface{}, n+2)
1742	a[0], a[1], a[2], a[3] = "zadd", key, "incr", "xx"
1743	return c.zIncr(a, n, member)
1744}
1745
1746func (c cmdable) ZCard(key string) *IntCmd {
1747	cmd := NewIntCmd("zcard", key)
1748	_ = c(cmd)
1749	return cmd
1750}
1751
1752func (c cmdable) ZCount(key, min, max string) *IntCmd {
1753	cmd := NewIntCmd("zcount", key, min, max)
1754	_ = c(cmd)
1755	return cmd
1756}
1757
1758func (c cmdable) ZLexCount(key, min, max string) *IntCmd {
1759	cmd := NewIntCmd("zlexcount", key, min, max)
1760	_ = c(cmd)
1761	return cmd
1762}
1763
1764func (c cmdable) ZIncrBy(key string, increment float64, member string) *FloatCmd {
1765	cmd := NewFloatCmd("zincrby", key, increment, member)
1766	_ = c(cmd)
1767	return cmd
1768}
1769
1770func (c cmdable) ZInterStore(destination string, store *ZStore) *IntCmd {
1771	args := make([]interface{}, 3+len(store.Keys))
1772	args[0] = "zinterstore"
1773	args[1] = destination
1774	args[2] = len(store.Keys)
1775	for i, key := range store.Keys {
1776		args[3+i] = key
1777	}
1778	if len(store.Weights) > 0 {
1779		args = append(args, "weights")
1780		for _, weight := range store.Weights {
1781			args = append(args, weight)
1782		}
1783	}
1784	if store.Aggregate != "" {
1785		args = append(args, "aggregate", store.Aggregate)
1786	}
1787	cmd := NewIntCmd(args...)
1788	_ = c(cmd)
1789	return cmd
1790}
1791
1792func (c cmdable) ZPopMax(key string, count ...int64) *ZSliceCmd {
1793	args := []interface{}{
1794		"zpopmax",
1795		key,
1796	}
1797
1798	switch len(count) {
1799	case 0:
1800		break
1801	case 1:
1802		args = append(args, count[0])
1803	default:
1804		panic("too many arguments")
1805	}
1806
1807	cmd := NewZSliceCmd(args...)
1808	_ = c(cmd)
1809	return cmd
1810}
1811
1812func (c cmdable) ZPopMin(key string, count ...int64) *ZSliceCmd {
1813	args := []interface{}{
1814		"zpopmin",
1815		key,
1816	}
1817
1818	switch len(count) {
1819	case 0:
1820		break
1821	case 1:
1822		args = append(args, count[0])
1823	default:
1824		panic("too many arguments")
1825	}
1826
1827	cmd := NewZSliceCmd(args...)
1828	_ = c(cmd)
1829	return cmd
1830}
1831
1832func (c cmdable) zRange(key string, start, stop int64, withScores bool) *StringSliceCmd {
1833	args := []interface{}{
1834		"zrange",
1835		key,
1836		start,
1837		stop,
1838	}
1839	if withScores {
1840		args = append(args, "withscores")
1841	}
1842	cmd := NewStringSliceCmd(args...)
1843	_ = c(cmd)
1844	return cmd
1845}
1846
1847func (c cmdable) ZRange(key string, start, stop int64) *StringSliceCmd {
1848	return c.zRange(key, start, stop, false)
1849}
1850
1851func (c cmdable) ZRangeWithScores(key string, start, stop int64) *ZSliceCmd {
1852	cmd := NewZSliceCmd("zrange", key, start, stop, "withscores")
1853	_ = c(cmd)
1854	return cmd
1855}
1856
1857type ZRangeBy struct {
1858	Min, Max      string
1859	Offset, Count int64
1860}
1861
1862func (c cmdable) zRangeBy(zcmd, key string, opt *ZRangeBy, withScores bool) *StringSliceCmd {
1863	args := []interface{}{zcmd, key, opt.Min, opt.Max}
1864	if withScores {
1865		args = append(args, "withscores")
1866	}
1867	if opt.Offset != 0 || opt.Count != 0 {
1868		args = append(
1869			args,
1870			"limit",
1871			opt.Offset,
1872			opt.Count,
1873		)
1874	}
1875	cmd := NewStringSliceCmd(args...)
1876	_ = c(cmd)
1877	return cmd
1878}
1879
1880func (c cmdable) ZRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd {
1881	return c.zRangeBy("zrangebyscore", key, opt, false)
1882}
1883
1884func (c cmdable) ZRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd {
1885	return c.zRangeBy("zrangebylex", key, opt, false)
1886}
1887
1888func (c cmdable) ZRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd {
1889	args := []interface{}{"zrangebyscore", key, opt.Min, opt.Max, "withscores"}
1890	if opt.Offset != 0 || opt.Count != 0 {
1891		args = append(
1892			args,
1893			"limit",
1894			opt.Offset,
1895			opt.Count,
1896		)
1897	}
1898	cmd := NewZSliceCmd(args...)
1899	_ = c(cmd)
1900	return cmd
1901}
1902
1903func (c cmdable) ZRank(key, member string) *IntCmd {
1904	cmd := NewIntCmd("zrank", key, member)
1905	_ = c(cmd)
1906	return cmd
1907}
1908
1909func (c cmdable) ZRem(key string, members ...interface{}) *IntCmd {
1910	args := make([]interface{}, 2, 2+len(members))
1911	args[0] = "zrem"
1912	args[1] = key
1913	args = appendArgs(args, members)
1914	cmd := NewIntCmd(args...)
1915	_ = c(cmd)
1916	return cmd
1917}
1918
1919func (c cmdable) ZRemRangeByRank(key string, start, stop int64) *IntCmd {
1920	cmd := NewIntCmd(
1921		"zremrangebyrank",
1922		key,
1923		start,
1924		stop,
1925	)
1926	_ = c(cmd)
1927	return cmd
1928}
1929
1930func (c cmdable) ZRemRangeByScore(key, min, max string) *IntCmd {
1931	cmd := NewIntCmd("zremrangebyscore", key, min, max)
1932	_ = c(cmd)
1933	return cmd
1934}
1935
1936func (c cmdable) ZRemRangeByLex(key, min, max string) *IntCmd {
1937	cmd := NewIntCmd("zremrangebylex", key, min, max)
1938	_ = c(cmd)
1939	return cmd
1940}
1941
1942func (c cmdable) ZRevRange(key string, start, stop int64) *StringSliceCmd {
1943	cmd := NewStringSliceCmd("zrevrange", key, start, stop)
1944	_ = c(cmd)
1945	return cmd
1946}
1947
1948func (c cmdable) ZRevRangeWithScores(key string, start, stop int64) *ZSliceCmd {
1949	cmd := NewZSliceCmd("zrevrange", key, start, stop, "withscores")
1950	_ = c(cmd)
1951	return cmd
1952}
1953
1954func (c cmdable) zRevRangeBy(zcmd, key string, opt *ZRangeBy) *StringSliceCmd {
1955	args := []interface{}{zcmd, key, opt.Max, opt.Min}
1956	if opt.Offset != 0 || opt.Count != 0 {
1957		args = append(
1958			args,
1959			"limit",
1960			opt.Offset,
1961			opt.Count,
1962		)
1963	}
1964	cmd := NewStringSliceCmd(args...)
1965	_ = c(cmd)
1966	return cmd
1967}
1968
1969func (c cmdable) ZRevRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd {
1970	return c.zRevRangeBy("zrevrangebyscore", key, opt)
1971}
1972
1973func (c cmdable) ZRevRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd {
1974	return c.zRevRangeBy("zrevrangebylex", key, opt)
1975}
1976
1977func (c cmdable) ZRevRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd {
1978	args := []interface{}{"zrevrangebyscore", key, opt.Max, opt.Min, "withscores"}
1979	if opt.Offset != 0 || opt.Count != 0 {
1980		args = append(
1981			args,
1982			"limit",
1983			opt.Offset,
1984			opt.Count,
1985		)
1986	}
1987	cmd := NewZSliceCmd(args...)
1988	_ = c(cmd)
1989	return cmd
1990}
1991
1992func (c cmdable) ZRevRank(key, member string) *IntCmd {
1993	cmd := NewIntCmd("zrevrank", key, member)
1994	_ = c(cmd)
1995	return cmd
1996}
1997
1998func (c cmdable) ZScore(key, member string) *FloatCmd {
1999	cmd := NewFloatCmd("zscore", key, member)
2000	_ = c(cmd)
2001	return cmd
2002}
2003
2004func (c cmdable) ZUnionStore(dest string, store *ZStore) *IntCmd {
2005	args := make([]interface{}, 3+len(store.Keys))
2006	args[0] = "zunionstore"
2007	args[1] = dest
2008	args[2] = len(store.Keys)
2009	for i, key := range store.Keys {
2010		args[3+i] = key
2011	}
2012	if len(store.Weights) > 0 {
2013		args = append(args, "weights")
2014		for _, weight := range store.Weights {
2015			args = append(args, weight)
2016		}
2017	}
2018	if store.Aggregate != "" {
2019		args = append(args, "aggregate", store.Aggregate)
2020	}
2021	cmd := NewIntCmd(args...)
2022	_ = c(cmd)
2023	return cmd
2024}
2025
2026//------------------------------------------------------------------------------
2027
2028func (c cmdable) PFAdd(key string, els ...interface{}) *IntCmd {
2029	args := make([]interface{}, 2, 2+len(els))
2030	args[0] = "pfadd"
2031	args[1] = key
2032	args = appendArgs(args, els)
2033	cmd := NewIntCmd(args...)
2034	_ = c(cmd)
2035	return cmd
2036}
2037
2038func (c cmdable) PFCount(keys ...string) *IntCmd {
2039	args := make([]interface{}, 1+len(keys))
2040	args[0] = "pfcount"
2041	for i, key := range keys {
2042		args[1+i] = key
2043	}
2044	cmd := NewIntCmd(args...)
2045	_ = c(cmd)
2046	return cmd
2047}
2048
2049func (c cmdable) PFMerge(dest string, keys ...string) *StatusCmd {
2050	args := make([]interface{}, 2+len(keys))
2051	args[0] = "pfmerge"
2052	args[1] = dest
2053	for i, key := range keys {
2054		args[2+i] = key
2055	}
2056	cmd := NewStatusCmd(args...)
2057	_ = c(cmd)
2058	return cmd
2059}
2060
2061//------------------------------------------------------------------------------
2062
2063func (c cmdable) BgRewriteAOF() *StatusCmd {
2064	cmd := NewStatusCmd("bgrewriteaof")
2065	_ = c(cmd)
2066	return cmd
2067}
2068
2069func (c cmdable) BgSave() *StatusCmd {
2070	cmd := NewStatusCmd("bgsave")
2071	_ = c(cmd)
2072	return cmd
2073}
2074
2075func (c cmdable) ClientKill(ipPort string) *StatusCmd {
2076	cmd := NewStatusCmd("client", "kill", ipPort)
2077	_ = c(cmd)
2078	return cmd
2079}
2080
2081// ClientKillByFilter is new style synx, while the ClientKill is old
2082// CLIENT KILL <option> [value] ... <option> [value]
2083func (c cmdable) ClientKillByFilter(keys ...string) *IntCmd {
2084	args := make([]interface{}, 2+len(keys))
2085	args[0] = "client"
2086	args[1] = "kill"
2087	for i, key := range keys {
2088		args[2+i] = key
2089	}
2090	cmd := NewIntCmd(args...)
2091	_ = c(cmd)
2092	return cmd
2093}
2094
2095func (c cmdable) ClientList() *StringCmd {
2096	cmd := NewStringCmd("client", "list")
2097	_ = c(cmd)
2098	return cmd
2099}
2100
2101func (c cmdable) ClientPause(dur time.Duration) *BoolCmd {
2102	cmd := NewBoolCmd("client", "pause", formatMs(dur))
2103	_ = c(cmd)
2104	return cmd
2105}
2106
2107func (c cmdable) ClientID() *IntCmd {
2108	cmd := NewIntCmd("client", "id")
2109	_ = c(cmd)
2110	return cmd
2111}
2112
2113func (c cmdable) ClientUnblock(id int64) *IntCmd {
2114	cmd := NewIntCmd("client", "unblock", id)
2115	_ = c(cmd)
2116	return cmd
2117}
2118
2119func (c cmdable) ClientUnblockWithError(id int64) *IntCmd {
2120	cmd := NewIntCmd("client", "unblock", id, "error")
2121	_ = c(cmd)
2122	return cmd
2123}
2124
2125// ClientSetName assigns a name to the connection.
2126func (c statefulCmdable) ClientSetName(name string) *BoolCmd {
2127	cmd := NewBoolCmd("client", "setname", name)
2128	_ = c(cmd)
2129	return cmd
2130}
2131
2132// ClientGetName returns the name of the connection.
2133func (c cmdable) ClientGetName() *StringCmd {
2134	cmd := NewStringCmd("client", "getname")
2135	_ = c(cmd)
2136	return cmd
2137}
2138
2139func (c cmdable) ConfigGet(parameter string) *SliceCmd {
2140	cmd := NewSliceCmd("config", "get", parameter)
2141	_ = c(cmd)
2142	return cmd
2143}
2144
2145func (c cmdable) ConfigResetStat() *StatusCmd {
2146	cmd := NewStatusCmd("config", "resetstat")
2147	_ = c(cmd)
2148	return cmd
2149}
2150
2151func (c cmdable) ConfigSet(parameter, value string) *StatusCmd {
2152	cmd := NewStatusCmd("config", "set", parameter, value)
2153	_ = c(cmd)
2154	return cmd
2155}
2156
2157func (c cmdable) ConfigRewrite() *StatusCmd {
2158	cmd := NewStatusCmd("config", "rewrite")
2159	_ = c(cmd)
2160	return cmd
2161}
2162
2163// Deperecated. Use DBSize instead.
2164func (c cmdable) DbSize() *IntCmd {
2165	return c.DBSize()
2166}
2167
2168func (c cmdable) DBSize() *IntCmd {
2169	cmd := NewIntCmd("dbsize")
2170	_ = c(cmd)
2171	return cmd
2172}
2173
2174func (c cmdable) FlushAll() *StatusCmd {
2175	cmd := NewStatusCmd("flushall")
2176	_ = c(cmd)
2177	return cmd
2178}
2179
2180func (c cmdable) FlushAllAsync() *StatusCmd {
2181	cmd := NewStatusCmd("flushall", "async")
2182	_ = c(cmd)
2183	return cmd
2184}
2185
2186func (c cmdable) FlushDB() *StatusCmd {
2187	cmd := NewStatusCmd("flushdb")
2188	_ = c(cmd)
2189	return cmd
2190}
2191
2192func (c cmdable) FlushDBAsync() *StatusCmd {
2193	cmd := NewStatusCmd("flushdb", "async")
2194	_ = c(cmd)
2195	return cmd
2196}
2197
2198func (c cmdable) Info(section ...string) *StringCmd {
2199	args := []interface{}{"info"}
2200	if len(section) > 0 {
2201		args = append(args, section[0])
2202	}
2203	cmd := NewStringCmd(args...)
2204	_ = c(cmd)
2205	return cmd
2206}
2207
2208func (c cmdable) LastSave() *IntCmd {
2209	cmd := NewIntCmd("lastsave")
2210	_ = c(cmd)
2211	return cmd
2212}
2213
2214func (c cmdable) Save() *StatusCmd {
2215	cmd := NewStatusCmd("save")
2216	_ = c(cmd)
2217	return cmd
2218}
2219
2220func (c cmdable) shutdown(modifier string) *StatusCmd {
2221	var args []interface{}
2222	if modifier == "" {
2223		args = []interface{}{"shutdown"}
2224	} else {
2225		args = []interface{}{"shutdown", modifier}
2226	}
2227	cmd := NewStatusCmd(args...)
2228	_ = c(cmd)
2229	if err := cmd.Err(); err != nil {
2230		if err == io.EOF {
2231			// Server quit as expected.
2232			cmd.err = nil
2233		}
2234	} else {
2235		// Server did not quit. String reply contains the reason.
2236		cmd.err = errors.New(cmd.val)
2237		cmd.val = ""
2238	}
2239	return cmd
2240}
2241
2242func (c cmdable) Shutdown() *StatusCmd {
2243	return c.shutdown("")
2244}
2245
2246func (c cmdable) ShutdownSave() *StatusCmd {
2247	return c.shutdown("save")
2248}
2249
2250func (c cmdable) ShutdownNoSave() *StatusCmd {
2251	return c.shutdown("nosave")
2252}
2253
2254func (c cmdable) SlaveOf(host, port string) *StatusCmd {
2255	cmd := NewStatusCmd("slaveof", host, port)
2256	_ = c(cmd)
2257	return cmd
2258}
2259
2260func (c cmdable) SlowLog() {
2261	panic("not implemented")
2262}
2263
2264func (c cmdable) Sync() {
2265	panic("not implemented")
2266}
2267
2268func (c cmdable) Time() *TimeCmd {
2269	cmd := NewTimeCmd("time")
2270	_ = c(cmd)
2271	return cmd
2272}
2273
2274//------------------------------------------------------------------------------
2275
2276func (c cmdable) Eval(script string, keys []string, args ...interface{}) *Cmd {
2277	cmdArgs := make([]interface{}, 3+len(keys), 3+len(keys)+len(args))
2278	cmdArgs[0] = "eval"
2279	cmdArgs[1] = script
2280	cmdArgs[2] = len(keys)
2281	for i, key := range keys {
2282		cmdArgs[3+i] = key
2283	}
2284	cmdArgs = appendArgs(cmdArgs, args)
2285	cmd := NewCmd(cmdArgs...)
2286	_ = c(cmd)
2287	return cmd
2288}
2289
2290func (c cmdable) EvalSha(sha1 string, keys []string, args ...interface{}) *Cmd {
2291	cmdArgs := make([]interface{}, 3+len(keys), 3+len(keys)+len(args))
2292	cmdArgs[0] = "evalsha"
2293	cmdArgs[1] = sha1
2294	cmdArgs[2] = len(keys)
2295	for i, key := range keys {
2296		cmdArgs[3+i] = key
2297	}
2298	cmdArgs = appendArgs(cmdArgs, args)
2299	cmd := NewCmd(cmdArgs...)
2300	_ = c(cmd)
2301	return cmd
2302}
2303
2304func (c cmdable) ScriptExists(hashes ...string) *BoolSliceCmd {
2305	args := make([]interface{}, 2+len(hashes))
2306	args[0] = "script"
2307	args[1] = "exists"
2308	for i, hash := range hashes {
2309		args[2+i] = hash
2310	}
2311	cmd := NewBoolSliceCmd(args...)
2312	_ = c(cmd)
2313	return cmd
2314}
2315
2316func (c cmdable) ScriptFlush() *StatusCmd {
2317	cmd := NewStatusCmd("script", "flush")
2318	_ = c(cmd)
2319	return cmd
2320}
2321
2322func (c cmdable) ScriptKill() *StatusCmd {
2323	cmd := NewStatusCmd("script", "kill")
2324	_ = c(cmd)
2325	return cmd
2326}
2327
2328func (c cmdable) ScriptLoad(script string) *StringCmd {
2329	cmd := NewStringCmd("script", "load", script)
2330	_ = c(cmd)
2331	return cmd
2332}
2333
2334//------------------------------------------------------------------------------
2335
2336func (c cmdable) DebugObject(key string) *StringCmd {
2337	cmd := NewStringCmd("debug", "object", key)
2338	_ = c(cmd)
2339	return cmd
2340}
2341
2342//------------------------------------------------------------------------------
2343
2344// Publish posts the message to the channel.
2345func (c cmdable) Publish(channel string, message interface{}) *IntCmd {
2346	cmd := NewIntCmd("publish", channel, message)
2347	_ = c(cmd)
2348	return cmd
2349}
2350
2351func (c cmdable) PubSubChannels(pattern string) *StringSliceCmd {
2352	args := []interface{}{"pubsub", "channels"}
2353	if pattern != "*" {
2354		args = append(args, pattern)
2355	}
2356	cmd := NewStringSliceCmd(args...)
2357	_ = c(cmd)
2358	return cmd
2359}
2360
2361func (c cmdable) PubSubNumSub(channels ...string) *StringIntMapCmd {
2362	args := make([]interface{}, 2+len(channels))
2363	args[0] = "pubsub"
2364	args[1] = "numsub"
2365	for i, channel := range channels {
2366		args[2+i] = channel
2367	}
2368	cmd := NewStringIntMapCmd(args...)
2369	_ = c(cmd)
2370	return cmd
2371}
2372
2373func (c cmdable) PubSubNumPat() *IntCmd {
2374	cmd := NewIntCmd("pubsub", "numpat")
2375	_ = c(cmd)
2376	return cmd
2377}
2378
2379//------------------------------------------------------------------------------
2380
2381func (c cmdable) ClusterSlots() *ClusterSlotsCmd {
2382	cmd := NewClusterSlotsCmd("cluster", "slots")
2383	_ = c(cmd)
2384	return cmd
2385}
2386
2387func (c cmdable) ClusterNodes() *StringCmd {
2388	cmd := NewStringCmd("cluster", "nodes")
2389	_ = c(cmd)
2390	return cmd
2391}
2392
2393func (c cmdable) ClusterMeet(host, port string) *StatusCmd {
2394	cmd := NewStatusCmd("cluster", "meet", host, port)
2395	_ = c(cmd)
2396	return cmd
2397}
2398
2399func (c cmdable) ClusterForget(nodeID string) *StatusCmd {
2400	cmd := NewStatusCmd("cluster", "forget", nodeID)
2401	_ = c(cmd)
2402	return cmd
2403}
2404
2405func (c cmdable) ClusterReplicate(nodeID string) *StatusCmd {
2406	cmd := NewStatusCmd("cluster", "replicate", nodeID)
2407	_ = c(cmd)
2408	return cmd
2409}
2410
2411func (c cmdable) ClusterResetSoft() *StatusCmd {
2412	cmd := NewStatusCmd("cluster", "reset", "soft")
2413	_ = c(cmd)
2414	return cmd
2415}
2416
2417func (c cmdable) ClusterResetHard() *StatusCmd {
2418	cmd := NewStatusCmd("cluster", "reset", "hard")
2419	_ = c(cmd)
2420	return cmd
2421}
2422
2423func (c cmdable) ClusterInfo() *StringCmd {
2424	cmd := NewStringCmd("cluster", "info")
2425	_ = c(cmd)
2426	return cmd
2427}
2428
2429func (c cmdable) ClusterKeySlot(key string) *IntCmd {
2430	cmd := NewIntCmd("cluster", "keyslot", key)
2431	_ = c(cmd)
2432	return cmd
2433}
2434
2435func (c cmdable) ClusterGetKeysInSlot(slot int, count int) *StringSliceCmd {
2436	cmd := NewStringSliceCmd("cluster", "getkeysinslot", slot, count)
2437	_ = c(cmd)
2438	return cmd
2439}
2440
2441func (c cmdable) ClusterCountFailureReports(nodeID string) *IntCmd {
2442	cmd := NewIntCmd("cluster", "count-failure-reports", nodeID)
2443	_ = c(cmd)
2444	return cmd
2445}
2446
2447func (c cmdable) ClusterCountKeysInSlot(slot int) *IntCmd {
2448	cmd := NewIntCmd("cluster", "countkeysinslot", slot)
2449	_ = c(cmd)
2450	return cmd
2451}
2452
2453func (c cmdable) ClusterDelSlots(slots ...int) *StatusCmd {
2454	args := make([]interface{}, 2+len(slots))
2455	args[0] = "cluster"
2456	args[1] = "delslots"
2457	for i, slot := range slots {
2458		args[2+i] = slot
2459	}
2460	cmd := NewStatusCmd(args...)
2461	_ = c(cmd)
2462	return cmd
2463}
2464
2465func (c cmdable) ClusterDelSlotsRange(min, max int) *StatusCmd {
2466	size := max - min + 1
2467	slots := make([]int, size)
2468	for i := 0; i < size; i++ {
2469		slots[i] = min + i
2470	}
2471	return c.ClusterDelSlots(slots...)
2472}
2473
2474func (c cmdable) ClusterSaveConfig() *StatusCmd {
2475	cmd := NewStatusCmd("cluster", "saveconfig")
2476	_ = c(cmd)
2477	return cmd
2478}
2479
2480func (c cmdable) ClusterSlaves(nodeID string) *StringSliceCmd {
2481	cmd := NewStringSliceCmd("cluster", "slaves", nodeID)
2482	_ = c(cmd)
2483	return cmd
2484}
2485
2486func (c cmdable) ReadOnly() *StatusCmd {
2487	cmd := NewStatusCmd("readonly")
2488	_ = c(cmd)
2489	return cmd
2490}
2491
2492func (c cmdable) ReadWrite() *StatusCmd {
2493	cmd := NewStatusCmd("readwrite")
2494	_ = c(cmd)
2495	return cmd
2496}
2497
2498func (c cmdable) ClusterFailover() *StatusCmd {
2499	cmd := NewStatusCmd("cluster", "failover")
2500	_ = c(cmd)
2501	return cmd
2502}
2503
2504func (c cmdable) ClusterAddSlots(slots ...int) *StatusCmd {
2505	args := make([]interface{}, 2+len(slots))
2506	args[0] = "cluster"
2507	args[1] = "addslots"
2508	for i, num := range slots {
2509		args[2+i] = num
2510	}
2511	cmd := NewStatusCmd(args...)
2512	_ = c(cmd)
2513	return cmd
2514}
2515
2516func (c cmdable) ClusterAddSlotsRange(min, max int) *StatusCmd {
2517	size := max - min + 1
2518	slots := make([]int, size)
2519	for i := 0; i < size; i++ {
2520		slots[i] = min + i
2521	}
2522	return c.ClusterAddSlots(slots...)
2523}
2524
2525//------------------------------------------------------------------------------
2526
2527func (c cmdable) GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd {
2528	args := make([]interface{}, 2+3*len(geoLocation))
2529	args[0] = "geoadd"
2530	args[1] = key
2531	for i, eachLoc := range geoLocation {
2532		args[2+3*i] = eachLoc.Longitude
2533		args[2+3*i+1] = eachLoc.Latitude
2534		args[2+3*i+2] = eachLoc.Name
2535	}
2536	cmd := NewIntCmd(args...)
2537	_ = c(cmd)
2538	return cmd
2539}
2540
2541// GeoRadius is a read-only GEORADIUS_RO command.
2542func (c cmdable) GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd {
2543	cmd := NewGeoLocationCmd(query, "georadius_ro", key, longitude, latitude)
2544	if query.Store != "" || query.StoreDist != "" {
2545		cmd.SetErr(errors.New("GeoRadius does not support Store or StoreDist"))
2546		return cmd
2547	}
2548	_ = c(cmd)
2549	return cmd
2550}
2551
2552// GeoRadiusStore is a writing GEORADIUS command.
2553func (c cmdable) GeoRadiusStore(key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd {
2554	args := geoLocationArgs(query, "georadius", key, longitude, latitude)
2555	cmd := NewIntCmd(args...)
2556	if query.Store == "" && query.StoreDist == "" {
2557		cmd.SetErr(errors.New("GeoRadiusStore requires Store or StoreDist"))
2558		return cmd
2559	}
2560	_ = c(cmd)
2561	return cmd
2562}
2563
2564// GeoRadius is a read-only GEORADIUSBYMEMBER_RO command.
2565func (c cmdable) GeoRadiusByMember(key, member string, query *GeoRadiusQuery) *GeoLocationCmd {
2566	cmd := NewGeoLocationCmd(query, "georadiusbymember_ro", key, member)
2567	if query.Store != "" || query.StoreDist != "" {
2568		cmd.SetErr(errors.New("GeoRadiusByMember does not support Store or StoreDist"))
2569		return cmd
2570	}
2571	_ = c(cmd)
2572	return cmd
2573}
2574
2575// GeoRadiusByMemberStore is a writing GEORADIUSBYMEMBER command.
2576func (c cmdable) GeoRadiusByMemberStore(key, member string, query *GeoRadiusQuery) *IntCmd {
2577	args := geoLocationArgs(query, "georadiusbymember", key, member)
2578	cmd := NewIntCmd(args...)
2579	if query.Store == "" && query.StoreDist == "" {
2580		cmd.SetErr(errors.New("GeoRadiusByMemberStore requires Store or StoreDist"))
2581		return cmd
2582	}
2583	_ = c(cmd)
2584	return cmd
2585}
2586
2587func (c cmdable) GeoDist(key string, member1, member2, unit string) *FloatCmd {
2588	if unit == "" {
2589		unit = "km"
2590	}
2591	cmd := NewFloatCmd("geodist", key, member1, member2, unit)
2592	_ = c(cmd)
2593	return cmd
2594}
2595
2596func (c cmdable) GeoHash(key string, members ...string) *StringSliceCmd {
2597	args := make([]interface{}, 2+len(members))
2598	args[0] = "geohash"
2599	args[1] = key
2600	for i, member := range members {
2601		args[2+i] = member
2602	}
2603	cmd := NewStringSliceCmd(args...)
2604	_ = c(cmd)
2605	return cmd
2606}
2607
2608func (c cmdable) GeoPos(key string, members ...string) *GeoPosCmd {
2609	args := make([]interface{}, 2+len(members))
2610	args[0] = "geopos"
2611	args[1] = key
2612	for i, member := range members {
2613		args[2+i] = member
2614	}
2615	cmd := NewGeoPosCmd(args...)
2616	_ = c(cmd)
2617	return cmd
2618}
2619
2620//------------------------------------------------------------------------------
2621
2622func (c cmdable) MemoryUsage(key string, samples ...int) *IntCmd {
2623	args := []interface{}{"memory", "usage", key}
2624	if len(samples) > 0 {
2625		if len(samples) != 1 {
2626			panic("MemoryUsage expects single sample count")
2627		}
2628		args = append(args, "SAMPLES", samples[0])
2629	}
2630	cmd := NewIntCmd(args...)
2631	_ = c(cmd)
2632	return cmd
2633}
2634