Skip to main content

redis/commands/
mod.rs

1#![allow(unused_parens)]
2
3use crate::cmd::{Cmd, Iter, cmd};
4use crate::connection::{Connection, ConnectionLike, Msg, RedisConnectionInfo};
5use crate::pipeline::Pipeline;
6use crate::types::{
7    ExistenceCheck, ExpireOption, Expiry, FieldExistenceCheck, FromRedisValue, IntegerReplyOrNoOp,
8    NumericBehavior, RedisResult, RedisWrite, SetExpiry, ToRedisArgs, ToSingleRedisArg,
9    ValueComparison,
10};
11
12#[cfg(feature = "vector-sets")]
13use crate::types::Value;
14
15#[cfg(feature = "vector-sets")]
16use serde::ser::Serialize;
17use std::collections::HashSet;
18
19#[macro_use]
20mod macros;
21
22#[cfg(feature = "json")]
23#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
24pub mod json;
25
26#[cfg(feature = "json")]
27pub use json::JsonCommands;
28
29#[cfg(all(feature = "json", feature = "aio"))]
30pub use json::JsonAsyncCommands;
31
32#[cfg(feature = "cluster")]
33use crate::cluster_handling::sync_connection::ClusterPipeline;
34
35#[cfg(feature = "geospatial")]
36pub mod geo;
37
38#[cfg(feature = "streams")]
39pub mod streams;
40
41#[cfg(feature = "acl")]
42pub mod acl;
43
44#[cfg(feature = "vector-sets")]
45#[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
46pub mod vector_sets;
47
48#[cfg(feature = "search_unfinished")]
49#[cfg_attr(docsrs, doc(cfg(feature = "search_unfinished")))]
50pub mod search;
51
52pub mod hotkeys;
53
54#[cfg(any(feature = "cluster", feature = "cache-aio"))]
55enum Properties {
56    ReadOnlyCacheable,
57    ReadOnly,
58    Neither,
59}
60
61#[cfg(any(feature = "cluster", feature = "cache-aio"))]
62fn command_properties(cmd: &[u8]) -> Properties {
63    match cmd {
64        // ReadonlyCacheable: Commands that operate on concrete keys and return cacheable values
65        b"BITCOUNT" | b"BITFIELD_RO" | b"BITPOS" | b"DUMP" | b"EXISTS" | b"GEODIST"
66        | b"GEOHASH" | b"GEOPOS" | b"GET" | b"GETBIT" | b"GETRANGE" | b"HEXISTS" | b"HGET"
67        | b"HGETALL" | b"HKEYS" | b"HLEN" | b"HMGET" | b"HSTRLEN" | b"HVALS" | b"JSON.ARRINDEX"
68        | b"JSON.ARRLEN" | b"JSON.GET" | b"JSON.OBJLEN" | b"JSON.OBJKEYS" | b"JSON.MGET"
69        | b"JSON.RESP" | b"JSON.STRLEN" | b"JSON.TYPE" | b"LCS" | b"LINDEX" | b"LLEN" | b"LPOS"
70        | b"LRANGE" | b"MGET" | b"SCARD" | b"SDIFF" | b"SINTER" | b"SINTERCARD" | b"SISMEMBER"
71        | b"SMEMBERS" | b"SMISMEMBER" | b"STRLEN" | b"SUBSTR" | b"SUNION" | b"TYPE" | b"ZCARD"
72        | b"ZCOUNT" | b"ZDIFF" | b"ZINTER" | b"ZINTERCARD" | b"ZLEXCOUNT" | b"ZMSCORE"
73        | b"ZRANGE" | b"ZRANGEBYLEX" | b"ZRANGEBYSCORE" | b"ZRANK" | b"ZREVRANGE"
74        | b"ZREVRANGEBYLEX" | b"ZREVRANGEBYSCORE" | b"ZREVRANK" | b"ZSCORE" | b"ZUNION" => {
75            Properties::ReadOnlyCacheable
76        }
77
78        b"ACL CAT"
79        | b"ACL DELUSER"
80        | b"ACL DRYRUN"
81        | b"ACL GENPASS"
82        | b"ACL GETUSER"
83        | b"ACL HELP"
84        | b"ACL LIST"
85        | b"ACL LOAD"
86        | b"ACL LOG"
87        | b"ACL SAVE"
88        | b"ACL SETUSER"
89        | b"ACL USERS"
90        | b"ACL WHOAMI"
91        | b"AUTH"
92        | b"BF.CARD"
93        | b"BF.EXISTS"
94        | b"BF.INFO"
95        | b"BF.MEXISTS"
96        | b"BF.SCANDUMP"
97        | b"BGREWRITEAOF"
98        | b"BGSAVE"
99        | b"PFCOUNT"
100        | b"CLIENT ID"
101        | b"CLIENT CACHING"
102        | b"CLIENT CAPA"
103        | b"CLIENT GETNAME"
104        | b"CLIENT GETREDIR"
105        | b"CLIENT HELP"
106        | b"CLIENT INFO"
107        | b"CLIENT KILL"
108        | b"CLIENT LIST"
109        | b"CLIENT NO-EVICT"
110        | b"CLIENT NO-TOUCH"
111        | b"CLIENT PAUSE"
112        | b"CLIENT REPLY"
113        | b"CLIENT SETINFO"
114        | b"CLIENT SETNAME"
115        | b"CLIENT TRACKING"
116        | b"CLIENT TRACKINGINFO"
117        | b"CLIENT UNBLOCK"
118        | b"CLIENT UNPAUSE"
119        | b"CLUSTER COUNT-FAILURE-REPORTS"
120        | b"CLUSTER COUNTKEYSINSLOT"
121        | b"CLUSTER FAILOVER"
122        | b"CLUSTER GETKEYSINSLOT"
123        | b"CLUSTER HELP"
124        | b"CLUSTER INFO"
125        | b"CLUSTER KEYSLOT"
126        | b"CLUSTER LINKS"
127        | b"CLUSTER MYID"
128        | b"CLUSTER MYSHARDID"
129        | b"CLUSTER NODES"
130        | b"CLUSTER REPLICATE"
131        | b"CLUSTER SAVECONFIG"
132        | b"CLUSTER SHARDS"
133        | b"CLUSTER SLOTS"
134        | b"COMMAND COUNT"
135        | b"COMMAND DOCS"
136        | b"COMMAND GETKEYS"
137        | b"COMMAND GETKEYSANDFLAGS"
138        | b"COMMAND HELP"
139        | b"COMMAND INFO"
140        | b"COMMAND LIST"
141        | b"CONFIG GET"
142        | b"CONFIG HELP"
143        | b"CONFIG RESETSTAT"
144        | b"CONFIG REWRITE"
145        | b"CONFIG SET"
146        | b"DBSIZE"
147        | b"ECHO"
148        | b"EVAL_RO"
149        | b"EVALSHA_RO"
150        | b"EXPIRETIME"
151        | b"FCALL_RO"
152        | b"FT.AGGREGATE"
153        | b"FT.EXPLAIN"
154        | b"FT.EXPLAINCLI"
155        | b"FT.INFO"
156        | b"FT.PROFILE"
157        | b"FT.SEARCH"
158        | b"FT._ALIASLIST"
159        | b"FT._LIST"
160        | b"FUNCTION DUMP"
161        | b"FUNCTION HELP"
162        | b"FUNCTION KILL"
163        | b"FUNCTION LIST"
164        | b"FUNCTION STATS"
165        | b"GEORADIUSBYMEMBER_RO"
166        | b"GEORADIUS_RO"
167        | b"GEOSEARCH"
168        | b"HELLO"
169        | b"HRANDFIELD"
170        | b"HSCAN"
171        | b"INFO"
172        | b"JSON.DEBUG"
173        | b"KEYS"
174        | b"LASTSAVE"
175        | b"LATENCY DOCTOR"
176        | b"LATENCY GRAPH"
177        | b"LATENCY HELP"
178        | b"LATENCY HISTOGRAM"
179        | b"LATENCY HISTORY"
180        | b"LATENCY LATEST"
181        | b"LATENCY RESET"
182        | b"LOLWUT"
183        | b"MEMORY DOCTOR"
184        | b"MEMORY HELP"
185        | b"MEMORY MALLOC-STATS"
186        | b"MEMORY PURGE"
187        | b"MEMORY STATS"
188        | b"MEMORY USAGE"
189        | b"MODULE HELP"
190        | b"MODULE LIST"
191        | b"MODULE LOAD"
192        | b"MODULE LOADEX"
193        | b"MODULE UNLOAD"
194        | b"OBJECT ENCODING"
195        | b"OBJECT FREQ"
196        | b"OBJECT HELP"
197        | b"OBJECT IDLETIME"
198        | b"OBJECT REFCOUNT"
199        | b"PEXPIRETIME"
200        | b"PING"
201        | b"PTTL"
202        | b"PUBLISH"
203        | b"PUBSUB CHANNELS"
204        | b"PUBSUB HELP"
205        | b"PUBSUB NUMPAT"
206        | b"PUBSUB NUMSUB"
207        | b"PUBSUB SHARDCHANNELS"
208        | b"PUBSUB SHARDNUMSUB"
209        | b"RANDOMKEY"
210        | b"REPLICAOF"
211        | b"RESET"
212        | b"ROLE"
213        | b"SAVE"
214        | b"SCAN"
215        | b"SCRIPT DEBUG"
216        | b"SCRIPT EXISTS"
217        | b"SCRIPT FLUSH"
218        | b"SCRIPT KILL"
219        | b"SCRIPT LOAD"
220        | b"SCRIPT SHOW"
221        | b"SELECT"
222        | b"SHUTDOWN"
223        | b"SLOWLOG GET"
224        | b"SLOWLOG HELP"
225        | b"SLOWLOG LEN"
226        | b"SLOWLOG RESET"
227        | b"SORT_RO"
228        | b"SPUBLISH"
229        | b"SRANDMEMBER"
230        | b"SSCAN"
231        | b"SSUBSCRIBE"
232        | b"SUBSCRIBE"
233        | b"SUNSUBSCRIBE"
234        | b"TIME"
235        | b"TOUCH"
236        | b"TTL"
237        | b"UNSUBSCRIBE"
238        | b"XINFO CONSUMERS"
239        | b"XINFO GROUPS"
240        | b"XINFO STREAM"
241        | b"XLEN"
242        | b"XPENDING"
243        | b"XRANGE"
244        | b"XREAD"
245        | b"XREVRANGE"
246        | b"ZRANDMEMBER"
247        | b"ZSCAN" => Properties::ReadOnly,
248        _ => Properties::Neither,
249    }
250}
251
252#[cfg(feature = "cluster")]
253pub(crate) fn is_readonly_cmd(cmd: &[u8]) -> bool {
254    matches!(
255        command_properties(cmd),
256        Properties::ReadOnly | Properties::ReadOnlyCacheable
257    )
258}
259
260#[cfg(feature = "cache-aio")]
261pub(crate) fn is_cachable_cmd(cmd: &[u8]) -> bool {
262    matches!(command_properties(cmd), Properties::ReadOnlyCacheable)
263}
264
265// Note - Brackets are needed around return types for purposes of macro branching.
266implement_commands! {
267    'a
268    // most common operations
269
270    /// Get the value of a key.  If key is a vec this becomes an `MGET` (if using `TypedCommands`, you should specifically use `mget` to get the correct return type.
271    /// [Redis Docs](https://redis.io/commands/get/)
272    fn get<K: ToSingleRedisArg >(key: K) -> (Option<String>) {
273        cmd("GET").arg(key).take()
274    }
275
276    /// Get values of keys
277    /// [Redis Docs](https://redis.io/commands/MGET)
278    fn mget<K: ToRedisArgs>(key: K) -> (Vec<Option<String>>) {
279        cmd("MGET").arg(key).take()
280    }
281
282    /// Gets all keys matching pattern
283    /// [Redis Docs](https://redis.io/commands/KEYS)
284    fn keys<K: ToSingleRedisArg>(key: K) -> (Vec<String>) {
285        cmd("KEYS").arg(key).take()
286    }
287
288    /// Set the string value of a key.
289    /// [Redis Docs](https://redis.io/commands/SET)
290    fn set<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, value: V) -> (()) {
291        cmd("SET").arg(key).arg(value).take()
292    }
293
294    /// Set the string value of a key with options.
295    /// [Redis Docs](https://redis.io/commands/SET)
296    fn set_options<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, value: V, options: SetOptions) -> (Option<String>) {
297        cmd("SET").arg(key).arg(value).arg(options).take()
298    }
299
300    /// Sets multiple keys to their values.
301    /// [Redis Docs](https://redis.io/commands/MSET)
302    fn mset<K: ToRedisArgs, V: ToRedisArgs>(items: &'a [(K, V)]) -> (()) {
303        cmd("MSET").arg(items).take()
304    }
305
306    /// Set the value and expiration of a key.
307    /// [Redis Docs](https://redis.io/commands/SETEX)
308    fn set_ex<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, value: V, seconds: u64) -> (()) {
309        cmd("SETEX").arg(key).arg(seconds).arg(value).take()
310    }
311
312    /// Set the value and expiration in milliseconds of a key.
313    /// [Redis Docs](https://redis.io/commands/PSETEX)
314    fn pset_ex<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, value: V, milliseconds: u64) -> (()) {
315        cmd("PSETEX").arg(key).arg(milliseconds).arg(value).take()
316    }
317
318    /// Set the value of a key, only if the key does not exist
319    /// [Redis Docs](https://redis.io/commands/SETNX)
320    fn set_nx<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, value: V) -> (bool) {
321        cmd("SETNX").arg(key).arg(value).take()
322    }
323
324    /// Sets multiple keys to their values failing if at least one already exists.
325    /// [Redis Docs](https://redis.io/commands/MSETNX)
326    fn mset_nx<K: ToRedisArgs, V: ToRedisArgs>(items: &'a [(K, V)]) -> (bool) {
327        cmd("MSETNX").arg(items).take()
328    }
329
330    /// Sets the given keys to their respective values.
331    /// This command is an extension of the MSETNX that adds expiration and XX options.
332    /// [Redis Docs](https://redis.io/commands/MSETEX)
333    fn mset_ex<K: ToRedisArgs, V: ToRedisArgs>(items: &'a [(K, V)], options: MSetOptions) -> (bool) {
334        cmd("MSETEX").arg(items.len()).arg(items).arg(options).take()
335    }
336
337    /// Set the string value of a key and return its old value.
338    /// [Redis Docs](https://redis.io/commands/GETSET)
339    fn getset<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, value: V) -> (Option<String>) {
340        cmd("GETSET").arg(key).arg(value).take()
341    }
342
343    /// Get a range of bytes/substring from the value of a key. Negative values provide an offset from the end of the value.
344    /// Redis returns an empty string if the key doesn't exist, not Nil
345    /// [Redis Docs](https://redis.io/commands/GETRANGE)
346    fn getrange<K: ToSingleRedisArg>(key: K, from: isize, to: isize) -> (String) {
347        cmd("GETRANGE").arg(key).arg(from).arg(to).take()
348    }
349
350    /// Overwrite the part of the value stored in key at the specified offset.
351    /// [Redis Docs](https://redis.io/commands/SETRANGE)
352    fn setrange<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, offset: isize, value: V) -> (usize) {
353        cmd("SETRANGE").arg(key).arg(offset).arg(value).take()
354    }
355
356    /// Delete one or more keys.
357    /// Returns the number of keys deleted.
358    /// [Redis Docs](https://redis.io/commands/DEL)
359    fn del<K: ToRedisArgs>(key: K) -> (usize) {
360        cmd("DEL").arg(key).take()
361    }
362
363    /// Conditionally removes the specified key. A key is ignored if it does not exist.
364    /// IFEQ `match-value` - Delete the key only if its value is equal to `match-value`
365    /// IFNE `match-value` - Delete the key only if its value is not equal to `match-value`
366    /// IFDEQ `match-digest` - Delete the key only if the digest of its value is equal to `match-digest`
367    /// IFDNE `match-digest` - Delete the key only if the digest of its value is not equal to `match-digest`
368    /// [Redis Docs](https://redis.io/commands/DELEX)
369    fn del_ex<K: ToSingleRedisArg>(key: K, value_comparison: ValueComparison) -> (usize) {
370        cmd("DELEX").arg(key).arg(value_comparison).take()
371    }
372
373    /// Get the hex signature of the value stored in the specified key.
374    /// For the digest, Redis will use [XXH3](https://xxhash.com)
375    /// [Redis Docs](https://redis.io/commands/DIGEST)
376    fn digest<K: ToSingleRedisArg>(key: K) -> (Option<String>) {
377        cmd("DIGEST").arg(key).take()
378    }
379
380    /// Determine if a key exists.
381    /// [Redis Docs](https://redis.io/commands/EXISTS)
382    fn exists<K: ToRedisArgs>(key: K) -> (bool) {
383        cmd("EXISTS").arg(key).take()
384    }
385
386    /// Determine the type of key.
387    /// [Redis Docs](https://redis.io/commands/TYPE)
388    fn key_type<K: ToSingleRedisArg>(key: K) -> (crate::types::ValueType) {
389        cmd("TYPE").arg(key).take()
390    }
391
392    /// Set a key's time to live in seconds.
393    /// Returns whether expiration was set.
394    /// [Redis Docs](https://redis.io/commands/EXPIRE)
395    fn expire<K: ToSingleRedisArg>(key: K, seconds: i64) -> (bool) {
396        cmd("EXPIRE").arg(key).arg(seconds).take()
397    }
398
399    /// Set the expiration for a key as a UNIX timestamp.
400    /// Returns whether expiration was set.
401    /// [Redis Docs](https://redis.io/commands/EXPIREAT)
402    fn expire_at<K: ToSingleRedisArg>(key: K, ts: i64) -> (bool) {
403        cmd("EXPIREAT").arg(key).arg(ts).take()
404    }
405
406    /// Set a key's time to live in milliseconds.
407    /// Returns whether expiration was set.
408    /// [Redis Docs](https://redis.io/commands/PEXPIRE)
409    fn pexpire<K: ToSingleRedisArg>(key: K, ms: i64) -> (bool) {
410        cmd("PEXPIRE").arg(key).arg(ms).take()
411    }
412
413    /// Set the expiration for a key as a UNIX timestamp in milliseconds.
414    /// Returns whether expiration was set.
415    /// [Redis Docs](https://redis.io/commands/PEXPIREAT)
416    fn pexpire_at<K: ToSingleRedisArg>(key: K, ts: i64) -> (bool) {
417        cmd("PEXPIREAT").arg(key).arg(ts).take()
418    }
419
420    /// Get the absolute Unix expiration timestamp in seconds.
421    /// Returns `ExistsButNotRelevant` if key exists but has no expiration time.
422    /// [Redis Docs](https://redis.io/commands/EXPIRETIME)
423    fn expire_time<K: ToSingleRedisArg>(key: K) -> (IntegerReplyOrNoOp) {
424        cmd("EXPIRETIME").arg(key).take()
425    }
426
427    /// Get the absolute Unix expiration timestamp in milliseconds.
428    /// Returns `ExistsButNotRelevant` if key exists but has no expiration time.
429    /// [Redis Docs](https://redis.io/commands/PEXPIRETIME)
430    fn pexpire_time<K: ToSingleRedisArg>(key: K) -> (IntegerReplyOrNoOp) {
431        cmd("PEXPIRETIME").arg(key).take()
432    }
433
434    /// Remove the expiration from a key.
435    /// Returns whether a timeout was removed.
436    /// [Redis Docs](https://redis.io/commands/PERSIST)
437    fn persist<K: ToSingleRedisArg>(key: K) -> (bool) {
438        cmd("PERSIST").arg(key).take()
439    }
440
441    /// Get the time to live for a key in seconds.
442    /// Returns `ExistsButNotRelevant` if key exists but has no expiration time.
443    /// [Redis Docs](https://redis.io/commands/TTL)
444    fn ttl<K: ToSingleRedisArg>(key: K) -> (IntegerReplyOrNoOp) {
445        cmd("TTL").arg(key).take()
446    }
447
448    /// Get the time to live for a key in milliseconds.
449    /// Returns `ExistsButNotRelevant` if key exists but has no expiration time.
450    /// [Redis Docs](https://redis.io/commands/PTTL)
451    fn pttl<K: ToSingleRedisArg>(key: K) -> (IntegerReplyOrNoOp) {
452        cmd("PTTL").arg(key).take()
453    }
454
455    /// Get the value of a key and set expiration
456    /// [Redis Docs](https://redis.io/commands/GETEX)
457    fn get_ex<K: ToSingleRedisArg>(key: K, expire_at: Expiry) -> (Option<String>) {
458        cmd("GETEX").arg(key).arg(expire_at).take()
459    }
460
461    /// Get the value of a key and delete it
462    /// [Redis Docs](https://redis.io/commands/GETDEL)
463    fn get_del<K: ToSingleRedisArg>(key: K) -> (Option<String>) {
464        cmd("GETDEL").arg(key).take()
465    }
466
467    /// Copy the value from one key to another, returning whether the copy was successful.
468    /// [Redis Docs](https://redis.io/commands/COPY)
469    fn copy<KSrc: ToSingleRedisArg, KDst: ToSingleRedisArg, Db: ToString>(
470        source: KSrc,
471        destination: KDst,
472        options: CopyOptions<Db>
473    ) -> (bool) {
474        cmd("COPY").arg(source).arg(destination).arg(options).take()
475    }
476
477    /// Rename a key.
478    /// Errors if key does not exist.
479    /// [Redis Docs](https://redis.io/commands/RENAME)
480    fn rename<K: ToSingleRedisArg, N: ToSingleRedisArg>(key: K, new_key: N) -> (()) {
481        cmd("RENAME").arg(key).arg(new_key).take()
482    }
483
484    /// Rename a key, only if the new key does not exist.
485    /// Errors if key does not exist.
486    /// Returns whether the key was renamed, or false if the new key already exists.
487    /// [Redis Docs](https://redis.io/commands/RENAMENX)
488    fn rename_nx<K: ToSingleRedisArg, N: ToSingleRedisArg>(key: K, new_key: N) -> (bool) {
489        cmd("RENAMENX").arg(key).arg(new_key).take()
490    }
491
492    /// Unlink one or more keys. This is a non-blocking version of `DEL`.
493    /// Returns number of keys unlinked.
494    /// [Redis Docs](https://redis.io/commands/UNLINK)
495    fn unlink<K: ToRedisArgs>(key: K) -> (usize) {
496        cmd("UNLINK").arg(key).take()
497    }
498
499    // common string operations
500
501    /// Append a value to a key.
502    /// Returns length of string after operation.
503    /// [Redis Docs](https://redis.io/commands/APPEND)
504    fn append<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, value: V) -> (usize) {
505        cmd("APPEND").arg(key).arg(value).take()
506    }
507
508    /// Increment the numeric value of a key by the given amount.  This
509    /// issues a `INCRBY` or `INCRBYFLOAT` depending on the type.
510    /// If the key does not exist, it is set to 0 before performing the operation.
511    fn incr<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, delta: V) -> (isize) {
512        cmd(if delta.describe_numeric_behavior() == NumericBehavior::NumberIsFloat {
513            "INCRBYFLOAT"
514        } else {
515            "INCRBY"
516        }).arg(key).arg(delta).take()
517    }
518
519    /// Decrement the numeric value of a key by the given amount.
520    /// If the key does not exist, it is set to 0 before performing the operation.
521    /// [Redis Docs](https://redis.io/commands/DECRBY)
522    fn decr<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, delta: V) -> (isize) {
523        cmd("DECRBY").arg(key).arg(delta).take()
524    }
525
526    /// Sets or clears the bit at offset in the string value stored at key.
527    /// Returns the original bit value stored at offset.
528    /// [Redis Docs](https://redis.io/commands/SETBIT)
529    fn setbit<K: ToSingleRedisArg>(key: K, offset: usize, value: bool) -> (bool) {
530        cmd("SETBIT").arg(key).arg(offset).arg(i32::from(value)).take()
531    }
532
533    /// Returns the bit value at offset in the string value stored at key.
534    /// [Redis Docs](https://redis.io/commands/GETBIT)
535    fn getbit<K: ToSingleRedisArg>(key: K, offset: usize) -> (bool) {
536        cmd("GETBIT").arg(key).arg(offset).take()
537    }
538
539    /// Count set bits in a string.
540    /// Returns 0 if key does not exist.
541    /// [Redis Docs](https://redis.io/commands/BITCOUNT)
542    fn bitcount<K: ToSingleRedisArg>(key: K) -> (usize) {
543        cmd("BITCOUNT").arg(key).take()
544    }
545
546    /// Count set bits in a string in a range.
547    /// Returns 0 if key does not exist.
548    /// [Redis Docs](https://redis.io/commands/BITCOUNT)
549    fn bitcount_range<K: ToSingleRedisArg>(key: K, start: usize, end: usize) -> (usize) {
550        cmd("BITCOUNT").arg(key).arg(start).arg(end).take()
551    }
552
553    /// Perform a bitwise AND between multiple keys (containing string values)
554    /// and store the result in the destination key.
555    /// Returns size of destination string after operation.
556    /// [Redis Docs](https://redis.io/commands/BITOP)
557    fn bit_and<D: ToSingleRedisArg, S: ToRedisArgs>(dstkey: D, srckeys: S) -> (usize) {
558        cmd("BITOP").arg("AND").arg(dstkey).arg(srckeys).take()
559    }
560
561    /// Perform a bitwise OR between multiple keys (containing string values)
562    /// and store the result in the destination key.
563    /// Returns size of destination string after operation.
564    /// [Redis Docs](https://redis.io/commands/BITOP)
565    fn bit_or<D: ToSingleRedisArg, S: ToRedisArgs>(dstkey: D, srckeys: S) -> (usize) {
566        cmd("BITOP").arg("OR").arg(dstkey).arg(srckeys).take()
567    }
568
569    /// Perform a bitwise XOR between multiple keys (containing string values)
570    /// and store the result in the destination key.
571    /// Returns size of destination string after operation.
572    /// [Redis Docs](https://redis.io/commands/BITOP)
573    fn bit_xor<D: ToSingleRedisArg, S: ToRedisArgs>(dstkey: D, srckeys: S) -> (usize) {
574        cmd("BITOP").arg("XOR").arg(dstkey).arg(srckeys).take()
575    }
576
577    /// Perform a bitwise NOT of the key (containing string values)
578    /// and store the result in the destination key.
579    /// Returns size of destination string after operation.
580    /// [Redis Docs](https://redis.io/commands/BITOP)
581    fn bit_not<D: ToSingleRedisArg, S: ToSingleRedisArg>(dstkey: D, srckey: S) -> (usize) {
582        cmd("BITOP").arg("NOT").arg(dstkey).arg(srckey).take()
583    }
584
585    /// DIFF(X, Y1, Y2, …) \
586    /// Perform a **set difference** to extract the members of X that are not members of any of Y1, Y2,…. \
587    /// Logical representation: X  ∧ ¬(Y1 ∨ Y2 ∨ …) \
588    /// [Redis Docs](https://redis.io/commands/BITOP)
589    fn bit_diff<D: ToSingleRedisArg, S: ToRedisArgs>(dstkey: D, srckeys: S) -> (usize) {
590        cmd("BITOP").arg("DIFF").arg(dstkey).arg(srckeys).take()
591    }
592
593    /// DIFF1(X, Y1, Y2, …) (Relative complement difference) \
594    /// Perform a **relative complement set difference** to extract the members of one or more of Y1, Y2,… that are not members of X. \
595    /// Logical representation: ¬X  ∧ (Y1 ∨ Y2 ∨ …) \
596    /// [Redis Docs](https://redis.io/commands/BITOP)
597    fn bit_diff1<D: ToSingleRedisArg, S: ToRedisArgs>(dstkey: D, srckeys: S) -> (usize) {
598        cmd("BITOP").arg("DIFF1").arg(dstkey).arg(srckeys).take()
599    }
600
601    /// ANDOR(X, Y1, Y2, …) \
602    /// Perform an **"intersection of union(s)"** operation to extract the members of X that are also members of one or more of Y1, Y2,…. \
603    /// Logical representation: X ∧ (Y1 ∨ Y2 ∨ …) \
604    /// [Redis Docs](https://redis.io/commands/BITOP)
605    fn bit_and_or<D: ToSingleRedisArg, S: ToRedisArgs>(dstkey: D, srckeys: S) -> (usize) {
606        cmd("BITOP").arg("ANDOR").arg(dstkey).arg(srckeys).take()
607    }
608
609    /// ONE(X, Y1, Y2, …) \
610    /// Perform an **"exclusive membership"** operation to extract the members of exactly **one** of X, Y1, Y2, …. \
611    /// Logical representation: (X ∨ Y1 ∨ Y2 ∨ …) ∧ ¬((X ∧ Y1) ∨ (X ∧ Y2) ∨ (Y1 ∧ Y2) ∨ (Y1 ∧ Y3) ∨ …) \
612    /// [Redis Docs](https://redis.io/commands/BITOP)
613    fn bit_one<D: ToSingleRedisArg, S: ToRedisArgs>(dstkey: D, srckeys: S) -> (usize) {
614        cmd("BITOP").arg("ONE").arg(dstkey).arg(srckeys).take()
615    }
616
617    /// Get the length of the value stored in a key.
618    /// 0 if key does not exist.
619    /// [Redis Docs](https://redis.io/commands/STRLEN)
620    fn strlen<K: ToSingleRedisArg>(key: K) -> (usize) {
621        cmd("STRLEN").arg(key).take()
622    }
623
624    // hash operations
625
626    /// Gets a single (or multiple) fields from a hash.
627    fn hget<K: ToSingleRedisArg, F: ToSingleRedisArg>(key: K, field: F) -> (Option<String>) {
628        cmd("HGET").arg(key).arg(field).take()
629    }
630
631    /// Gets multiple fields from a hash.
632    /// [Redis Docs](https://redis.io/commands/HMGET)
633    fn hmget<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, fields: F) -> (Vec<String>) {
634        cmd("HMGET").arg(key).arg(fields).take()
635    }
636
637    /// Get the value of one or more fields of a given hash key, and optionally set their expiration
638    /// [Redis Docs](https://redis.io/commands/HGETEX)
639    fn hget_ex<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, fields: F, expire_at: Expiry) -> (Vec<String>) {
640        cmd("HGETEX").arg(key).arg(expire_at).arg("FIELDS").arg(fields.num_of_args()).arg(fields).take()
641    }
642
643    /// Deletes a single (or multiple) fields from a hash.
644    /// Returns number of fields deleted.
645    /// [Redis Docs](https://redis.io/commands/HDEL)
646    fn hdel<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, field: F) -> (usize) {
647        cmd("HDEL").arg(key).arg(field).take()
648    }
649
650    /// Get and delete the value of one or more fields of a given hash key
651    /// [Redis Docs](https://redis.io/commands/HGETDEL)
652    fn hget_del<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, fields: F) -> (Vec<Option<String>>) {
653        cmd("HGETDEL").arg(key).arg("FIELDS").arg(fields.num_of_args()).arg(fields).take()
654    }
655
656    /// Sets a single field in a hash.
657    /// Returns number of fields added.
658    /// [Redis Docs](https://redis.io/commands/HSET)
659    fn hset<K: ToSingleRedisArg, F: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, field: F, value: V) -> (usize) {
660        cmd("HSET").arg(key).arg(field).arg(value).take()
661    }
662
663    /// Set the value of one or more fields of a given hash key, and optionally set their expiration
664    /// [Redis Docs](https://redis.io/commands/HSETEX)
665    fn hset_ex<K: ToSingleRedisArg, F: ToRedisArgs, V: ToRedisArgs>(key: K, hash_field_expiration_options: &'a HashFieldExpirationOptions, fields_values: &'a [(F, V)]) -> (bool) {
666        cmd("HSETEX").arg(key).arg(hash_field_expiration_options).arg("FIELDS").arg(fields_values.len()).arg(fields_values).take()
667    }
668
669    /// Sets a single field in a hash if it does not exist.
670    /// Returns whether the field was added.
671    /// [Redis Docs](https://redis.io/commands/HSETNX)
672    fn hset_nx<K: ToSingleRedisArg, F: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, field: F, value: V) -> (bool) {
673        cmd("HSETNX").arg(key).arg(field).arg(value).take()
674    }
675
676    /// Sets multiple fields in a hash.
677    /// [Redis Docs](https://redis.io/commands/HMSET)
678    fn hset_multiple<K: ToSingleRedisArg, F: ToRedisArgs, V: ToRedisArgs>(key: K, items: &'a [(F, V)]) -> (()) {
679        cmd("HMSET").arg(key).arg(items).take()
680    }
681
682    /// Increments a value.
683    /// Returns the new value of the field after incrementation.
684    fn hincr<K: ToSingleRedisArg, F: ToSingleRedisArg, D: ToSingleRedisArg>(key: K, field: F, delta: D) -> (f64) {
685        cmd(if delta.describe_numeric_behavior() == NumericBehavior::NumberIsFloat {
686            "HINCRBYFLOAT"
687        } else {
688            "HINCRBY"
689        }).arg(key).arg(field).arg(delta).take()
690    }
691
692    /// Checks if a field in a hash exists.
693    /// [Redis Docs](https://redis.io/commands/HEXISTS)
694    fn hexists<K: ToSingleRedisArg, F: ToSingleRedisArg>(key: K, field: F) -> (bool) {
695        cmd("HEXISTS").arg(key).arg(field).take()
696    }
697
698    /// Get one or more fields' TTL in seconds.
699    /// [Redis Docs](https://redis.io/commands/HTTL)
700    fn httl<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
701        cmd("HTTL").arg(key).arg("FIELDS").arg(fields.num_of_args()).arg(fields).take()
702    }
703
704    /// Get one or more fields' TTL in milliseconds.
705    /// [Redis Docs](https://redis.io/commands/HPTTL)
706    fn hpttl<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
707        cmd("HPTTL").arg(key).arg("FIELDS").arg(fields.num_of_args()).arg(fields).take()
708    }
709
710    /// Set one or more fields' time to live in seconds.
711    /// Returns an array where each element corresponds to the field at the same index in the fields argument.
712    /// Each element of the array is either:
713    /// 0 if the specified condition has not been met.
714    /// 1 if the expiration time was updated.
715    /// 2 if called with 0 seconds.
716    /// Errors if provided key exists but is not a hash.
717    /// [Redis Docs](https://redis.io/commands/HEXPIRE)
718    fn hexpire<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, seconds: i64, opt: ExpireOption, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
719       cmd("HEXPIRE").arg(key).arg(seconds).arg(opt).arg("FIELDS").arg(fields.num_of_args()).arg(fields).take()
720    }
721
722
723    /// Set the expiration for one or more fields as a UNIX timestamp in seconds.
724    /// Returns an array where each element corresponds to the field at the same index in the fields argument.
725    /// Each element of the array is either:
726    /// 0 if the specified condition has not been met.
727    /// 1 if the expiration time was updated.
728    /// 2 if called with a time in the past.
729    /// Errors if provided key exists but is not a hash.
730    /// [Redis Docs](https://redis.io/commands/HEXPIREAT)
731    fn hexpire_at<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, ts: i64, opt: ExpireOption, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
732        cmd("HEXPIREAT").arg(key).arg(ts).arg(opt).arg("FIELDS").arg(fields.num_of_args()).arg(fields).take()
733    }
734
735    /// Returns the absolute Unix expiration timestamp in seconds.
736    /// [Redis Docs](https://redis.io/commands/HEXPIRETIME)
737    fn hexpire_time<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
738        cmd("HEXPIRETIME").arg(key).arg("FIELDS").arg(fields.num_of_args()).arg(fields).take()
739    }
740
741    /// Remove the expiration from a key.
742    /// Returns 1 if the expiration was removed.
743    /// [Redis Docs](https://redis.io/commands/HPERSIST)
744    fn hpersist<K: ToSingleRedisArg, F :ToRedisArgs>(key: K, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
745        cmd("HPERSIST").arg(key).arg("FIELDS").arg(fields.num_of_args()).arg(fields).take()
746    }
747
748    /// Set one or more fields' time to live in milliseconds.
749    /// Returns an array where each element corresponds to the field at the same index in the fields argument.
750    /// Each element of the array is either:
751    /// 0 if the specified condition has not been met.
752    /// 1 if the expiration time was updated.
753    /// 2 if called with 0 seconds.
754    /// Errors if provided key exists but is not a hash.
755    /// [Redis Docs](https://redis.io/commands/HPEXPIRE)
756    fn hpexpire<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, milliseconds: i64, opt: ExpireOption, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
757        cmd("HPEXPIRE").arg(key).arg(milliseconds).arg(opt).arg("FIELDS").arg(fields.num_of_args()).arg(fields).take()
758    }
759
760    /// Set the expiration for one or more fields as a UNIX timestamp in milliseconds.
761    /// Returns an array where each element corresponds to the field at the same index in the fields argument.
762    /// Each element of the array is either:
763    /// 0 if the specified condition has not been met.
764    /// 1 if the expiration time was updated.
765    /// 2 if called with a time in the past.
766    /// Errors if provided key exists but is not a hash.
767    /// [Redis Docs](https://redis.io/commands/HPEXPIREAT)
768    fn hpexpire_at<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, ts: i64,  opt: ExpireOption, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
769        cmd("HPEXPIREAT").arg(key).arg(ts).arg(opt).arg("FIELDS").arg(fields.num_of_args()).arg(fields).take()
770    }
771
772    /// Returns the absolute Unix expiration timestamp in seconds.
773    /// [Redis Docs](https://redis.io/commands/HPEXPIRETIME)
774    fn hpexpire_time<K: ToSingleRedisArg, F: ToRedisArgs>(key: K, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
775        cmd("HPEXPIRETIME").arg(key).arg("FIELDS").arg(fields.num_of_args()).arg(fields).take()
776    }
777
778    /// Gets all the keys in a hash.
779    /// [Redis Docs](https://redis.io/commands/HKEYS)
780    fn hkeys<K: ToSingleRedisArg>(key: K) -> (Vec<String>) {
781        cmd("HKEYS").arg(key).take()
782    }
783
784    /// Gets all the values in a hash.
785    /// [Redis Docs](https://redis.io/commands/HVALS)
786    fn hvals<K: ToSingleRedisArg>(key: K) -> (Vec<String>) {
787        cmd("HVALS").arg(key).take()
788    }
789
790    /// Gets all the fields and values in a hash.
791    /// [Redis Docs](https://redis.io/commands/HGETALL)
792    fn hgetall<K: ToSingleRedisArg>(key: K) -> (std::collections::HashMap<String, String>) {
793        cmd("HGETALL").arg(key).take()
794    }
795
796    /// Gets the length of a hash.
797    /// Returns 0 if key does not exist.
798    /// [Redis Docs](https://redis.io/commands/HLEN)
799    fn hlen<K: ToSingleRedisArg>(key: K) -> (usize) {
800        cmd("HLEN").arg(key).take()
801    }
802
803    // list operations
804
805    /// Pop an element from a list, push it to another list
806    /// and return it; or block until one is available
807    /// [Redis Docs](https://redis.io/commands/BLMOVE)
808    fn blmove<S: ToSingleRedisArg, D: ToSingleRedisArg>(srckey: S, dstkey: D, src_dir: Direction, dst_dir: Direction, timeout: f64) -> (Option<String>) {
809        cmd("BLMOVE").arg(srckey).arg(dstkey).arg(src_dir).arg(dst_dir).arg(timeout).take()
810    }
811
812    /// Pops `count` elements from the first non-empty list key from the list of
813    /// provided key names; or blocks until one is available.
814    /// [Redis Docs](https://redis.io/commands/BLMPOP)
815    fn blmpop<K: ToRedisArgs>(timeout: f64, numkeys: usize, key: K, dir: Direction, count: usize) -> (Option<[String; 2]>) {
816        cmd("BLMPOP").arg(timeout).arg(numkeys).arg(key).arg(dir).arg("COUNT").arg(count).take()
817    }
818
819    /// Remove and get the first element in a list, or block until one is available.
820    /// [Redis Docs](https://redis.io/commands/BLPOP)
821    fn blpop<K: ToRedisArgs>(key: K, timeout: f64) -> (Option<[String; 2]>) {
822        cmd("BLPOP").arg(key).arg(timeout).take()
823    }
824
825    /// Remove and get the last element in a list, or block until one is available.
826    /// [Redis Docs](https://redis.io/commands/BRPOP)
827    fn brpop<K: ToRedisArgs>(key: K, timeout: f64) -> (Option<[String; 2]>) {
828        cmd("BRPOP").arg(key).arg(timeout).take()
829    }
830
831    /// Pop a value from a list, push it to another list and return it;
832    /// or block until one is available.
833    /// [Redis Docs](https://redis.io/commands/BRPOPLPUSH)
834    fn brpoplpush<S: ToSingleRedisArg, D: ToSingleRedisArg>(srckey: S, dstkey: D, timeout: f64) -> (Option<String>) {
835        cmd("BRPOPLPUSH").arg(srckey).arg(dstkey).arg(timeout).take()
836    }
837
838    /// Get an element from a list by its index.
839    /// [Redis Docs](https://redis.io/commands/LINDEX)
840    fn lindex<K: ToSingleRedisArg>(key: K, index: isize) -> (Option<String>) {
841        cmd("LINDEX").arg(key).arg(index).take()
842    }
843
844    /// Insert an element before another element in a list.
845    /// [Redis Docs](https://redis.io/commands/LINSERT)
846    fn linsert_before<K: ToSingleRedisArg, P: ToSingleRedisArg, V: ToSingleRedisArg>(
847            key: K, pivot: P, value: V) -> (isize) {
848        cmd("LINSERT").arg(key).arg("BEFORE").arg(pivot).arg(value).take()
849    }
850
851    /// Insert an element after another element in a list.
852    /// [Redis Docs](https://redis.io/commands/LINSERT)
853    fn linsert_after<K: ToSingleRedisArg, P: ToSingleRedisArg, V: ToSingleRedisArg>(
854            key: K, pivot: P, value: V) -> (isize) {
855        cmd("LINSERT").arg(key).arg("AFTER").arg(pivot).arg(value).take()
856    }
857
858    /// Returns the length of the list stored at key.
859    /// [Redis Docs](https://redis.io/commands/LLEN)
860    fn llen<K: ToSingleRedisArg>(key: K) -> (usize) {
861        cmd("LLEN").arg(key).take()
862    }
863
864    /// Pop an element a list, push it to another list and return it
865    /// [Redis Docs](https://redis.io/commands/LMOVE)
866    fn lmove<S: ToSingleRedisArg, D: ToSingleRedisArg>(srckey: S, dstkey: D, src_dir: Direction, dst_dir: Direction) -> (String) {
867        cmd("LMOVE").arg(srckey).arg(dstkey).arg(src_dir).arg(dst_dir).take()
868    }
869
870    /// Pops `count` elements from the first non-empty list key from the list of
871    /// provided key names.
872    /// [Redis Docs](https://redis.io/commands/LMPOP)
873    fn lmpop<K: ToRedisArgs>( numkeys: usize, key: K, dir: Direction, count: usize) -> (Option<(String, Vec<String>)>) {
874        cmd("LMPOP").arg(numkeys).arg(key).arg(dir).arg("COUNT").arg(count).take()
875    }
876
877    /// Removes and returns the up to `count` first elements of the list stored at key.
878    ///
879    /// If `count` is not specified, then defaults to first element.
880    /// [Redis Docs](https://redis.io/commands/LPOP)
881    fn lpop<K: ToSingleRedisArg>(key: K, count: Option<core::num::NonZeroUsize>) -> Generic {
882        cmd("LPOP").arg(key).arg(count).take()
883    }
884
885    /// Returns the index of the first matching value of the list stored at key.
886    /// [Redis Docs](https://redis.io/commands/LPOS)
887    fn lpos<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, value: V, options: LposOptions) -> Generic {
888        cmd("LPOS").arg(key).arg(value).arg(options).take()
889    }
890
891    /// Insert all the specified values at the head of the list stored at key.
892    /// [Redis Docs](https://redis.io/commands/LPUSH)
893    fn lpush<K: ToSingleRedisArg, V: ToRedisArgs>(key: K, value: V) -> (usize) {
894        cmd("LPUSH").arg(key).arg(value).take()
895    }
896
897    /// Inserts a value at the head of the list stored at key, only if key
898    /// already exists and holds a list.
899    /// [Redis Docs](https://redis.io/commands/LPUSHX)
900    fn lpush_exists<K: ToSingleRedisArg, V: ToRedisArgs>(key: K, value: V) -> (usize) {
901        cmd("LPUSHX").arg(key).arg(value).take()
902    }
903
904    /// Returns the specified elements of the list stored at key.
905    /// [Redis Docs](https://redis.io/commands/LRANGE)
906    fn lrange<K: ToSingleRedisArg>(key: K, start: isize, stop: isize) -> (Vec<String>) {
907        cmd("LRANGE").arg(key).arg(start).arg(stop).take()
908    }
909
910    /// Removes the first count occurrences of elements equal to value
911    /// from the list stored at key.
912    /// [Redis Docs](https://redis.io/commands/LREM)
913    fn lrem<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, count: isize, value: V) -> (usize) {
914        cmd("LREM").arg(key).arg(count).arg(value).take()
915    }
916
917    /// Trim an existing list so that it will contain only the specified
918    /// range of elements specified.
919    /// [Redis Docs](https://redis.io/commands/LTRIM)
920    fn ltrim<K: ToSingleRedisArg>(key: K, start: isize, stop: isize) -> (()) {
921        cmd("LTRIM").arg(key).arg(start).arg(stop).take()
922    }
923
924    /// Sets the list element at index to value
925    /// [Redis Docs](https://redis.io/commands/LSET)
926    fn lset<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, index: isize, value: V) -> (()) {
927        cmd("LSET").arg(key).arg(index).arg(value).take()
928    }
929
930    /// Sends a ping to the server
931    /// [Redis Docs](https://redis.io/commands/PING)
932    fn ping<>() -> (String) {
933         cmd("PING").take()
934    }
935
936    /// Sends a ping with a message to the server
937    /// [Redis Docs](https://redis.io/commands/PING)
938    fn ping_message<K: ToSingleRedisArg>(message: K) -> (String) {
939         cmd("PING").arg(message).take()
940    }
941
942    /// Removes and returns the up to `count` last elements of the list stored at key
943    ///
944    /// If `count` is not specified, then defaults to last element.
945    /// [Redis Docs](https://redis.io/commands/RPOP)
946    fn rpop<K: ToSingleRedisArg>(key: K, count: Option<core::num::NonZeroUsize>) -> Generic {
947        cmd("RPOP").arg(key).arg(count).take()
948    }
949
950    /// Pop a value from a list, push it to another list and return it.
951    /// [Redis Docs](https://redis.io/commands/RPOPLPUSH)
952    fn rpoplpush<K: ToSingleRedisArg, D: ToSingleRedisArg>(key: K, dstkey: D) -> (Option<String>) {
953        cmd("RPOPLPUSH").arg(key).arg(dstkey).take()
954    }
955
956    /// Insert all the specified values at the tail of the list stored at key.
957    /// [Redis Docs](https://redis.io/commands/RPUSH)
958    fn rpush<K: ToSingleRedisArg, V: ToRedisArgs>(key: K, value: V) -> (usize) {
959        cmd("RPUSH").arg(key).arg(value).take()
960    }
961
962    /// Inserts value at the tail of the list stored at key, only if key
963    /// already exists and holds a list.
964    /// [Redis Docs](https://redis.io/commands/RPUSHX)
965    fn rpush_exists<K: ToSingleRedisArg, V: ToRedisArgs>(key: K, value: V) -> (usize) {
966        cmd("RPUSHX").arg(key).arg(value).take()
967    }
968
969    // set commands
970
971    /// Add one or more members to a set.
972    /// [Redis Docs](https://redis.io/commands/SADD)
973    fn sadd<K: ToSingleRedisArg, M: ToRedisArgs>(key: K, member: M) -> (usize) {
974        cmd("SADD").arg(key).arg(member).take()
975    }
976
977    /// Get the number of members in a set.
978    /// [Redis Docs](https://redis.io/commands/SCARD)
979    fn scard<K: ToSingleRedisArg>(key: K) -> (usize) {
980        cmd("SCARD").arg(key).take()
981    }
982
983    /// Subtract multiple sets.
984    /// [Redis Docs](https://redis.io/commands/SDIFF)
985    fn sdiff<K: ToRedisArgs>(keys: K) -> (HashSet<String>) {
986        cmd("SDIFF").arg(keys).take()
987    }
988
989    /// Subtract multiple sets and store the resulting set in a key.
990    /// [Redis Docs](https://redis.io/commands/SDIFFSTORE)
991    fn sdiffstore<D: ToSingleRedisArg, K: ToRedisArgs>(dstkey: D, keys: K) -> (usize) {
992        cmd("SDIFFSTORE").arg(dstkey).arg(keys).take()
993    }
994
995    /// Intersect multiple sets.
996    /// [Redis Docs](https://redis.io/commands/SINTER)
997    fn sinter<K: ToRedisArgs>(keys: K) -> (HashSet<String>) {
998        cmd("SINTER").arg(keys).take()
999    }
1000
1001    /// Intersect multiple sets and store the resulting set in a key.
1002    /// [Redis Docs](https://redis.io/commands/SINTERSTORE)
1003    fn sinterstore<D: ToSingleRedisArg, K: ToRedisArgs>(dstkey: D, keys: K) -> (usize) {
1004        cmd("SINTERSTORE").arg(dstkey).arg(keys).take()
1005    }
1006
1007    /// Determine if a given value is a member of a set.
1008    /// [Redis Docs](https://redis.io/commands/SISMEMBER)
1009    fn sismember<K: ToSingleRedisArg, M: ToSingleRedisArg>(key: K, member: M) -> (bool) {
1010        cmd("SISMEMBER").arg(key).arg(member).take()
1011    }
1012
1013    /// Determine if given values are members of a set.
1014    /// [Redis Docs](https://redis.io/commands/SMISMEMBER)
1015    fn smismember<K: ToSingleRedisArg, M: ToRedisArgs>(key: K, members: M) -> (Vec<bool>) {
1016        cmd("SMISMEMBER").arg(key).arg(members).take()
1017    }
1018
1019    /// Get all the members in a set.
1020    /// [Redis Docs](https://redis.io/commands/SMEMBERS)
1021    fn smembers<K: ToSingleRedisArg>(key: K) -> (HashSet<String>) {
1022        cmd("SMEMBERS").arg(key).take()
1023    }
1024
1025    /// Move a member from one set to another.
1026    /// [Redis Docs](https://redis.io/commands/SMOVE)
1027    fn smove<S: ToSingleRedisArg, D: ToSingleRedisArg, M: ToSingleRedisArg>(srckey: S, dstkey: D, member: M) -> (bool) {
1028        cmd("SMOVE").arg(srckey).arg(dstkey).arg(member).take()
1029    }
1030
1031    /// Remove and return a random member from a set.
1032    /// [Redis Docs](https://redis.io/commands/SPOP)
1033    fn spop<K: ToSingleRedisArg>(key: K) -> Generic {
1034        cmd("SPOP").arg(key).take()
1035    }
1036
1037    /// Get one random member from a set.
1038    /// [Redis Docs](https://redis.io/commands/SRANDMEMBER)
1039    fn srandmember<K: ToSingleRedisArg>(key: K) -> (Option<String>) {
1040        cmd("SRANDMEMBER").arg(key).take()
1041    }
1042
1043    /// Get multiple random members from a set.
1044    /// [Redis Docs](https://redis.io/commands/SRANDMEMBER)
1045    fn srandmember_multiple<K: ToSingleRedisArg>(key: K, count: isize) -> (Vec<String>) {
1046        cmd("SRANDMEMBER").arg(key).arg(count).take()
1047    }
1048
1049    /// Remove one or more members from a set.
1050    /// [Redis Docs](https://redis.io/commands/SREM)
1051    fn srem<K: ToSingleRedisArg, M: ToRedisArgs>(key: K, member: M) -> (usize) {
1052        cmd("SREM").arg(key).arg(member).take()
1053    }
1054
1055    /// Add multiple sets.
1056    /// [Redis Docs](https://redis.io/commands/SUNION)
1057    fn sunion<K: ToRedisArgs>(keys: K) -> (HashSet<String>) {
1058        cmd("SUNION").arg(keys).take()
1059    }
1060
1061    /// Add multiple sets and store the resulting set in a key.
1062    /// [Redis Docs](https://redis.io/commands/SUNIONSTORE)
1063    fn sunionstore<D: ToSingleRedisArg, K: ToRedisArgs>(dstkey: D, keys: K) -> (usize) {
1064        cmd("SUNIONSTORE").arg(dstkey).arg(keys).take()
1065    }
1066
1067    // sorted set commands
1068
1069    /// Add one member to a sorted set, or update its score if it already exists.
1070    /// [Redis Docs](https://redis.io/commands/ZADD)
1071    fn zadd<K: ToSingleRedisArg, S: ToSingleRedisArg, M: ToSingleRedisArg>(key: K, member: M, score: S) -> usize{
1072        cmd("ZADD").arg(key).arg(score).arg(member).take()
1073    }
1074
1075    /// Add multiple members to a sorted set, or update its score if it already exists.
1076    /// [Redis Docs](https://redis.io/commands/ZADD)
1077    fn zadd_multiple<K: ToSingleRedisArg, S: ToRedisArgs, M: ToRedisArgs>(key: K, items: &'a [(S, M)]) -> (usize) {
1078        cmd("ZADD").arg(key).arg(items).take()
1079    }
1080
1081     /// Add one member to a sorted set, or update its score if it already exists.
1082     /// [Redis Docs](https://redis.io/commands/ZADD)
1083    fn zadd_options<K: ToSingleRedisArg, S: ToSingleRedisArg, M: ToSingleRedisArg>(key: K, member: M, score: S, options:&'a SortedSetAddOptions) -> usize{
1084        cmd("ZADD").arg(key).arg(options).arg(score).arg(member).take()
1085    }
1086
1087    /// Add multiple members to a sorted set, or update its score if it already exists.
1088    /// [Redis Docs](https://redis.io/commands/ZADD)
1089    fn zadd_multiple_options<K: ToSingleRedisArg, S: ToRedisArgs, M: ToRedisArgs>(key: K, items: &'a [(S, M)], options:&'a SortedSetAddOptions) -> (usize) {
1090        cmd("ZADD").arg(key).arg(options).arg(items).take()
1091    }
1092
1093    /// Get the number of members in a sorted set.
1094    /// [Redis Docs](https://redis.io/commands/ZCARD)
1095    fn zcard<K: ToSingleRedisArg>(key: K) -> (usize) {
1096        cmd("ZCARD").arg(key).take()
1097    }
1098
1099    /// Count the members in a sorted set with scores within the given values.
1100    /// [Redis Docs](https://redis.io/commands/ZCOUNT)
1101    fn zcount<K: ToSingleRedisArg, M: ToSingleRedisArg, MM: ToSingleRedisArg>(key: K, min: M, max: MM) -> (usize) {
1102        cmd("ZCOUNT").arg(key).arg(min).arg(max).take()
1103    }
1104
1105    /// Increments the member in a sorted set at key by delta.
1106    /// If the member does not exist, it is added with delta as its score.
1107    /// [Redis Docs](https://redis.io/commands/ZINCRBY)
1108    fn zincr<K: ToSingleRedisArg, M: ToSingleRedisArg, D: ToSingleRedisArg>(key: K, member: M, delta: D) -> (f64) {
1109        cmd("ZINCRBY").arg(key).arg(delta).arg(member).take()
1110    }
1111
1112    /// Intersect multiple sorted sets and store the resulting sorted set in
1113    /// a new key using SUM as aggregation function.
1114    /// [Redis Docs](https://redis.io/commands/ZINTERSTORE)
1115    fn zinterstore<D: ToSingleRedisArg, K: ToRedisArgs>(dstkey: D, keys: K) -> (usize) {
1116        cmd("ZINTERSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).take()
1117    }
1118
1119    /// Intersect multiple sorted sets and store the resulting sorted set in
1120    /// a new key using MIN as aggregation function.
1121    /// [Redis Docs](https://redis.io/commands/ZINTERSTORE)
1122    fn zinterstore_min<D: ToSingleRedisArg, K: ToRedisArgs>(dstkey: D, keys: K) -> (usize) {
1123        cmd("ZINTERSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).arg("AGGREGATE").arg("MIN").take()
1124    }
1125
1126    /// Intersect multiple sorted sets and store the resulting sorted set in
1127    /// a new key using MAX as aggregation function.
1128    /// [Redis Docs](https://redis.io/commands/ZINTERSTORE)
1129    fn zinterstore_max<D: ToSingleRedisArg, K: ToRedisArgs>(dstkey: D, keys: K) -> (usize) {
1130        cmd("ZINTERSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).arg("AGGREGATE").arg("MAX").take()
1131    }
1132
1133    /// [`Commands::zinterstore`], but with the ability to specify a
1134    /// multiplication factor for each sorted set by pairing one with each key
1135    /// in a tuple.
1136    /// [Redis Docs](https://redis.io/commands/ZINTERSTORE)
1137    fn zinterstore_weights<D: ToSingleRedisArg, K: ToRedisArgs, W: ToRedisArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1138        let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight):&(K, W)| -> ((&K, &W)) {(key, weight)}).unzip();
1139        cmd("ZINTERSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).arg("WEIGHTS").arg(weights).take()
1140    }
1141
1142    /// [`Commands::zinterstore_min`], but with the ability to specify a
1143    /// multiplication factor for each sorted set by pairing one with each key
1144    /// in a tuple.
1145    /// [Redis Docs](https://redis.io/commands/ZINTERSTORE)
1146    fn zinterstore_min_weights<D: ToSingleRedisArg, K: ToRedisArgs, W: ToRedisArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1147        let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight):&(K, W)| -> ((&K, &W)) {(key, weight)}).unzip();
1148        cmd("ZINTERSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).arg("AGGREGATE").arg("MIN").arg("WEIGHTS").arg(weights).take()
1149    }
1150
1151    /// [`Commands::zinterstore_max`], but with the ability to specify a
1152    /// multiplication factor for each sorted set by pairing one with each key
1153    /// in a tuple.
1154    /// [Redis Docs](https://redis.io/commands/ZINTERSTORE)
1155    fn zinterstore_max_weights<D: ToSingleRedisArg, K: ToRedisArgs, W: ToRedisArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1156        let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight):&(K, W)| -> ((&K, &W)) {(key, weight)}).unzip();
1157        cmd("ZINTERSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).arg("AGGREGATE").arg("MAX").arg("WEIGHTS").arg(weights).take()
1158    }
1159
1160    /// Count the number of members in a sorted set between a given lexicographical range.
1161    /// [Redis Docs](https://redis.io/commands/ZLEXCOUNT)
1162    fn zlexcount<K: ToSingleRedisArg, M: ToSingleRedisArg, MM: ToSingleRedisArg>(key: K, min: M, max: MM) -> (usize) {
1163        cmd("ZLEXCOUNT").arg(key).arg(min).arg(max).take()
1164    }
1165
1166    /// Removes and returns the member with the highest score in a sorted set.
1167    /// Blocks until a member is available otherwise.
1168    /// [Redis Docs](https://redis.io/commands/BZPOPMAX)
1169    fn bzpopmax<K: ToRedisArgs>(key: K, timeout: f64) -> (Option<(String, String, f64)>) {
1170        cmd("BZPOPMAX").arg(key).arg(timeout).take()
1171    }
1172
1173    /// Removes and returns up to count members with the highest scores in a sorted set
1174    /// [Redis Docs](https://redis.io/commands/ZPOPMAX)
1175    fn zpopmax<K: ToSingleRedisArg>(key: K, count: isize) -> (Vec<String>) {
1176        cmd("ZPOPMAX").arg(key).arg(count).take()
1177    }
1178
1179    /// Removes and returns the member with the lowest score in a sorted set.
1180    /// Blocks until a member is available otherwise.
1181    /// [Redis Docs](https://redis.io/commands/BZPOPMIN)
1182    fn bzpopmin<K: ToRedisArgs>(key: K, timeout: f64) -> (Option<(String, String, f64)>) {
1183        cmd("BZPOPMIN").arg(key).arg(timeout).take()
1184    }
1185
1186    /// Removes and returns up to count members with the lowest scores in a sorted set
1187    /// [Redis Docs](https://redis.io/commands/ZPOPMIN)
1188    fn zpopmin<K: ToSingleRedisArg>(key: K, count: isize) -> (Vec<String>) {
1189        cmd("ZPOPMIN").arg(key).arg(count).take()
1190    }
1191
1192    /// Removes and returns up to count members with the highest scores,
1193    /// from the first non-empty sorted set in the provided list of key names.
1194    /// Blocks until a member is available otherwise.
1195    /// [Redis Docs](https://redis.io/commands/BZMPOP)
1196    fn bzmpop_max<K: ToRedisArgs>(timeout: f64, keys: K, count: isize) -> (Option<(String, Vec<(String, f64)>)>) {
1197        cmd("BZMPOP").arg(timeout).arg(keys.num_of_args()).arg(keys).arg("MAX").arg("COUNT").arg(count).take()
1198    }
1199
1200    /// Removes and returns up to count members with the highest scores,
1201    /// from the first non-empty sorted set in the provided list of key names.
1202    /// [Redis Docs](https://redis.io/commands/ZMPOP)
1203    fn zmpop_max<K: ToRedisArgs>(keys: K, count: isize) -> (Option<(String, Vec<(String, f64)>)>) {
1204        cmd("ZMPOP").arg(keys.num_of_args()).arg(keys).arg("MAX").arg("COUNT").arg(count).take()
1205    }
1206
1207    /// Removes and returns up to count members with the lowest scores,
1208    /// from the first non-empty sorted set in the provided list of key names.
1209    /// Blocks until a member is available otherwise.
1210    /// [Redis Docs](https://redis.io/commands/BZMPOP)
1211    fn bzmpop_min<K: ToRedisArgs>(timeout: f64, keys: K, count: isize) -> (Option<(String, Vec<(String, f64)>)>) {
1212        cmd("BZMPOP").arg(timeout).arg(keys.num_of_args()).arg(keys).arg("MIN").arg("COUNT").arg(count).take()
1213    }
1214
1215    /// Removes and returns up to count members with the lowest scores,
1216    /// from the first non-empty sorted set in the provided list of key names.
1217    /// [Redis Docs](https://redis.io/commands/ZMPOP)
1218    fn zmpop_min<K: ToRedisArgs>(keys: K, count: isize) -> (Option<(String, Vec<(String, f64)>)>) {
1219        cmd("ZMPOP").arg(keys.num_of_args()).arg(keys).arg("MIN").arg("COUNT").arg(count).take()
1220    }
1221
1222    /// Return up to count random members in a sorted set (or 1 if `count == None`)
1223    /// [Redis Docs](https://redis.io/commands/ZRANDMEMBER)
1224    fn zrandmember<K: ToSingleRedisArg>(key: K, count: Option<isize>) -> Generic {
1225        cmd("ZRANDMEMBER").arg(key).arg(count).take()
1226    }
1227
1228    /// Return up to count random members in a sorted set with scores
1229    /// [Redis Docs](https://redis.io/commands/ZRANDMEMBER)
1230    fn zrandmember_withscores<K: ToSingleRedisArg>(key: K, count: isize) -> Generic {
1231        cmd("ZRANDMEMBER").arg(key).arg(count).arg("WITHSCORES").take()
1232    }
1233
1234    /// Return a range of members in a sorted set, by index
1235    /// [Redis Docs](https://redis.io/commands/ZRANGE)
1236    fn zrange<K: ToSingleRedisArg>(key: K, start: isize, stop: isize) -> (Vec<String>) {
1237        cmd("ZRANGE").arg(key).arg(start).arg(stop).take()
1238    }
1239
1240    /// Return a range of members in a sorted set, by index with scores.
1241    /// [Redis Docs](https://redis.io/commands/ZRANGE)
1242    fn zrange_withscores<K: ToSingleRedisArg>(key: K, start: isize, stop: isize) -> (Vec<(String, f64)>) {
1243        cmd("ZRANGE").arg(key).arg(start).arg(stop).arg("WITHSCORES").take()
1244    }
1245
1246    /// Return a range of members in a sorted set, by lexicographical range.
1247    /// [Redis Docs](https://redis.io/commands/ZRANGEBYLEX)
1248    fn zrangebylex<K: ToSingleRedisArg, M: ToSingleRedisArg, MM: ToSingleRedisArg>(key: K, min: M, max: MM) -> (Vec<String>) {
1249        cmd("ZRANGEBYLEX").arg(key).arg(min).arg(max).take()
1250    }
1251
1252    /// Return a range of members in a sorted set, by lexicographical
1253    /// range with offset and limit.
1254    /// [Redis Docs](https://redis.io/commands/ZRANGEBYLEX)
1255    fn zrangebylex_limit<K: ToSingleRedisArg, M: ToSingleRedisArg, MM: ToSingleRedisArg>(
1256            key: K, min: M, max: MM, offset: isize, count: isize) -> (Vec<String>) {
1257        cmd("ZRANGEBYLEX").arg(key).arg(min).arg(max).arg("LIMIT").arg(offset).arg(count).take()
1258    }
1259
1260    /// Return a range of members in a sorted set, by lexicographical range.
1261    /// [Redis Docs](https://redis.io/commands/ZREVRANGEBYLEX)
1262    fn zrevrangebylex<K: ToSingleRedisArg, MM: ToSingleRedisArg, M: ToSingleRedisArg>(key: K, max: MM, min: M) -> (Vec<String>) {
1263        cmd("ZREVRANGEBYLEX").arg(key).arg(max).arg(min).take()
1264    }
1265
1266    /// Return a range of members in a sorted set, by lexicographical
1267    /// range with offset and limit.
1268    /// [Redis Docs](https://redis.io/commands/ZREVRANGEBYLEX)
1269    fn zrevrangebylex_limit<K: ToSingleRedisArg, MM: ToSingleRedisArg, M: ToSingleRedisArg>(
1270            key: K, max: MM, min: M, offset: isize, count: isize) -> (Vec<String>) {
1271        cmd("ZREVRANGEBYLEX").arg(key).arg(max).arg(min).arg("LIMIT").arg(offset).arg(count).take()
1272    }
1273
1274    /// Return a range of members in a sorted set, by score.
1275    /// [Redis Docs](https://redis.io/commands/ZRANGEBYSCORE)
1276    fn zrangebyscore<K: ToSingleRedisArg, M: ToSingleRedisArg, MM: ToSingleRedisArg>(key: K, min: M, max: MM) -> (Vec<String>) {
1277        cmd("ZRANGEBYSCORE").arg(key).arg(min).arg(max).take()
1278    }
1279
1280    /// Return a range of members in a sorted set, by score with scores.
1281    /// [Redis Docs](https://redis.io/commands/ZRANGEBYSCORE)
1282    fn zrangebyscore_withscores<K: ToSingleRedisArg, M: ToSingleRedisArg, MM: ToSingleRedisArg>(key: K, min: M, max: MM) -> (Vec<(String, usize)>) {
1283        cmd("ZRANGEBYSCORE").arg(key).arg(min).arg(max).arg("WITHSCORES").take()
1284    }
1285
1286    /// Return a range of members in a sorted set, by score with limit.
1287    /// [Redis Docs](https://redis.io/commands/ZRANGEBYSCORE)
1288    fn zrangebyscore_limit<K: ToSingleRedisArg, M: ToSingleRedisArg, MM: ToSingleRedisArg>
1289            (key: K, min: M, max: MM, offset: isize, count: isize) -> (Vec<String>) {
1290        cmd("ZRANGEBYSCORE").arg(key).arg(min).arg(max).arg("LIMIT").arg(offset).arg(count).take()
1291    }
1292
1293    /// Return a range of members in a sorted set, by score with limit with scores.
1294    /// [Redis Docs](https://redis.io/commands/ZRANGEBYSCORE)
1295    fn zrangebyscore_limit_withscores<K: ToSingleRedisArg, M: ToSingleRedisArg, MM: ToSingleRedisArg>
1296            (key: K, min: M, max: MM, offset: isize, count: isize) -> (Vec<(String, usize)>) {
1297        cmd("ZRANGEBYSCORE").arg(key).arg(min).arg(max).arg("WITHSCORES")
1298            .arg("LIMIT").arg(offset).arg(count).take()
1299    }
1300
1301    /// Determine the index of a member in a sorted set.
1302    /// [Redis Docs](https://redis.io/commands/ZRANK)
1303    fn zrank<K: ToSingleRedisArg, M: ToSingleRedisArg>(key: K, member: M) -> (Option<usize>) {
1304        cmd("ZRANK").arg(key).arg(member).take()
1305    }
1306
1307    /// Remove one or more members from a sorted set.
1308    /// [Redis Docs](https://redis.io/commands/ZREM)
1309    fn zrem<K: ToSingleRedisArg, M: ToRedisArgs>(key: K, members: M) -> (usize) {
1310        cmd("ZREM").arg(key).arg(members).take()
1311    }
1312
1313    /// Remove all members in a sorted set between the given lexicographical range.
1314    /// [Redis Docs](https://redis.io/commands/ZREMRANGEBYLEX)
1315    fn zrembylex<K: ToSingleRedisArg, M: ToSingleRedisArg, MM: ToSingleRedisArg>(key: K, min: M, max: MM) -> (usize) {
1316        cmd("ZREMRANGEBYLEX").arg(key).arg(min).arg(max).take()
1317    }
1318
1319    /// Remove all members in a sorted set within the given indexes.
1320    /// [Redis Docs](https://redis.io/commands/ZREMRANGEBYRANK)
1321    fn zremrangebyrank<K: ToSingleRedisArg>(key: K, start: isize, stop: isize) -> (usize) {
1322        cmd("ZREMRANGEBYRANK").arg(key).arg(start).arg(stop).take()
1323    }
1324
1325    /// Remove all members in a sorted set within the given scores.
1326    /// [Redis Docs](https://redis.io/commands/ZREMRANGEBYSCORE)
1327    fn zrembyscore<K: ToSingleRedisArg, M: ToSingleRedisArg, MM: ToSingleRedisArg>(key: K, min: M, max: MM) -> (usize) {
1328        cmd("ZREMRANGEBYSCORE").arg(key).arg(min).arg(max).take()
1329    }
1330
1331    /// Return a range of members in a sorted set, by index,
1332    /// ordered from high to low.
1333    /// [Redis Docs](https://redis.io/commands/ZREVRANGE)
1334    fn zrevrange<K: ToSingleRedisArg>(key: K, start: isize, stop: isize) -> (Vec<String>) {
1335        cmd("ZREVRANGE").arg(key).arg(start).arg(stop).take()
1336    }
1337
1338    /// Return a range of members in a sorted set, by index, with scores
1339    /// ordered from high to low.
1340    /// [Redis Docs](https://redis.io/commands/ZREVRANGE)
1341    fn zrevrange_withscores<K: ToSingleRedisArg>(key: K, start: isize, stop: isize) -> (Vec<String>) {
1342        cmd("ZREVRANGE").arg(key).arg(start).arg(stop).arg("WITHSCORES").take()
1343    }
1344
1345    /// Return a range of members in a sorted set, by score.
1346    /// [Redis Docs](https://redis.io/commands/ZREVRANGEBYSCORE)
1347    fn zrevrangebyscore<K: ToSingleRedisArg, MM: ToSingleRedisArg, M: ToSingleRedisArg>(key: K, max: MM, min: M) -> (Vec<String>) {
1348        cmd("ZREVRANGEBYSCORE").arg(key).arg(max).arg(min).take()
1349    }
1350
1351    /// Return a range of members in a sorted set, by score with scores.
1352    /// [Redis Docs](https://redis.io/commands/ZREVRANGEBYSCORE)
1353    fn zrevrangebyscore_withscores<K: ToSingleRedisArg, MM: ToSingleRedisArg, M: ToSingleRedisArg>(key: K, max: MM, min: M) -> (Vec<String>) {
1354        cmd("ZREVRANGEBYSCORE").arg(key).arg(max).arg(min).arg("WITHSCORES").take()
1355    }
1356
1357    /// Return a range of members in a sorted set, by score with limit.
1358    /// [Redis Docs](https://redis.io/commands/ZREVRANGEBYSCORE)
1359    fn zrevrangebyscore_limit<K: ToSingleRedisArg, MM: ToSingleRedisArg, M: ToSingleRedisArg>
1360            (key: K, max: MM, min: M, offset: isize, count: isize) -> (Vec<String>) {
1361        cmd("ZREVRANGEBYSCORE").arg(key).arg(max).arg(min).arg("LIMIT").arg(offset).arg(count).take()
1362    }
1363
1364    /// Return a range of members in a sorted set, by score with limit with scores.
1365    /// [Redis Docs](https://redis.io/commands/ZREVRANGEBYSCORE)
1366    fn zrevrangebyscore_limit_withscores<K: ToSingleRedisArg, MM: ToSingleRedisArg, M: ToSingleRedisArg>
1367            (key: K, max: MM, min: M, offset: isize, count: isize) -> (Vec<String>) {
1368        cmd("ZREVRANGEBYSCORE").arg(key).arg(max).arg(min).arg("WITHSCORES")
1369            .arg("LIMIT").arg(offset).arg(count).take()
1370    }
1371
1372    /// Determine the index of a member in a sorted set, with scores ordered from high to low.
1373    /// [Redis Docs](https://redis.io/commands/ZREVRANK)
1374    fn zrevrank<K: ToSingleRedisArg, M: ToSingleRedisArg>(key: K, member: M) -> (Option<usize>) {
1375        cmd("ZREVRANK").arg(key).arg(member).take()
1376    }
1377
1378    /// Get the score associated with the given member in a sorted set.
1379    /// [Redis Docs](https://redis.io/commands/ZSCORE)
1380    fn zscore<K: ToSingleRedisArg, M: ToSingleRedisArg>(key: K, member: M) -> (Option<f64>) {
1381        cmd("ZSCORE").arg(key).arg(member).take()
1382    }
1383
1384    /// Get the scores associated with multiple members in a sorted set.
1385    /// [Redis Docs](https://redis.io/commands/ZMSCORE)
1386    fn zscore_multiple<K: ToSingleRedisArg, M: ToRedisArgs>(key: K, members: &'a [M]) -> (Option<Vec<f64>>) {
1387        cmd("ZMSCORE").arg(key).arg(members).take()
1388    }
1389
1390    /// Unions multiple sorted sets and store the resulting sorted set in
1391    /// a new key using SUM as aggregation function.
1392    /// [Redis Docs](https://redis.io/commands/ZUNIONSTORE)
1393    fn zunionstore<D: ToSingleRedisArg, K: ToRedisArgs>(dstkey: D, keys: K) -> (usize) {
1394        cmd("ZUNIONSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).take()
1395    }
1396
1397    /// Unions multiple sorted sets and store the resulting sorted set in
1398    /// a new key using MIN as aggregation function.
1399    /// [Redis Docs](https://redis.io/commands/ZUNIONSTORE)
1400    fn zunionstore_min<D: ToSingleRedisArg, K: ToRedisArgs>(dstkey: D, keys: K) -> (usize) {
1401        cmd("ZUNIONSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).arg("AGGREGATE").arg("MIN").take()
1402    }
1403
1404    /// Unions multiple sorted sets and store the resulting sorted set in
1405    /// a new key using MAX as aggregation function.
1406    /// [Redis Docs](https://redis.io/commands/ZUNIONSTORE)
1407    fn zunionstore_max<D: ToSingleRedisArg, K: ToRedisArgs>(dstkey: D, keys: K) -> (usize) {
1408        cmd("ZUNIONSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).arg("AGGREGATE").arg("MAX").take()
1409    }
1410
1411    /// [`Commands::zunionstore`], but with the ability to specify a
1412    /// multiplication factor for each sorted set by pairing one with each key
1413    /// in a tuple.
1414    /// [Redis Docs](https://redis.io/commands/ZUNIONSTORE)
1415    fn zunionstore_weights<D: ToSingleRedisArg, K: ToRedisArgs, W: ToRedisArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1416        let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight):&(K, W)| -> ((&K, &W)) {(key, weight)}).unzip();
1417        cmd("ZUNIONSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).arg("WEIGHTS").arg(weights).take()
1418    }
1419
1420    /// [`Commands::zunionstore_min`], but with the ability to specify a
1421    /// multiplication factor for each sorted set by pairing one with each key
1422    /// in a tuple.
1423    /// [Redis Docs](https://redis.io/commands/ZUNIONSTORE)
1424    fn zunionstore_min_weights<D: ToSingleRedisArg, K: ToRedisArgs, W: ToRedisArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1425        let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight):&(K, W)| -> ((&K, &W)) {(key, weight)}).unzip();
1426        cmd("ZUNIONSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).arg("AGGREGATE").arg("MIN").arg("WEIGHTS").arg(weights).take()
1427    }
1428
1429    /// [`Commands::zunionstore_max`], but with the ability to specify a
1430    /// multiplication factor for each sorted set by pairing one with each key
1431    /// in a tuple.
1432    /// [Redis Docs](https://redis.io/commands/ZUNIONSTORE)
1433    fn zunionstore_max_weights<D: ToSingleRedisArg, K: ToRedisArgs, W: ToRedisArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1434        let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight):&(K, W)| -> ((&K, &W)) {(key, weight)}).unzip();
1435        cmd("ZUNIONSTORE").arg(dstkey).arg(keys.num_of_args()).arg(keys).arg("AGGREGATE").arg("MAX").arg("WEIGHTS").arg(weights).take()
1436    }
1437
1438    // vector set commands
1439
1440    /// Add a new element into the vector set specified by key.
1441    /// [Redis Docs](https://redis.io/commands/VADD)
1442    #[cfg(feature = "vector-sets")]
1443    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1444    fn vadd<K: ToRedisArgs, E: ToRedisArgs>(key: K, input: vector_sets::VectorAddInput<'a>, element: E) -> (bool) {
1445        cmd("VADD").arg(key).arg(input).arg(element).take()
1446    }
1447
1448    /// Add a new element into the vector set specified by key with optional parameters for fine-tuning the insertion process.
1449    /// [Redis Docs](https://redis.io/commands/VADD)
1450    #[cfg(feature = "vector-sets")]
1451    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1452    fn vadd_options<K: ToRedisArgs, E: ToRedisArgs>(key: K, input: vector_sets::VectorAddInput<'a>, element: E, options: &'a vector_sets::VAddOptions) -> (bool) {
1453        cmd("VADD").arg(key).arg(options.reduction_dimension.map(|_| "REDUCE")).arg(options.reduction_dimension).arg(input).arg(element).arg(options).take()
1454    }
1455
1456    /// Get the number of members in a vector set.
1457    /// [Redis Docs](https://redis.io/commands/VCARD)
1458    #[cfg(feature = "vector-sets")]
1459    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1460    fn vcard<K: ToRedisArgs>(key: K) -> (usize) {
1461        cmd("VCARD").arg(key).take()
1462    }
1463
1464    /// Return the number of dimensions of the vectors in the specified vector set.
1465    /// [Redis Docs](https://redis.io/commands/VDIM)
1466    #[cfg(feature = "vector-sets")]
1467    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1468    fn vdim<K: ToRedisArgs>(key: K) -> (usize) {
1469        cmd("VDIM").arg(key).take()
1470    }
1471
1472    /// Return the approximate vector associated with a given element in the vector set.
1473    /// [Redis Docs](https://redis.io/commands/VEMB)
1474    #[cfg(feature = "vector-sets")]
1475    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1476    fn vemb<K: ToRedisArgs, E: ToRedisArgs>(key: K, element: E) -> Generic {
1477        cmd("VEMB").arg(key).arg(element).take()
1478    }
1479
1480    /// Return the raw internal representation of the approximate vector associated with a given element in the vector set.
1481    /// Vector sets normalize and may quantize vectors on insertion.
1482    /// VEMB reverses this process to approximate the original vector by de-normalizing and de-quantizing it.
1483    /// [Redis Docs](https://redis.io/commands/VEMB)
1484    #[cfg(feature = "vector-sets")]
1485    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1486    fn vemb_options<K: ToRedisArgs, E: ToRedisArgs>(key: K, element: E, options: &'a vector_sets::VEmbOptions) -> Generic {
1487        cmd("VEMB").arg(key).arg(element).arg(options).take()
1488    }
1489
1490    /// Remove an element from a vector set.
1491    /// [Redis Docs](https://redis.io/commands/VREM)
1492    #[cfg(feature = "vector-sets")]
1493    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1494    fn vrem<K: ToRedisArgs, E: ToRedisArgs>(key: K, element: E) -> (bool) {
1495        cmd("VREM").arg(key).arg(element).take()
1496    }
1497
1498    /// Associate a JSON object with an element in a vector set.
1499    /// Use this command to store attributes that can be used in filtered similarity searches with VSIM.
1500    /// [Redis Docs](https://redis.io/commands/VSETATTR)
1501    #[cfg(feature = "vector-sets")]
1502    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1503    fn vsetattr<K: ToRedisArgs, E: ToRedisArgs, J: Serialize>(key: K, element: E, json_object: &'a J) -> (bool) {
1504        let attributes_json = match serde_json::to_value(json_object) {
1505            Ok(serde_json::Value::String(s)) if s.is_empty() => "".to_string(),
1506            _ => serde_json::to_string(json_object).unwrap(),
1507        };
1508
1509        cmd("VSETATTR").arg(key).arg(element).arg(attributes_json).take()
1510    }
1511
1512    /// Delete the JSON attributes associated with an element in a vector set.
1513    /// This is an utility function that uses VSETATTR with an empty string.
1514    /// [Redis Docs](https://redis.io/commands/VSETATTR)
1515    #[cfg(feature = "vector-sets")]
1516    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1517    fn vdelattr<K: ToRedisArgs, E: ToRedisArgs>(key: K, element: E) -> (bool) {
1518        cmd("VSETATTR").arg(key).arg(element).arg("").take()
1519    }
1520
1521    /// Return the JSON attributes associated with an element in a vector set.
1522    /// [Redis Docs](https://redis.io/commands/VGETATTR)
1523    #[cfg(feature = "vector-sets")]
1524    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1525    fn vgetattr<K: ToRedisArgs, E: ToRedisArgs>(key: K, element: E) -> (Option<String>) {
1526        cmd("VGETATTR").arg(key).arg(element).take()
1527    }
1528
1529    /// Return metadata and internal details about a vector set, including
1530    /// size, dimensions, quantization type, and graph structure.
1531    /// [Redis Docs](https://redis.io/commands/VINFO)
1532    #[cfg(feature = "vector-sets")]
1533    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1534    fn vinfo<K: ToRedisArgs>(key: K) -> (Option<std::collections::HashMap<String, Value>>) {
1535        cmd("VINFO").arg(key).take()
1536    }
1537
1538    /// Return the neighbors of a specified element in a vector set.
1539    /// The command shows the connections for each layer of the HNSW graph.
1540    /// [Redis Docs](https://redis.io/commands/VLINKS)
1541    #[cfg(feature = "vector-sets")]
1542    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1543    fn vlinks<K: ToRedisArgs, E: ToRedisArgs>(key: K, element: E) -> Generic {
1544        cmd("VLINKS").arg(key).arg(element).take()
1545    }
1546
1547    /// Return the neighbors of a specified element in a vector set.
1548    /// The command shows the connections for each layer of the HNSW graph
1549    /// and includes similarity scores for each neighbor.
1550    /// [Redis Docs](https://redis.io/commands/VLINKS)]
1551    #[cfg(feature = "vector-sets")]
1552    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1553    fn vlinks_with_scores<K: ToRedisArgs, E: ToRedisArgs>(key: K, element: E) -> Generic {
1554        cmd("VLINKS").arg(key).arg(element).arg("WITHSCORES").take()
1555    }
1556
1557    /// Return one random elements from a vector set.
1558    /// [Redis Docs](https://redis.io/commands/VRANDMEMBER)
1559    #[cfg(feature = "vector-sets")]
1560    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1561    fn vrandmember<K: ToRedisArgs>(key: K) -> (Option<String>) {
1562        cmd("VRANDMEMBER").arg(key).take()
1563    }
1564
1565    /// Return multiple random elements from a vector set.
1566    /// [Redis Docs](https://redis.io/commands/VRANDMEMBER)
1567    #[cfg(feature = "vector-sets")]
1568    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1569    fn vrandmember_multiple<K: ToRedisArgs>(key: K, count: usize) -> (Vec<String>) {
1570        cmd("VRANDMEMBER").arg(key).arg(count).take()
1571    }
1572
1573    /// Perform vector similarity search.
1574    /// [Redis Docs](https://redis.io/commands/VSIM)
1575    #[cfg(feature = "vector-sets")]
1576    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1577    fn vsim<K: ToRedisArgs>(key: K, input: vector_sets::VectorSimilaritySearchInput<'a>) -> Generic {
1578        cmd("VSIM").arg(key).arg(input).take()
1579    }
1580
1581    /// Performs a vector similarity search with optional parameters for customization.
1582    /// [Redis Docs](https://redis.io/commands/VSIM)
1583    #[cfg(feature = "vector-sets")]
1584    #[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
1585    fn vsim_options<K: ToRedisArgs>(key: K, input: vector_sets::VectorSimilaritySearchInput<'a>, options: &'a vector_sets::VSimOptions) -> Generic {
1586        cmd("VSIM").arg(key).arg(input).arg(options).take()
1587    }
1588
1589    // hyperloglog commands
1590
1591    /// Adds the specified elements to the specified HyperLogLog.
1592    /// [Redis Docs](https://redis.io/commands/PFADD)
1593    fn pfadd<K: ToSingleRedisArg, E: ToRedisArgs>(key: K, element: E) -> (bool) {
1594        cmd("PFADD").arg(key).arg(element).take()
1595    }
1596
1597    /// Return the approximated cardinality of the set(s) observed by the
1598    /// HyperLogLog at key(s).
1599    /// [Redis Docs](https://redis.io/commands/PFCOUNT)
1600    fn pfcount<K: ToRedisArgs>(key: K) -> (usize) {
1601        cmd("PFCOUNT").arg(key).take()
1602    }
1603
1604    /// Merge N different HyperLogLogs into a single one.
1605    /// [Redis Docs](https://redis.io/commands/PFMERGE)
1606    fn pfmerge<D: ToSingleRedisArg, S: ToRedisArgs>(dstkey: D, srckeys: S) -> (()) {
1607        cmd("PFMERGE").arg(dstkey).arg(srckeys).take()
1608    }
1609
1610    /// Posts a message to the given channel.
1611    /// [Redis Docs](https://redis.io/commands/PUBLISH)
1612    fn publish<K: ToSingleRedisArg, E: ToSingleRedisArg>(channel: K, message: E) -> (usize) {
1613        cmd("PUBLISH").arg(channel).arg(message).take()
1614    }
1615
1616    /// Posts a message to the given sharded channel.
1617    /// [Redis Docs](https://redis.io/commands/SPUBLISH)
1618    fn spublish<K: ToSingleRedisArg, E: ToSingleRedisArg>(channel: K, message: E) -> (usize) {
1619        cmd("SPUBLISH").arg(channel).arg(message).take()
1620    }
1621
1622    // Object commands
1623
1624    /// Returns the encoding of a key.
1625    /// [Redis Docs](https://redis.io/commands/OBJECT)
1626    fn object_encoding<K: ToSingleRedisArg>(key: K) -> (Option<String>) {
1627        cmd("OBJECT").arg("ENCODING").arg(key).take()
1628    }
1629
1630    /// Returns the time in seconds since the last access of a key.
1631    /// [Redis Docs](https://redis.io/commands/OBJECT)
1632    fn object_idletime<K: ToSingleRedisArg>(key: K) -> (Option<usize>) {
1633        cmd("OBJECT").arg("IDLETIME").arg(key).take()
1634    }
1635
1636    /// Returns the logarithmic access frequency counter of a key.
1637    /// [Redis Docs](https://redis.io/commands/OBJECT)
1638    fn object_freq<K: ToSingleRedisArg>(key: K) -> (Option<usize>) {
1639        cmd("OBJECT").arg("FREQ").arg(key).take()
1640    }
1641
1642    /// Returns the reference count of a key.
1643    /// [Redis Docs](https://redis.io/commands/OBJECT)
1644    fn object_refcount<K: ToSingleRedisArg>(key: K) -> (Option<usize>) {
1645        cmd("OBJECT").arg("REFCOUNT").arg(key).take()
1646    }
1647
1648    /// Returns the name of the current connection as set by CLIENT SETNAME.
1649    /// [Redis Docs](https://redis.io/commands/CLIENT)
1650    fn client_getname<>() -> (Option<String>) {
1651        cmd("CLIENT").arg("GETNAME").take()
1652    }
1653
1654    /// Returns the ID of the current connection.
1655    /// [Redis Docs](https://redis.io/commands/CLIENT)
1656    fn client_id<>() -> (isize) {
1657        cmd("CLIENT").arg("ID").take()
1658    }
1659
1660    /// Command assigns a name to the current connection.
1661    /// [Redis Docs](https://redis.io/commands/CLIENT)
1662    fn client_setname<K: ToSingleRedisArg>(connection_name: K) -> (()) {
1663        cmd("CLIENT").arg("SETNAME").arg(connection_name).take()
1664    }
1665
1666    // ACL commands
1667
1668    /// When Redis is configured to use an ACL file (with the aclfile
1669    /// configuration option), this command will reload the ACLs from the file,
1670    /// replacing all the current ACL rules with the ones defined in the file.
1671    #[cfg(feature = "acl")]
1672    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1673    /// [Redis Docs](https://redis.io/commands/ACL)
1674    fn acl_load<>() -> () {
1675        cmd("ACL").arg("LOAD").take()
1676    }
1677
1678    /// When Redis is configured to use an ACL file (with the aclfile
1679    /// configuration option), this command will save the currently defined
1680    /// ACLs from the server memory to the ACL file.
1681    #[cfg(feature = "acl")]
1682    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1683    /// [Redis Docs](https://redis.io/commands/ACL)
1684    fn acl_save<>() -> () {
1685        cmd("ACL").arg("SAVE").take()
1686    }
1687
1688    /// Shows the currently active ACL rules in the Redis server.
1689    #[cfg(feature = "acl")]
1690    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1691    /// [Redis Docs](https://redis.io/commands/ACL)
1692    fn acl_list<>() -> (Vec<String>) {
1693        cmd("ACL").arg("LIST").take()
1694    }
1695
1696    /// Shows a list of all the usernames of the currently configured users in
1697    /// the Redis ACL system.
1698    #[cfg(feature = "acl")]
1699    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1700    /// [Redis Docs](https://redis.io/commands/ACL)
1701    fn acl_users<>() -> (Vec<String>) {
1702        cmd("ACL").arg("USERS").take()
1703    }
1704
1705    /// Returns all the rules defined for an existing ACL user.
1706    #[cfg(feature = "acl")]
1707    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1708    /// [Redis Docs](https://redis.io/commands/ACL)
1709    fn acl_getuser<K: ToSingleRedisArg>(username: K) -> (Option<acl::AclInfo>) {
1710        cmd("ACL").arg("GETUSER").arg(username).take()
1711    }
1712
1713    /// Creates an ACL user without any privilege.
1714    #[cfg(feature = "acl")]
1715    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1716    /// [Redis Docs](https://redis.io/commands/ACL)
1717    fn acl_setuser<K: ToSingleRedisArg>(username: K) -> () {
1718        cmd("ACL").arg("SETUSER").arg(username).take()
1719    }
1720
1721    /// Creates an ACL user with the specified rules or modify the rules of
1722    /// an existing user.
1723    #[cfg(feature = "acl")]
1724    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1725    /// [Redis Docs](https://redis.io/commands/ACL)
1726    fn acl_setuser_rules<K: ToSingleRedisArg>(username: K, rules: &'a [acl::Rule]) -> () {
1727        cmd("ACL").arg("SETUSER").arg(username).arg(rules).take()
1728    }
1729
1730    /// Delete all the specified ACL users and terminate all the connections
1731    /// that are authenticated with such users.
1732    #[cfg(feature = "acl")]
1733    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1734    /// [Redis Docs](https://redis.io/commands/ACL)
1735    fn acl_deluser<K: ToRedisArgs>(usernames: &'a [K]) -> (usize) {
1736        cmd("ACL").arg("DELUSER").arg(usernames).take()
1737    }
1738
1739    /// Simulate the execution of a given command by a given user.
1740    #[cfg(feature = "acl")]
1741    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1742    /// [Redis Docs](https://redis.io/commands/ACL)
1743    fn acl_dryrun<K: ToSingleRedisArg, C: ToSingleRedisArg, A: ToRedisArgs>(username: K, command: C, args: A) -> (String) {
1744        cmd("ACL").arg("DRYRUN").arg(username).arg(command).arg(args).take()
1745    }
1746
1747    /// Shows the available ACL categories.
1748    /// [Redis Docs](https://redis.io/commands/ACL)
1749    #[cfg(feature = "acl")]
1750    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1751    fn acl_cat<>() -> (HashSet<String>) {
1752        cmd("ACL").arg("CAT").take()
1753    }
1754
1755    /// Shows all the Redis commands in the specified category.
1756    /// [Redis Docs](https://redis.io/commands/ACL)
1757    #[cfg(feature = "acl")]
1758    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1759    fn acl_cat_categoryname<K: ToSingleRedisArg>(categoryname: K) -> (HashSet<String>) {
1760        cmd("ACL").arg("CAT").arg(categoryname).take()
1761    }
1762
1763    /// Generates a 256-bits password starting from /dev/urandom if available.
1764    /// [Redis Docs](https://redis.io/commands/ACL)
1765    #[cfg(feature = "acl")]
1766    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1767    fn acl_genpass<>() -> (String) {
1768        cmd("ACL").arg("GENPASS").take()
1769    }
1770
1771    /// Generates a 1-to-1024-bits password starting from /dev/urandom if available.
1772    /// [Redis Docs](https://redis.io/commands/ACL)
1773    #[cfg(feature = "acl")]
1774    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1775    fn acl_genpass_bits<>(bits: isize) -> (String) {
1776        cmd("ACL").arg("GENPASS").arg(bits).take()
1777    }
1778
1779    /// Returns the username the current connection is authenticated with.
1780    /// [Redis Docs](https://redis.io/commands/ACL)
1781    #[cfg(feature = "acl")]
1782    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1783    fn acl_whoami<>() -> (String) {
1784        cmd("ACL").arg("WHOAMI").take()
1785    }
1786
1787    /// Shows a list of recent ACL security events
1788    /// [Redis Docs](https://redis.io/commands/ACL)
1789    #[cfg(feature = "acl")]
1790    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1791    fn acl_log<>(count: isize) -> (Vec<String>) {
1792        cmd("ACL").arg("LOG").arg(count).take()
1793
1794    }
1795
1796    /// Clears the ACL log.
1797    /// [Redis Docs](https://redis.io/commands/ACL)
1798    #[cfg(feature = "acl")]
1799    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1800    fn acl_log_reset<>() -> () {
1801        cmd("ACL").arg("LOG").arg("RESET").take()
1802    }
1803
1804    /// Returns a helpful text describing the different subcommands.
1805    /// [Redis Docs](https://redis.io/commands/ACL)
1806    #[cfg(feature = "acl")]
1807    #[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
1808    fn acl_help<>() -> (Vec<String>) {
1809        cmd("ACL").arg("HELP").take()
1810    }
1811
1812    //
1813    // geospatial commands
1814    //
1815
1816    /// Adds the specified geospatial items to the specified key.
1817    ///
1818    /// Every member has to be written as a tuple of `(longitude, latitude,
1819    /// member_name)`. It can be a single tuple, or a vector of tuples.
1820    ///
1821    /// `longitude, latitude` can be set using [`redis::geo::Coord`][1].
1822    ///
1823    /// [1]: ./geo/struct.Coord.html
1824    ///
1825    /// Returns the number of elements added to the sorted set, not including
1826    /// elements already existing for which the score was updated.
1827    ///
1828    /// # Example
1829    ///
1830    /// ```rust,no_run
1831    /// use redis::{Commands, Connection, RedisResult};
1832    /// use redis::geo::Coord;
1833    ///
1834    /// fn add_point(con: &mut Connection) -> (RedisResult<isize>) {
1835    ///     con.geo_add("my_gis", (Coord::lon_lat(13.361389, 38.115556), "Palermo"))
1836    /// }
1837    ///
1838    /// fn add_point_with_tuples(con: &mut Connection) -> (RedisResult<isize>) {
1839    ///     con.geo_add("my_gis", ("13.361389", "38.115556", "Palermo"))
1840    /// }
1841    ///
1842    /// fn add_many_points(con: &mut Connection) -> (RedisResult<isize>) {
1843    ///     con.geo_add("my_gis", &[
1844    ///         ("13.361389", "38.115556", "Palermo"),
1845    ///         ("15.087269", "37.502669", "Catania")
1846    ///     ])
1847    /// }
1848    /// ```
1849    /// [Redis Docs](https://redis.io/commands/GEOADD)
1850    #[cfg(feature = "geospatial")]
1851    #[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
1852    fn geo_add<K: ToSingleRedisArg, M: ToRedisArgs>(key: K, members: M) -> (usize) {
1853        cmd("GEOADD").arg(key).arg(members).take()
1854    }
1855
1856    /// Return the distance between two members in the geospatial index
1857    /// represented by the sorted set.
1858    ///
1859    /// If one or both the members are missing, the command returns NULL, so
1860    /// it may be convenient to parse its response as either `Option<f64>` or
1861    /// `Option<String>`.
1862    ///
1863    /// # Example
1864    ///
1865    /// ```rust,no_run
1866    /// use redis::{Commands, RedisResult};
1867    /// use redis::geo::Unit;
1868    ///
1869    /// fn get_dists(con: &mut redis::Connection) {
1870    ///     let x: RedisResult<f64> = con.geo_dist(
1871    ///         "my_gis",
1872    ///         "Palermo",
1873    ///         "Catania",
1874    ///         Unit::Kilometers
1875    ///     );
1876    ///     // x is Ok(166.2742)
1877    ///
1878    ///     let x: RedisResult<Option<f64>> = con.geo_dist(
1879    ///         "my_gis",
1880    ///         "Palermo",
1881    ///         "Atlantis",
1882    ///         Unit::Meters
1883    ///     );
1884    ///     // x is Ok(None)
1885    /// }
1886    /// ```
1887    /// [Redis Docs](https://redis.io/commands/GEODIST)
1888    #[cfg(feature = "geospatial")]
1889    #[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
1890    fn geo_dist<K: ToSingleRedisArg, M1: ToSingleRedisArg, M2: ToSingleRedisArg>(
1891        key: K,
1892        member1: M1,
1893        member2: M2,
1894        unit: geo::Unit
1895    ) -> (Option<f64>) {
1896        cmd("GEODIST")
1897            .arg(key)
1898            .arg(member1)
1899            .arg(member2)
1900            .arg(unit)
1901            .take()
1902    }
1903
1904    /// Return valid [Geohash][1] strings representing the position of one or
1905    /// more members of the geospatial index represented by the sorted set at
1906    /// key.
1907    ///
1908    /// [1]: https://en.wikipedia.org/wiki/Geohash
1909    ///
1910    /// # Example
1911    ///
1912    /// ```rust,no_run
1913    /// use redis::{Commands, RedisResult};
1914    ///
1915    /// fn get_hash(con: &mut redis::Connection) {
1916    ///     let x: RedisResult<Vec<String>> = con.geo_hash("my_gis", "Palermo");
1917    ///     // x is vec!["sqc8b49rny0"]
1918    ///
1919    ///     let x: RedisResult<Vec<String>> = con.geo_hash("my_gis", &["Palermo", "Catania"]);
1920    ///     // x is vec!["sqc8b49rny0", "sqdtr74hyu0"]
1921    /// }
1922    /// ```
1923    /// [Redis Docs](https://redis.io/commands/GEOHASH)
1924    #[cfg(feature = "geospatial")]
1925    #[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
1926    fn geo_hash<K: ToSingleRedisArg, M: ToRedisArgs>(key: K, members: M) -> (Vec<String>) {
1927        cmd("GEOHASH").arg(key).arg(members).take()
1928    }
1929
1930    /// Return the positions of all the specified members of the geospatial
1931    /// index represented by the sorted set at key.
1932    ///
1933    /// Every position is a pair of `(longitude, latitude)`. [`redis::geo::Coord`][1]
1934    /// can be used to convert these value in a struct.
1935    ///
1936    /// [1]: ./geo/struct.Coord.html
1937    ///
1938    /// # Example
1939    ///
1940    /// ```rust,no_run
1941    /// use redis::{Commands, RedisResult};
1942    /// use redis::geo::Coord;
1943    ///
1944    /// fn get_position(con: &mut redis::Connection) {
1945    ///     let x: RedisResult<Vec<Vec<f64>>> = con.geo_pos("my_gis", &["Palermo", "Catania"]);
1946    ///     // x is [ [ 13.361389, 38.115556 ], [ 15.087269, 37.502669 ] ];
1947    ///
1948    ///     let x: Vec<Coord<f64>> = con.geo_pos("my_gis", "Palermo").unwrap();
1949    ///     // x[0].longitude is 13.361389
1950    ///     // x[0].latitude is 38.115556
1951    /// }
1952    /// ```
1953    /// [Redis Docs](https://redis.io/commands/GEOPOS)
1954    #[cfg(feature = "geospatial")]
1955    #[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
1956    fn geo_pos<K: ToSingleRedisArg, M: ToRedisArgs>(key: K, members: M) -> (Vec<Option<geo::Coord<f64>>>) {
1957        cmd("GEOPOS").arg(key).arg(members).take()
1958    }
1959
1960    /// Return the members of a sorted set populated with geospatial information
1961    /// using [`geo_add`](#method.geo_add), which are within the borders of the area
1962    /// specified with the center location and the maximum distance from the center
1963    /// (the radius).
1964    ///
1965    /// Every item in the result can be read with [`redis::geo::RadiusSearchResult`][1],
1966    /// which support the multiple formats returned by `GEORADIUS`.
1967    ///
1968    /// [1]: ./geo/struct.RadiusSearchResult.html
1969    ///
1970    /// ```rust,no_run
1971    /// use redis::{Commands, RedisResult};
1972    /// use redis::geo::{RadiusOptions, RadiusSearchResult, RadiusOrder, Unit};
1973    ///
1974    /// fn radius(con: &mut redis::Connection) -> (Vec<RadiusSearchResult>) {
1975    ///     let opts = RadiusOptions::default().with_dist().order(RadiusOrder::Asc);
1976    ///     con.geo_radius("my_gis", 15.90, 37.21, 51.39, Unit::Kilometers, opts).unwrap()
1977    /// }
1978    /// ```
1979    /// [Redis Docs](https://redis.io/commands/GEORADIUS)
1980    #[cfg(feature = "geospatial")]
1981    #[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
1982    fn geo_radius<K: ToSingleRedisArg>(
1983        key: K,
1984        longitude: f64,
1985        latitude: f64,
1986        radius: f64,
1987        unit: geo::Unit,
1988        options: geo::RadiusOptions
1989    ) -> (Vec<geo::RadiusSearchResult>) {
1990        cmd("GEORADIUS")
1991            .arg(key)
1992            .arg(longitude)
1993            .arg(latitude)
1994            .arg(radius)
1995            .arg(unit)
1996            .arg(options)
1997            .take()
1998    }
1999
2000    /// Retrieve members selected by distance with the center of `member`. The
2001    /// member itself is always contained in the results.
2002    /// [Redis Docs](https://redis.io/commands/GEORADIUSBYMEMBER)
2003    #[cfg(feature = "geospatial")]
2004    #[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
2005    fn geo_radius_by_member<K: ToSingleRedisArg, M: ToSingleRedisArg>(
2006        key: K,
2007        member: M,
2008        radius: f64,
2009        unit: geo::Unit,
2010        options: geo::RadiusOptions
2011    ) -> (Vec<geo::RadiusSearchResult>) {
2012        cmd("GEORADIUSBYMEMBER")
2013            .arg(key)
2014            .arg(member)
2015            .arg(radius)
2016            .arg(unit)
2017            .arg(options)
2018            .take()
2019    }
2020
2021    //
2022    // streams commands
2023    //
2024
2025    /// Ack pending stream messages checked out by a consumer.
2026    ///
2027    /// ```text
2028    /// XACK <key> <group> <id> <id> ... <id>
2029    /// ```
2030    /// [Redis Docs](https://redis.io/commands/XACK)
2031    #[cfg(feature = "streams")]
2032    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2033    fn xack<K: ToRedisArgs, G: ToRedisArgs, I: ToRedisArgs>(
2034        key: K,
2035        group: G,
2036        ids: &'a [I]) -> (usize) {
2037        cmd("XACK")            .arg(key)
2038                        .arg(group)
2039                        .arg(ids)
2040            .take()
2041    }
2042
2043    /// Negatively acknowledge (NACK) one or more pending stream messages.
2044    ///
2045    /// `ids` that are present in the consumer group's PEL are moved to the
2046    /// head of the PEL and marked as unowned (last consumer is set to an
2047    /// empty string), so they are prioritized over idle pending messages on
2048    /// the next `XREADGROUP ... CLAIM`. `ids` not present in the PEL are
2049    /// silently skipped; the returned count reflects only ids actually NACKed.
2050    ///
2051    /// `options` carries the required NACK mode, which selects how the per-message
2052    /// delivery counter is adjusted. See [`streams::StreamNackMode`] and [`streams::StreamNackOptions`].
2053    ///
2054    /// ```no_run
2055    /// use redis::{Commands, RedisResult};
2056    /// use redis::streams::{StreamNackMode, StreamNackOptions};
2057    /// let client = redis::Client::open("redis://127.0.0.1/0").unwrap();
2058    /// let mut con = client.get_connection().unwrap();
2059    ///
2060    /// let opts = StreamNackOptions::new(StreamNackMode::Fail);
2061    /// let nacked: RedisResult<usize> = con.xnack("k1", "g1", &["1-1", "1-2"], &opts);
2062    /// ```
2063    ///
2064    /// ```text
2065    /// XNACK <key> <group> <SILENT|FAIL|FATAL> IDS <numids> <id> [<id> ...]
2066    /// ```
2067    /// [Redis Docs](https://redis.io/commands/XNACK)
2068    #[cfg(feature = "streams")]
2069    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2070    fn xnack<K: ToSingleRedisArg, G: ToSingleRedisArg, ID: ToSingleRedisArg>(
2071        key: K,
2072        group: G,
2073        ids: &'a [ID],
2074        options: &'a streams::StreamNackOptions
2075    ) -> (usize) {
2076        cmd("XNACK")
2077            .arg(key)
2078            .arg(group)
2079            .arg(options)
2080            .arg("IDS")
2081            .arg(ids.len())
2082            .arg(ids)
2083            .take()
2084    }
2085
2086
2087    /// Add a stream message by `key`. Use `*` as the `id` for the current timestamp.
2088    ///
2089    /// ```text
2090    /// XADD key <ID or *> [field value] [field value] ...
2091    /// ```
2092    /// [Redis Docs](https://redis.io/commands/XADD)
2093    #[cfg(feature = "streams")]
2094    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2095    fn xadd<K: ToRedisArgs, ID: ToRedisArgs, F: ToRedisArgs, V: ToRedisArgs>(
2096        key: K,
2097        id: ID,
2098        items: &'a [(F, V)]
2099    ) -> (Option<String>) {
2100        cmd("XADD").arg(key).arg(id).arg(items).take()
2101    }
2102
2103
2104    /// BTreeMap variant for adding a stream message by `key`.
2105    /// Use `*` as the `id` for the current timestamp.
2106    ///
2107    /// ```text
2108    /// XADD key <ID or *> [rust BTreeMap] ...
2109    /// ```
2110    /// [Redis Docs](https://redis.io/commands/XADD)
2111    #[cfg(feature = "streams")]
2112    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2113    fn xadd_map<K: ToRedisArgs, ID: ToRedisArgs, BTM: ToRedisArgs>(
2114        key: K,
2115        id: ID,
2116        map: BTM
2117    ) -> (Option<String>) {
2118        cmd("XADD").arg(key).arg(id).arg(map).take()
2119    }
2120
2121
2122    /// Add a stream message with options.
2123    ///
2124    /// Items can be any list type, e.g.
2125    /// ```rust
2126    /// // static items
2127    /// let items = &[("key", "val"), ("key2", "val2")];
2128    /// # use std::collections::BTreeMap;
2129    /// // A map (Can be BTreeMap, HashMap, etc)
2130    /// let mut map: BTreeMap<&str, &str> = BTreeMap::new();
2131    /// map.insert("ab", "cd");
2132    /// map.insert("ef", "gh");
2133    /// map.insert("ij", "kl");
2134    /// ```
2135    ///
2136    /// Supports idempotent message production for preventing duplicate entries.
2137    ///
2138    /// [Idempotency Docs](https://redis.io/docs/latest/develop/data-types/streams/idempotency/)
2139    /// See [`streams::StreamAddOptions::idmp`] and [`streams::StreamAddOptions::idmpauto`] for more details.
2140    ///
2141    /// ```text
2142    /// XADD key [NOMKSTREAM] [KEEPREF | DELREF | ACKED] [IDMPAUTO pid | IDMP pid iid] [<MAXLEN|MINID> [~|=] threshold [LIMIT count]] <* | ID> field value [field value]  ...
2143    /// ```
2144    /// [Redis Docs](https://redis.io/commands/XADD)
2145    #[cfg(feature = "streams")]
2146    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2147    fn xadd_options<
2148        K: ToRedisArgs, ID: ToRedisArgs, I: ToRedisArgs
2149    >(
2150        key: K,
2151        id: ID,
2152        items: I,
2153        options: &'a streams::StreamAddOptions
2154    ) -> (Option<String>) {
2155        cmd("XADD")            .arg(key)
2156                        .arg(options)
2157                        .arg(id)
2158                        .arg(items)
2159            .take()
2160    }
2161
2162
2163    /// Add a stream message while capping the stream at a maxlength.
2164    ///
2165    /// ```text
2166    /// XADD key [MAXLEN [~|=] <count>] <ID or *> [field value] [field value] ...
2167    /// ```
2168    /// [Redis Docs](https://redis.io/commands/XADD)
2169    #[cfg(feature = "streams")]
2170    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2171    fn xadd_maxlen<
2172        K: ToSingleRedisArg,
2173        ID: ToRedisArgs,
2174        F: ToRedisArgs,
2175        V: ToRedisArgs
2176    >(
2177        key: K,
2178        maxlen: streams::StreamMaxlen,
2179        id: ID,
2180        items: &'a [(F, V)]
2181    ) -> (Option<String>) {
2182        cmd("XADD")            .arg(key)
2183                        .arg(maxlen)
2184                        .arg(id)
2185                        .arg(items)
2186            .take()
2187    }
2188
2189
2190    /// BTreeMap variant for adding a stream message while capping the stream at a maxlength.
2191    ///
2192    /// ```text
2193    /// XADD key [MAXLEN [~|=] <count>] <ID or *> [rust BTreeMap] ...
2194    /// ```
2195    /// [Redis Docs](https://redis.io/commands/XADD)
2196    #[cfg(feature = "streams")]
2197    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2198    fn xadd_maxlen_map<K: ToSingleRedisArg, ID: ToRedisArgs, BTM: ToRedisArgs>(
2199        key: K,
2200        maxlen: streams::StreamMaxlen,
2201        id: ID,
2202        map: BTM
2203    ) -> (Option<String>) {
2204        cmd("XADD")            .arg(key)
2205                        .arg(maxlen)
2206                        .arg(id)
2207                        .arg(map)
2208            .take()
2209    }
2210
2211    /// Perform a combined xpending and xclaim flow.
2212    ///
2213    /// ```no_run
2214    /// use redis::{Connection,Commands,RedisResult};
2215    /// use redis::streams::{StreamAutoClaimOptions, StreamAutoClaimReply};
2216    /// let client = redis::Client::open("redis://127.0.0.1/0").unwrap();
2217    /// let mut con = client.get_connection().unwrap();
2218    ///
2219    /// let opts = StreamAutoClaimOptions::default();
2220    /// let results : RedisResult<StreamAutoClaimReply> = con.xautoclaim_options("k1", "g1", "c1", 10, "0-0", opts);
2221    /// ```
2222    ///
2223    /// ```text
2224    /// XAUTOCLAIM <key> <group> <consumer> <min-idle-time> <start> [COUNT <count>] [JUSTID]
2225    /// ```
2226    /// [Redis Docs](https://redis.io/commands/XAUTOCLAIM)
2227    #[cfg(feature = "streams")]
2228    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2229    fn xautoclaim_options<
2230        K: ToSingleRedisArg,
2231        G: ToRedisArgs,
2232        C: ToRedisArgs,
2233        MIT: ToRedisArgs,
2234        S: ToRedisArgs
2235    >(
2236        key: K,
2237        group: G,
2238        consumer: C,
2239        min_idle_time: MIT,
2240        start: S,
2241        options: streams::StreamAutoClaimOptions
2242    ) -> (streams::StreamAutoClaimReply) {
2243        cmd("XAUTOCLAIM")            .arg(key)
2244                        .arg(group)
2245                        .arg(consumer)
2246                        .arg(min_idle_time)
2247                        .arg(start)
2248                        .arg(options)
2249            .take()
2250    }
2251
2252    /// Claim pending, unacked messages, after some period of time,
2253    /// currently checked out by another consumer.
2254    ///
2255    /// This method only accepts the must-have arguments for claiming messages.
2256    /// If optional arguments are required, see `xclaim_options` below.
2257    ///
2258    /// ```text
2259    /// XCLAIM <key> <group> <consumer> <min-idle-time> [<ID-1> <ID-2>]
2260    /// ```
2261    /// [Redis Docs](https://redis.io/commands/XCLAIM)
2262    #[cfg(feature = "streams")]
2263    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2264    fn xclaim<K: ToSingleRedisArg, G: ToRedisArgs, C: ToRedisArgs, MIT: ToRedisArgs, ID: ToRedisArgs>(
2265        key: K,
2266        group: G,
2267        consumer: C,
2268        min_idle_time: MIT,
2269        ids: &'a [ID]
2270    ) -> (streams::StreamClaimReply) {
2271        cmd("XCLAIM")            .arg(key)
2272                        .arg(group)
2273                        .arg(consumer)
2274                        .arg(min_idle_time)
2275                        .arg(ids)
2276            .take()
2277    }
2278
2279    /// This is the optional arguments version for claiming unacked, pending messages
2280    /// currently checked out by another consumer.
2281    ///
2282    /// ```no_run
2283    /// use redis::{Connection,Commands,RedisResult};
2284    /// use redis::streams::{StreamClaimOptions,StreamClaimReply};
2285    /// let client = redis::Client::open("redis://127.0.0.1/0").unwrap();
2286    /// let mut con = client.get_connection().unwrap();
2287    ///
2288    /// // Claim all pending messages for key "k1",
2289    /// // from group "g1", checked out by consumer "c1"
2290    /// // for 10ms with RETRYCOUNT 2 and FORCE
2291    ///
2292    /// let opts = StreamClaimOptions::default()
2293    ///     .with_force()
2294    ///     .retry(2);
2295    /// let results: RedisResult<StreamClaimReply> =
2296    ///     con.xclaim_options("k1", "g1", "c1", 10, &["0"], opts);
2297    ///
2298    /// // All optional arguments return a `Result<StreamClaimReply>` with one exception:
2299    /// // Passing JUSTID returns only the message `id` and omits the HashMap for each message.
2300    ///
2301    /// let opts = StreamClaimOptions::default()
2302    ///     .with_justid();
2303    /// let results: RedisResult<Vec<String>> =
2304    ///     con.xclaim_options("k1", "g1", "c1", 10, &["0"], opts);
2305    /// ```
2306    ///
2307    /// ```text
2308    /// XCLAIM <key> <group> <consumer> <min-idle-time> <ID-1> <ID-2>
2309    ///     [IDLE <milliseconds>] [TIME <mstime>] [RETRYCOUNT <count>]
2310    ///     [FORCE] [JUSTID] [LASTID <lastid>]
2311    /// ```
2312    /// [Redis Docs](https://redis.io/commands/XCLAIM)
2313    #[cfg(feature = "streams")]
2314    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2315    fn xclaim_options<
2316        K: ToSingleRedisArg,
2317        G: ToRedisArgs,
2318        C: ToRedisArgs,
2319        MIT: ToRedisArgs,
2320        ID: ToRedisArgs
2321    >(
2322        key: K,
2323        group: G,
2324        consumer: C,
2325        min_idle_time: MIT,
2326        ids: &'a [ID],
2327        options: streams::StreamClaimOptions
2328    ) -> Generic {
2329        cmd("XCLAIM")            .arg(key)
2330                        .arg(group)
2331                        .arg(consumer)
2332                        .arg(min_idle_time)
2333                        .arg(ids)
2334                        .arg(options)
2335            .take()
2336    }
2337
2338
2339    /// Deletes a list of `id`s for a given stream `key`.
2340    ///
2341    /// ```text
2342    /// XDEL <key> [<ID1> <ID2> ... <IDN>]
2343    /// ```
2344    /// [Redis Docs](https://redis.io/commands/XDEL)
2345    #[cfg(feature = "streams")]
2346    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2347    fn xdel<K: ToSingleRedisArg, ID: ToRedisArgs>(
2348        key: K,
2349        ids: &'a [ID]
2350    ) -> (usize) {
2351        cmd("XDEL").arg(key).arg(ids).take()
2352    }
2353
2354    /// An extension of the Streams `XDEL` command that provides finer control over how message entries are deleted with respect to consumer groups.
2355    #[cfg(feature = "streams")]
2356    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2357    fn xdel_ex<K: ToRedisArgs, ID: ToRedisArgs>(key: K, ids: &'a [ID], options: streams::StreamDeletionPolicy) -> (Vec<streams::XDelExStatusCode>) {
2358        cmd("XDELEX").arg(key).arg(options).arg("IDS").arg(ids.len()).arg(ids).take()
2359    }
2360
2361    /// A combination of `XACK` and `XDEL` that acknowledges and attempts to delete a list of `ids` for a given stream `key` and consumer `group`.
2362    #[cfg(feature = "streams")]
2363    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2364    fn xack_del<K: ToRedisArgs, G: ToRedisArgs, ID: ToRedisArgs>(key: K, group: G, ids: &'a [ID], options: streams::StreamDeletionPolicy) -> (Vec<streams::XAckDelStatusCode>) {
2365        cmd("XACKDEL").arg(key).arg(group).arg(options).arg("IDS").arg(ids.len()).arg(ids).take()
2366    }
2367
2368    /// This command is used for creating a consumer `group`. It expects the stream key
2369    /// to already exist. Otherwise, use `xgroup_create_mkstream` if it doesn't.
2370    /// The `id` is the starting message id all consumers should read from. Use `$` If you want
2371    /// all consumers to read from the last message added to stream.
2372    ///
2373    /// ```text
2374    /// XGROUP CREATE <key> <groupname> <id or $>
2375    /// ```
2376    /// [Redis Docs](https://redis.io/commands/XGROUP)
2377    #[cfg(feature = "streams")]
2378    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2379    fn xgroup_create<K: ToRedisArgs, G: ToRedisArgs, ID: ToRedisArgs>(
2380        key: K,
2381        group: G,
2382        id: ID
2383    ) -> () {
2384        cmd("XGROUP")            .arg("CREATE")
2385                        .arg(key)
2386                        .arg(group)
2387                        .arg(id)
2388            .take()
2389    }
2390
2391    /// This creates a `consumer` explicitly (vs implicit via XREADGROUP)
2392    /// for given stream `key.
2393    ///
2394    /// The return value is either a 0 or a 1 for the number of consumers created
2395    /// 0 means the consumer already exists
2396    ///
2397    /// ```text
2398    /// XGROUP CREATECONSUMER <key> <groupname> <consumername>
2399    /// ```
2400    /// [Redis Docs](https://redis.io/commands/XGROUP)
2401    #[cfg(feature = "streams")]
2402    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2403    fn xgroup_createconsumer<K: ToRedisArgs, G: ToRedisArgs, C: ToRedisArgs>(
2404        key: K,
2405        group: G,
2406        consumer: C
2407    ) -> bool {
2408        cmd("XGROUP")            .arg("CREATECONSUMER")
2409                        .arg(key)
2410                        .arg(group)
2411                        .arg(consumer)
2412            .take()
2413    }
2414
2415    /// This is the alternate version for creating a consumer `group`
2416    /// which makes the stream if it doesn't exist.
2417    ///
2418    /// ```text
2419    /// XGROUP CREATE <key> <groupname> <id or $> [MKSTREAM]
2420    /// ```
2421    /// [Redis Docs](https://redis.io/commands/XGROUP)
2422    #[cfg(feature = "streams")]
2423    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2424    fn xgroup_create_mkstream<
2425        K: ToRedisArgs,
2426        G: ToRedisArgs,
2427        ID: ToRedisArgs
2428    >(
2429        key: K,
2430        group: G,
2431        id: ID
2432    ) -> () {
2433        cmd("XGROUP")            .arg("CREATE")
2434                        .arg(key)
2435                        .arg(group)
2436                        .arg(id)
2437                        .arg("MKSTREAM")
2438            .take()
2439    }
2440
2441
2442    /// Alter which `id` you want consumers to begin reading from an existing
2443    /// consumer `group`.
2444    ///
2445    /// ```text
2446    /// XGROUP SETID <key> <groupname> <id or $>
2447    /// ```
2448    /// [Redis Docs](https://redis.io/commands/XGROUP)
2449    #[cfg(feature = "streams")]
2450    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2451    fn xgroup_setid<K: ToRedisArgs, G: ToRedisArgs, ID: ToRedisArgs>(
2452        key: K,
2453        group: G,
2454        id: ID
2455    ) -> () {
2456        cmd("XGROUP")
2457            .arg("SETID")
2458            .arg(key)
2459            .arg(group)
2460            .arg(id)
2461            .take()
2462    }
2463
2464
2465    /// Destroy an existing consumer `group` for a given stream `key`
2466    ///
2467    /// ```text
2468    /// XGROUP SETID <key> <groupname> <id or $>
2469    /// ```
2470    /// [Redis Docs](https://redis.io/commands/XGROUP)
2471    #[cfg(feature = "streams")]
2472    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2473    fn xgroup_destroy<K: ToRedisArgs, G: ToRedisArgs>(
2474        key: K,
2475        group: G
2476    ) -> bool {
2477        cmd("XGROUP").arg("DESTROY").arg(key).arg(group).take()
2478    }
2479
2480    /// This deletes a `consumer` from an existing consumer `group`
2481    /// for given stream `key.
2482    ///
2483    /// ```text
2484    /// XGROUP DELCONSUMER <key> <groupname> <consumername>
2485    /// ```
2486    /// [Redis Docs](https://redis.io/commands/XGROUP)
2487    #[cfg(feature = "streams")]
2488    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2489    fn xgroup_delconsumer<K: ToRedisArgs, G: ToRedisArgs, C: ToRedisArgs>(
2490        key: K,
2491        group: G,
2492        consumer: C
2493    ) -> usize {
2494        cmd("XGROUP")
2495            .arg("DELCONSUMER")
2496            .arg(key)
2497            .arg(group)
2498            .arg(consumer)
2499            .take()
2500    }
2501
2502
2503    /// This returns all info details about
2504    /// which consumers have read messages for given consumer `group`.
2505    /// Take note of the StreamInfoConsumersReply return type.
2506    ///
2507    /// *It's possible this return value might not contain new fields
2508    /// added by Redis in future versions.*
2509    ///
2510    /// ```text
2511    /// XINFO CONSUMERS <key> <group>
2512    /// ```
2513    /// [Redis Docs](https://redis.io/commands/XINFO")
2514    #[cfg(feature = "streams")]
2515    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2516    fn xinfo_consumers<K: ToRedisArgs, G: ToRedisArgs>(
2517        key: K,
2518        group: G
2519    ) -> (streams::StreamInfoConsumersReply) {
2520        cmd("XINFO")
2521            .arg("CONSUMERS")
2522            .arg(key)
2523            .arg(group)
2524            .take()
2525    }
2526
2527
2528    /// Returns all consumer `group`s created for a given stream `key`.
2529    /// Take note of the StreamInfoGroupsReply return type.
2530    ///
2531    /// *It's possible this return value might not contain new fields
2532    /// added by Redis in future versions.*
2533    ///
2534    /// ```text
2535    /// XINFO GROUPS <key>
2536    /// ```
2537    /// [Redis Docs](https://redis.io/commands/XINFO-GROUPS)
2538    #[cfg(feature = "streams")]
2539    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2540    fn xinfo_groups<K: ToRedisArgs>(key: K) -> (streams::StreamInfoGroupsReply) {
2541        cmd("XINFO").arg("GROUPS").arg(key).take()
2542    }
2543
2544
2545    /// Returns info about high-level stream details
2546    /// (first & last message `id`, length, number of groups, etc.)
2547    /// Take note of the StreamInfoStreamReply return type.
2548    ///
2549    /// *It's possible this return value might not contain new fields added by Redis in future versions,
2550    /// such as the idempotency fields introduced in Redis 8.6. For IDMP tracking statistics, use
2551    /// [`xinfo_stream_with_idempotency`](Self::xinfo_stream_with_idempotency).*
2552    ///
2553    /// ```text
2554    /// XINFO STREAM <key>
2555    /// ```
2556    /// [Redis Docs](https://redis.io/commands/XINFO-STREAM)
2557    #[cfg(feature = "streams")]
2558    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2559    fn xinfo_stream<K: ToRedisArgs>(key: K) -> (streams::StreamInfoStreamReply) {
2560        cmd("XINFO").arg("STREAM").arg(key).take()
2561    }
2562
2563    // TODO: Remove this function when creating the next major release.
2564    /// Returns stream info with idempotency tracking statistics (Redis 8.6+).
2565    ///
2566    /// This command returns [`StreamInfoStreamReplyWithIdempotency`](streams::StreamInfoStreamReplyWithIdempotency)
2567    /// which composes [`StreamInfoStreamReply`](streams::StreamInfoStreamReply) (accessible via the `base` field)
2568    /// and adds IDMP (Idempotent Message Processing) tracking fields.
2569    ///
2570    /// ```text
2571    /// XINFO STREAM <key>
2572    /// ```
2573    /// [Redis Docs](https://redis.io/commands/XINFO-STREAM)
2574    #[cfg(feature = "streams")]
2575    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2576    fn xinfo_stream_with_idempotency<K: ToRedisArgs>(key: K) -> (streams::StreamInfoStreamReplyWithIdempotency) {
2577        cmd("XINFO").arg("STREAM").arg(key).take()
2578    }
2579
2580    /// Returns the number of messages for a given stream `key`.
2581    ///
2582    /// ```text
2583    /// XLEN <key>
2584    /// ```
2585    #[cfg(feature = "streams")]
2586    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2587    /// [Redis Docs](https://redis.io/commands/XLEN)
2588    fn xlen<K: ToRedisArgs>(key: K) -> usize {
2589        cmd("XLEN").arg(key).take()
2590    }
2591
2592
2593    /// This is a basic version of making XPENDING command calls which only
2594    /// passes a stream `key` and consumer `group` and it
2595    /// returns details about which consumers have pending messages
2596    /// that haven't been acked.
2597    ///
2598    /// You can use this method along with
2599    /// `xclaim` or `xclaim_options` for determining which messages
2600    /// need to be retried.
2601    ///
2602    /// Take note of the StreamPendingReply return type.
2603    ///
2604    /// ```text
2605    /// XPENDING <key> <group> [<start> <stop> <count> [<consumer>]]
2606    /// ```
2607    /// [Redis Docs](https://redis.io/commands/XPENDING)
2608    #[cfg(feature = "streams")]
2609    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2610    fn xpending<K: ToRedisArgs, G: ToRedisArgs>(
2611        key: K,
2612        group: G
2613    ) -> (streams::StreamPendingReply) {
2614        cmd("XPENDING").arg(key).arg(group).take()
2615    }
2616
2617
2618    /// This XPENDING version returns a list of all messages over the range.
2619    /// You can use this for paginating pending messages (but without the message HashMap).
2620    ///
2621    /// Start and end follow the same rules `xrange` args. Set start to `-`
2622    /// and end to `+` for the entire stream.
2623    ///
2624    /// Take note of the StreamPendingCountReply return type.
2625    ///
2626    /// ```text
2627    /// XPENDING <key> <group> <start> <stop> <count>
2628    /// ```
2629    /// [Redis Docs](https://redis.io/commands/XPENDING)
2630    #[cfg(feature = "streams")]
2631    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2632    fn xpending_count<
2633        K: ToRedisArgs,
2634        G: ToRedisArgs,
2635        S: ToRedisArgs,
2636        E: ToRedisArgs,
2637        C: ToRedisArgs
2638    >(
2639        key: K,
2640        group: G,
2641        start: S,
2642        end: E,
2643        count: C
2644    ) -> (streams::StreamPendingCountReply) {
2645        cmd("XPENDING")
2646            .arg(key)
2647            .arg(group)
2648            .arg(start)
2649            .arg(end)
2650            .arg(count)
2651            .take()
2652    }
2653
2654
2655    /// An alternate version of `xpending_count` which filters by `consumer` name.
2656    ///
2657    /// Start and end follow the same rules `xrange` args. Set start to `-`
2658    /// and end to `+` for the entire stream.
2659    ///
2660    /// Take note of the StreamPendingCountReply return type.
2661    ///
2662    /// ```text
2663    /// XPENDING <key> <group> <start> <stop> <count> <consumer>
2664    /// ```
2665    /// [Redis Docs](https://redis.io/commands/XPENDING)
2666    #[cfg(feature = "streams")]
2667    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2668    fn xpending_consumer_count<
2669        K: ToRedisArgs,
2670        G: ToRedisArgs,
2671        S: ToRedisArgs,
2672        E: ToRedisArgs,
2673        C: ToRedisArgs,
2674        CN: ToRedisArgs
2675    >(
2676        key: K,
2677        group: G,
2678        start: S,
2679        end: E,
2680        count: C,
2681        consumer: CN
2682    ) -> (streams::StreamPendingCountReply) {
2683        cmd("XPENDING")
2684            .arg(key)
2685            .arg(group)
2686            .arg(start)
2687            .arg(end)
2688            .arg(count)
2689            .arg(consumer)
2690            .take()
2691    }
2692
2693    /// Returns a range of messages in a given stream `key`.
2694    ///
2695    /// Set `start` to `-` to begin at the first message.
2696    /// Set `end` to `+` to end the most recent message.
2697    /// You can pass message `id` to both `start` and `end`.
2698    ///
2699    /// Take note of the StreamRangeReply return type.
2700    ///
2701    /// ```text
2702    /// XRANGE key start end
2703    /// ```
2704    /// [Redis Docs](https://redis.io/commands/XRANGE)
2705    #[cfg(feature = "streams")]
2706    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2707    fn xrange<K: ToRedisArgs, S: ToRedisArgs, E: ToRedisArgs>(
2708        key: K,
2709        start: S,
2710        end: E
2711    ) -> (streams::StreamRangeReply) {
2712        cmd("XRANGE").arg(key).arg(start).arg(end).take()
2713    }
2714
2715
2716    /// A helper method for automatically returning all messages in a stream by `key`.
2717    /// **Use with caution!**
2718    ///
2719    /// ```text
2720    /// XRANGE key - +
2721    /// ```
2722    /// [Redis Docs](https://redis.io/commands/XRANGE)
2723    #[cfg(feature = "streams")]
2724    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2725    fn xrange_all<K: ToRedisArgs>(key: K) -> (streams::StreamRangeReply) {
2726        cmd("XRANGE").arg(key).arg("-").arg("+").take()
2727    }
2728
2729
2730    /// A method for paginating a stream by `key`.
2731    ///
2732    /// ```text
2733    /// XRANGE key start end [COUNT <n>]
2734    /// ```
2735    /// [Redis Docs](https://redis.io/commands/XRANGE)
2736    #[cfg(feature = "streams")]
2737    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2738    fn xrange_count<K: ToRedisArgs, S: ToRedisArgs, E: ToRedisArgs, C: ToRedisArgs>(
2739        key: K,
2740        start: S,
2741        end: E,
2742        count: C
2743    ) -> (streams::StreamRangeReply) {
2744        cmd("XRANGE")
2745            .arg(key)
2746            .arg(start)
2747            .arg(end)
2748            .arg("COUNT")
2749            .arg(count)
2750            .take()
2751    }
2752
2753
2754    /// Read a list of `id`s for each stream `key`.
2755    /// This is the basic form of reading streams.
2756    /// For more advanced control, like blocking, limiting, or reading by consumer `group`,
2757    /// see `xread_options`.
2758    ///
2759    /// ```text
2760    /// XREAD STREAMS key_1 key_2 ... key_N ID_1 ID_2 ... ID_N
2761    /// ```
2762    /// [Redis Docs](https://redis.io/commands/XREAD)
2763    #[cfg(feature = "streams")]
2764    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2765    fn xread<K: ToRedisArgs, ID: ToRedisArgs>(
2766        keys: &'a [K],
2767        ids: &'a [ID]
2768    ) -> (Option<streams::StreamReadReply>) {
2769        cmd("XREAD").arg("STREAMS").arg(keys).arg(ids).take()
2770    }
2771
2772    /// This method handles setting optional arguments for
2773    /// `XREAD` or `XREADGROUP` Redis commands.
2774    /// ```no_run
2775    /// use redis::{Connection,RedisResult,Commands};
2776    /// use redis::streams::{StreamReadOptions,StreamReadReply};
2777    /// let client = redis::Client::open("redis://127.0.0.1/0").unwrap();
2778    /// let mut con = client.get_connection().unwrap();
2779    ///
2780    /// // Read 10 messages from the start of the stream,
2781    /// // without registering as a consumer group.
2782    ///
2783    /// let opts = StreamReadOptions::default()
2784    ///     .count(10);
2785    /// let results: RedisResult<StreamReadReply> =
2786    ///     con.xread_options(&["k1"], &["0"], &opts);
2787    ///
2788    /// // Read all undelivered messages for a given
2789    /// // consumer group. Be advised: the consumer group must already
2790    /// // exist before making this call. Also note: we're passing
2791    /// // '>' as the id here, which means all undelivered messages.
2792    ///
2793    /// let opts = StreamReadOptions::default()
2794    ///     .group("group-1", "consumer-1");
2795    /// let results: RedisResult<StreamReadReply> =
2796    ///     con.xread_options(&["k1"], &[">"], &opts);
2797    /// ```
2798    ///
2799    /// ```text
2800    /// XREAD [BLOCK <milliseconds>] [COUNT <count>]
2801    ///     STREAMS key_1 key_2 ... key_N
2802    ///     ID_1 ID_2 ... ID_N
2803    ///
2804    /// XREADGROUP [GROUP group-name consumer-name] [BLOCK <milliseconds>] [COUNT <count>] [NOACK]
2805    ///     STREAMS key_1 key_2 ... key_N
2806    ///     ID_1 ID_2 ... ID_N
2807    /// ```
2808    /// [Redis Docs](https://redis.io/commands/XREAD)
2809    #[cfg(feature = "streams")]
2810    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2811    fn xread_options<K: ToRedisArgs, ID: ToRedisArgs>(
2812        keys: &'a [K],
2813        ids: &'a [ID],
2814        options: &'a streams::StreamReadOptions
2815    ) -> (Option<streams::StreamReadReply>) {
2816        cmd(if options.read_only() {
2817            "XREAD"
2818        } else {
2819            "XREADGROUP"
2820        })
2821        .arg(options)
2822        .arg("STREAMS")
2823        .arg(keys)
2824        .arg(ids)
2825        .take()
2826    }
2827
2828    /// This is the reverse version of `xrange`.
2829    /// The same rules apply for `start` and `end` here.
2830    ///
2831    /// ```text
2832    /// XREVRANGE key end start
2833    /// ```
2834    /// [Redis Docs](https://redis.io/commands/XREVRANGE)
2835    #[cfg(feature = "streams")]
2836    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2837    fn xrevrange<K: ToRedisArgs, E: ToRedisArgs, S: ToRedisArgs>(
2838        key: K,
2839        end: E,
2840        start: S
2841    ) -> (streams::StreamRangeReply) {
2842        cmd("XREVRANGE").arg(key).arg(end).arg(start).take()
2843    }
2844
2845    /// This is the reverse version of `xrange_all`.
2846    /// The same rules apply for `start` and `end` here.
2847    ///
2848    /// ```text
2849    /// XREVRANGE key + -
2850    /// ```
2851    /// [Redis Docs](https://redis.io/commands/XREVRANGE)
2852    #[cfg(feature = "streams")]
2853    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2854    fn xrevrange_all<K: ToRedisArgs>(key: K) -> (streams::StreamRangeReply) {
2855        cmd("XREVRANGE").arg(key).arg("+").arg("-").take()
2856    }
2857
2858    /// This is the reverse version of `xrange_count`.
2859    /// The same rules apply for `start` and `end` here.
2860    ///
2861    /// ```text
2862    /// XREVRANGE key end start [COUNT <n>]
2863    /// ```
2864    /// [Redis Docs](https://redis.io/commands/XREVRANGE)
2865    #[cfg(feature = "streams")]
2866    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2867    fn xrevrange_count<K: ToRedisArgs, E: ToRedisArgs, S: ToRedisArgs, C: ToRedisArgs>(
2868        key: K,
2869        end: E,
2870        start: S,
2871        count: C
2872    ) -> (streams::StreamRangeReply) {
2873        cmd("XREVRANGE")
2874            .arg(key)
2875            .arg(end)
2876            .arg(start)
2877            .arg("COUNT")
2878            .arg(count)
2879            .take()
2880    }
2881
2882    /// Trim a stream `key` to a MAXLEN count.
2883    ///
2884    /// ```text
2885    /// XTRIM <key> MAXLEN [~|=] <count>  (Same as XADD MAXLEN option)
2886    /// ```
2887    /// [Redis Docs](https://redis.io/commands/XTRIM)
2888    #[cfg(feature = "streams")]
2889    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2890    fn xtrim<K: ToRedisArgs>(
2891        key: K,
2892        maxlen: streams::StreamMaxlen
2893    ) -> usize {
2894        cmd("XTRIM").arg(key).arg(maxlen).take()
2895    }
2896
2897     /// Trim a stream `key` with full options
2898     ///
2899     /// ```text
2900     /// XTRIM <key> <MAXLEN|MINID> [~|=] <threshold> [LIMIT <count>]  (Same as XADD MAXID|MINID options) [KEEPREF | DELREF | ACKED]
2901     /// ```
2902     /// [Redis Docs](https://redis.io/commands/XTRIM)
2903    #[cfg(feature = "streams")]
2904    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2905    fn xtrim_options<K: ToRedisArgs>(
2906        key: K,
2907        options: &'a streams::StreamTrimOptions
2908    ) -> usize {
2909        cmd("XTRIM").arg(key).arg(options).take()
2910    }
2911
2912    /// Configure idempotency parameters for a stream
2913    ///
2914    /// Sets the IDMP configuration parameters for a stream. This command configures
2915    /// how long idempotent IDs are retained and the maximum number of idempotent IDs
2916    /// tracked per producer.
2917    ///
2918    /// **Note:** Calling XCFGSET clears all existing producer IDMP maps for the stream.
2919    ///
2920    /// ```text
2921    /// XCFGSET key [IDMP-DURATION idmp-duration] [IDMP-MAXSIZE idmp-maxsize]
2922    /// ```
2923    /// [Redis Docs](https://redis.io/commands/XCFGSET)
2924    ///
2925    /// # Example
2926    /// ```no_run
2927    /// use redis::{Commands, streams::StreamConfigOptions};
2928    /// # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
2929    /// # let mut con = client.get_connection().unwrap();
2930    ///
2931    /// // Configure stream with 5 minute duration and max 1000 IDs per producer
2932    /// // Valid ranges: IDMP-DURATION (1-86400), IDMP-MAXSIZE (1-10000)
2933    /// let opts = StreamConfigOptions::with_idempotency_seconds(300)
2934    ///     .unwrap()
2935    ///     .idempotency_maxsize(1000)
2936    ///     .unwrap();
2937    /// let result: String = con.xcfgset("key", &opts).unwrap();
2938    /// assert_eq!(result, "OK");
2939    /// ```
2940    #[cfg(feature = "streams")]
2941    #[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
2942    fn xcfgset<K: ToRedisArgs>(
2943        key: K,
2944        options: &'a streams::StreamConfigOptions
2945    ) -> String {
2946        cmd("XCFGSET").arg(key).arg(options).take()
2947    }
2948
2949    // script commands
2950
2951    /// Load a script.
2952    ///
2953    /// See [`invoke_script`](Self::invoke_script) to actually run the scripts.
2954    #[cfg_attr(feature = "script", doc = r##"
2955
2956# Examples:
2957
2958```rust,no_run
2959# fn do_something() -> redis::RedisResult<()> {
2960# let client = redis::Client::open("redis://127.0.0.1/").unwrap();
2961# let mut con = client.get_connection().unwrap();
2962let script = redis::Script::new(r"
2963    return tonumber(ARGV[1]) + tonumber(ARGV[2]);
2964");
2965let (load_res, invok_res): (String, isize) = redis::pipe()
2966    .load_script(&script)
2967    .invoke_script(script.arg(1).arg(2))
2968    .query(&mut con)?;
2969
2970assert_eq!(load_res, "1ca80f2366c125a7c43519ce241d5c24c2b64023");
2971assert_eq!(invok_res, 3);
2972# Ok(()) }
2973```
2974"##)]
2975    #[cfg(feature = "script")]
2976    #[cfg_attr(docsrs, doc(cfg(feature = "script")))]
2977    fn load_script<>(script: &'a crate::Script) -> Generic {
2978        script.load_cmd().take()
2979    }
2980
2981    /// Invoke a prepared script.
2982    ///
2983    /// Note: Unlike[`ScriptInvocation::invoke`](crate::ScriptInvocation::invoke), this function
2984    /// does _not_ automatically load the script. If the invoked script did not get loaded beforehand, you
2985    /// need to manually load it (e.g.: using [`load_script`](Self::load_script) or
2986    /// [`ScriptInvocation::load`](crate::ScriptInvocation::load)). Otherwise this command will fail.
2987    #[cfg_attr(feature = "script", doc = r##"
2988
2989# Examples:
2990
2991```rust,no_run
2992# fn do_something() -> redis::RedisResult<()> {
2993# let client = redis::Client::open("redis://127.0.0.1/").unwrap();
2994# let mut con = client.get_connection().unwrap();
2995let script = redis::Script::new(r"
2996    return tonumber(ARGV[1]) + tonumber(ARGV[2]);
2997");
2998let (load_res, invok_1_res, invok_2_res): (String, isize, isize) = redis::pipe()
2999    .load_script(&script)
3000    .invoke_script(script.arg(1).arg(2))
3001    .invoke_script(script.arg(2).arg(3))
3002    .query(&mut con)?;
3003
3004assert_eq!(load_res, "1ca80f2366c125a7c43519ce241d5c24c2b64023");
3005assert_eq!(invok_1_res, 3);
3006assert_eq!(invok_2_res, 5);
3007# Ok(()) }
3008```
3009"##)]
3010    #[cfg(feature = "script")]
3011    #[cfg_attr(docsrs, doc(cfg(feature = "script")))]
3012    fn invoke_script<>(invocation: &'a crate::ScriptInvocation<'a>) -> Generic {
3013        invocation.eval_cmd().take()
3014    }
3015
3016    // cleanup commands
3017
3018    /// Deletes all the keys of all databases
3019    ///
3020    /// Whether the flushing happens asynchronously or synchronously depends on the configuration
3021    /// of your Redis server.
3022    ///
3023    /// To enforce a flush mode, use [`Commands::flushall_options`].
3024    ///
3025    /// ```text
3026    /// FLUSHALL
3027    /// ```
3028    /// [Redis Docs](https://redis.io/commands/FLUSHALL)
3029    fn flushall<>() -> () {
3030        cmd("FLUSHALL").take()
3031    }
3032
3033    /// Deletes all the keys of all databases with options
3034    ///
3035    /// ```text
3036    /// FLUSHALL [ASYNC|SYNC]
3037    /// ```
3038    /// [Redis Docs](https://redis.io/commands/FLUSHALL)
3039    fn flushall_options<>(options: &'a FlushAllOptions) -> () {
3040        cmd("FLUSHALL").arg(options).take()
3041    }
3042
3043    /// Deletes all the keys of the current database
3044    ///
3045    /// Whether the flushing happens asynchronously or synchronously depends on the configuration
3046    /// of your Redis server.
3047    ///
3048    /// To enforce a flush mode, use [`Commands::flushdb_options`].
3049    ///
3050    /// ```text
3051    /// FLUSHDB
3052    /// ```
3053    /// [Redis Docs](https://redis.io/commands/FLUSHDB)
3054    fn flushdb<>() -> () {
3055        cmd("FLUSHDB").take()
3056    }
3057
3058    /// Deletes all the keys of the current database with options
3059    ///
3060    /// ```text
3061    /// FLUSHDB [ASYNC|SYNC]
3062    /// ```
3063    /// [Redis Docs](https://redis.io/commands/FLUSHDB)
3064    fn flushdb_options<>(options: &'a FlushDbOptions) -> () {
3065        cmd("FLUSHDB").arg(options).take()
3066    }
3067
3068    // Bloom filter commands
3069
3070    /// Adds an item to a Bloom filter.
3071    ///
3072    /// ```text
3073    /// BF.ADD <key> <item>
3074    /// ```
3075    ///
3076    /// [Redis Docs](https://redis.io/commands/BF.ADD)
3077    #[cfg(feature = "bloom")]
3078    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3079    fn bf_add<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, value: V) -> (bool) {
3080        cmd("BF.ADD").arg(key).arg(value).take()
3081    }
3082
3083    /// Returns the number of items in a Bloom filter.
3084    ///
3085    /// ```text
3086    /// BF.CARD <key>
3087    /// ```
3088    ///
3089    /// [Redis Docs](https://redis.io/commands/BF.CARD)
3090    #[cfg(feature = "bloom")]
3091    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3092    fn bf_card<K: ToSingleRedisArg>(key: K) -> (usize) {
3093        cmd("BF.CARD").arg(key).take()
3094    }
3095
3096    /// Checks if an item exists in a Bloom filter.
3097    ///
3098    /// ```text
3099    /// BF.EXISTS <key> <item>
3100    /// ```
3101    ///
3102    /// # Caveats
3103    ///
3104    /// If `key` is not a Bloom filter, Redis' module yields `Ok(false)` as if `item` was missing,
3105    /// while Valkey yields a `WRONGKEY` error.
3106    ///
3107    /// [Redis Docs](https://redis.io/commands/BF.EXISTS)
3108    /// [Valkey Docs](https://valkey.io/commands/bf.exists/)
3109    #[cfg(feature = "bloom")]
3110    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3111    fn bf_exists<K: ToSingleRedisArg, V: ToSingleRedisArg>(key: K, value: V) -> (bool) {
3112        cmd("BF.EXISTS").arg(key).arg(value).take()
3113    }
3114
3115    /// Returns all available information about a Bloom filter.
3116    ///
3117    /// ```text
3118    /// BF.INFO <key>
3119    /// ```
3120    ///
3121    /// [Redis Docs](https://redis.io/commands/BF.INFO)
3122    #[cfg(feature = "bloom")]
3123    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3124    fn bf_info<K: ToSingleRedisArg>(key: K) -> (std::collections::HashMap<String, crate::bloom::BloomFilterInfoTypeResponse>) {
3125        cmd("BF.INFO").arg(key).take()
3126    }
3127
3128    /// Returns specific information about a Bloom filter.
3129    ///
3130    /// ```text
3131    /// BF.INFO <key> <type>
3132    /// ```
3133    ///
3134    /// Due incompatibilities between RESP2 and RESP3, and furthermore conversion limitations of our
3135    /// framework, this function cannot return `i64` directly. Instead, it returns
3136    /// [`BloomFilterInfoTypeResponse`](crate::bloom::BloomFilterInfoTypeResponse)) which derefs to
3137    /// `i64`.
3138    ///
3139    /// [Redis Docs](https://redis.io/commands/BF.INFO)
3140    #[cfg(feature = "bloom")]
3141    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3142    fn bf_info_type<K: ToSingleRedisArg>(key: K, info_type: crate::bloom::BloomFilterInfoType) -> (crate::bloom::BloomFilterInfoTypeResponse) {
3143        cmd("BF.INFO").arg(key).arg(info_type).take()
3144    }
3145
3146    /// Adds items to a Bloom filter, creating it with default options if it does not yet exist.
3147    ///
3148    /// ```text
3149    /// BF.INSERT <key> ITEMS <item1> <item2> ...
3150    /// ```
3151    ///
3152    /// [Redis Docs](https://redis.io/commands/BF.INSERT)
3153    #[cfg(feature = "bloom")]
3154    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3155    fn bf_insert<K: ToSingleRedisArg,  V: ToRedisArgs>(
3156        key: K,
3157        items: V) -> (Vec<bool>){
3158        cmd("BF.INSERT")
3159            .arg(key)
3160            .arg("ITEMS")
3161            .arg(items)
3162            .take()
3163    }
3164
3165    /// Adds items to a Bloom filter, creating it with custom options if it does not exist yet.
3166    ///
3167    /// ```text
3168    /// BF.INSERT <key> [options] ITEMS <item1> <item2> ...
3169    /// ```
3170    ///
3171    /// [Redis Docs](https://redis.io/commands/BF.INSERT)
3172    #[cfg(feature = "bloom")]
3173    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3174    fn bf_insert_options<K: ToSingleRedisArg,  V: ToRedisArgs>(
3175        key: K,
3176        items: V,
3177        options: crate::bloom::BloomFilterInsertOptions) -> (Vec<bool>) {
3178        cmd("BF.INSERT")
3179            .arg(key)
3180            .arg(options)
3181            .arg("ITEMS")
3182            .arg(items)
3183            .take()
3184    }
3185
3186
3187    /// Restores a Bloom filter previously saved using [`bf_scandump`](Self::bf_scandump).
3188    ///
3189    /// ```text
3190    /// BF.LOADCHUNK <key> <iterator> <data>
3191    /// ```
3192    ///
3193    /// [Redis Docs](https://redis.io/commands/BF.LOADCHUNK)
3194    #[cfg(feature = "bloom")]
3195    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3196    fn bf_loadchunk<K: ToSingleRedisArg>(key: K, chunk: crate::bloom::BloomFilterDumpChunk) -> (()) {
3197        cmd("BF.LOADCHUNK").arg(key).arg(chunk.iterator).arg(chunk.data).take()
3198    }
3199
3200    /// Adds multiple items to a Bloom filter.
3201    ///
3202    /// ```text
3203    /// BF.MADD <key> <item1> <item2> ...
3204    /// ```
3205    ///
3206    /// [Redis Docs](https://redis.io/commands/BF.MADD)
3207    #[cfg(feature = "bloom")]
3208    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3209    fn bf_madd<K: ToSingleRedisArg, V: ToRedisArgs>(key: K, items: V) -> (Vec<bool>) {
3210        cmd("BF.MADD").arg(key).arg(items).take()
3211    }
3212
3213    /// Checks if an item exists in a Bloom filter.
3214    ///
3215    /// ```text
3216    /// BF.MEXISTS <key> <item1> <item2> ...
3217    /// ```
3218    ///
3219    /// # Caveats
3220    ///
3221    /// If `key` is not a Bloom filter, Redis' module yields an array of `false`s as if all items
3222    /// were missing, while Valkey yields a `WRONGTYPE` error.
3223    ///
3224    /// [Redis Docs](https://redis.io/commands/BF.MEXISTS)
3225    /// [Valkey Docs](https://valkey.io/commands/bf.mexists/)
3226    #[cfg(feature = "bloom")]
3227    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3228    fn bf_mexists<K: ToSingleRedisArg, V: ToRedisArgs>(key: K, items: &'a [V]) -> (Vec<bool>) {
3229        cmd("BF.MEXISTS").arg(key).arg(items).take()
3230    }
3231
3232    /// Creates an empty Bloom filter with default settings.
3233    ///
3234    /// ```text
3235    /// BF.RESERVE <key> <error_rate> <capacity>
3236    /// ```
3237    ///
3238    /// [Redis Docs](https://redis.io/commands/BF.RESERVE)
3239    #[cfg(feature = "bloom")]
3240    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3241    fn bf_reserve<K: ToSingleRedisArg>(key: K, err_rate: f64, capacity: usize) -> (()) {
3242        cmd("BF.RESERVE").arg(key).arg(err_rate).arg(capacity).take()
3243    }
3244
3245    /// Creates an empty Bloom filter with options.
3246    ///
3247    /// ```text
3248    /// BF.RESERVE <key> <error_rate> <capacity> [EXPANSION expansion] [NONSCALING]
3249    /// ```
3250    ///
3251    /// [Redis Docs](https://redis.io/commands/BF.RESERVE)
3252    #[cfg(feature = "bloom")]
3253    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3254    fn bf_reserve_options<K: ToSingleRedisArg, E: ToSingleRedisArg, C: ToSingleRedisArg>(key: K, err_rate: E, capacity: C, options: crate::bloom::BloomFilterScalingOptions) -> (()) {
3255      cmd("BF.RESERVE").arg(key).arg(err_rate).arg(capacity).arg(options).take()
3256    }
3257
3258    /// Begins an incremental save of the Bloom filter
3259    ///
3260    /// ```text
3261    /// BF.SCANDUMP <key> <iterator>
3262    /// ```
3263    ///
3264    /// [`BloomFilterDumpIterator`](crate::bloom::BloomFilterDumpIterator) allows to dump a Bloom
3265    /// filter in a more accessible way than manually dumping chunk by chunk.
3266    ///
3267    /// [Redis Docs](https://redis.io/commands/BF.SCANDUMP)
3268    #[cfg(feature = "bloom")]
3269    #[cfg_attr(docsrs, doc(cfg(feature = "bloom")))]
3270    fn bf_scandump<K: ToSingleRedisArg>(key: K, iterator: i64) -> (crate::bloom::BloomFilterDumpChunk) {
3271        cmd("BF.SCANDUMP").arg(key).arg(iterator).take()
3272    }
3273}
3274
3275/// Allows pubsub callbacks to stop receiving messages.
3276///
3277/// Arbitrary data may be returned from `Break`.
3278#[non_exhaustive]
3279pub enum ControlFlow<U> {
3280    /// Continues.
3281    Continue,
3282    /// Breaks with a value.
3283    Break(U),
3284}
3285
3286/// The PubSub trait allows subscribing to one or more channels
3287/// and receiving a callback whenever a message arrives.
3288///
3289/// Each method handles subscribing to the list of keys, waiting for
3290/// messages, and unsubscribing from the same list of channels once
3291/// a ControlFlow::Break is encountered.
3292///
3293/// Once (p)subscribe returns Ok(U), the connection is again safe to use
3294/// for calling other methods.
3295///
3296/// # Examples
3297///
3298/// ```rust,no_run
3299/// # fn do_something() -> redis::RedisResult<()> {
3300/// use redis::{PubSubCommands, ControlFlow};
3301/// let client = redis::Client::open("redis://127.0.0.1/")?;
3302/// let mut con = client.get_connection()?;
3303/// let mut count = 0;
3304/// con.subscribe(&["foo"], |msg| {
3305///     // do something with message
3306///     assert_eq!(msg.get_channel(), Ok(String::from("foo")));
3307///
3308///     // increment messages seen counter
3309///     count += 1;
3310///     match count {
3311///         // stop after receiving 10 messages
3312///         10 => ControlFlow::Break(()),
3313///         _ => ControlFlow::Continue,
3314///     }
3315/// })?;
3316/// # Ok(()) }
3317/// ```
3318// TODO In the future, it would be nice to implement Try such that `?` will work
3319//      within the closure.
3320pub trait PubSubCommands: Sized {
3321    /// Subscribe to a list of channels using SUBSCRIBE and run the provided
3322    /// closure for each message received.
3323    ///
3324    /// For every `Msg` passed to the provided closure, either
3325    /// `ControlFlow::Break` or `ControlFlow::Continue` must be returned. This
3326    /// method will not return until `ControlFlow::Break` is observed.
3327    fn subscribe<C, F, U>(&mut self, _: C, _: F) -> RedisResult<U>
3328    where
3329        F: FnMut(Msg) -> ControlFlow<U>,
3330        C: ToRedisArgs;
3331
3332    /// Subscribe to a list of channels using PSUBSCRIBE and run the provided
3333    /// closure for each message received.
3334    ///
3335    /// For every `Msg` passed to the provided closure, either
3336    /// `ControlFlow::Break` or `ControlFlow::Continue` must be returned. This
3337    /// method will not return until `ControlFlow::Break` is observed.
3338    fn psubscribe<P, F, U>(&mut self, _: P, _: F) -> RedisResult<U>
3339    where
3340        F: FnMut(Msg) -> ControlFlow<U>,
3341        P: ToRedisArgs;
3342}
3343
3344impl<T> Commands for T where T: ConnectionLike {}
3345
3346#[cfg(feature = "aio")]
3347impl<T> AsyncCommands for T where T: crate::aio::ConnectionLike + Send + Sync + Sized {}
3348
3349impl<T> TypedCommands for T where T: ConnectionLike {}
3350
3351#[cfg(feature = "aio")]
3352impl<T> AsyncTypedCommands for T where T: crate::aio::ConnectionLike + Send + Sync + Sized {}
3353
3354impl PubSubCommands for Connection {
3355    fn subscribe<C, F, U>(&mut self, channels: C, mut func: F) -> RedisResult<U>
3356    where
3357        F: FnMut(Msg) -> ControlFlow<U>,
3358        C: ToRedisArgs,
3359    {
3360        let mut pubsub = self.as_pubsub();
3361        pubsub.subscribe(channels)?;
3362
3363        loop {
3364            let msg = pubsub.get_message()?;
3365            match func(msg) {
3366                ControlFlow::Continue => continue,
3367                ControlFlow::Break(value) => return Ok(value),
3368            }
3369        }
3370    }
3371
3372    fn psubscribe<P, F, U>(&mut self, patterns: P, mut func: F) -> RedisResult<U>
3373    where
3374        F: FnMut(Msg) -> ControlFlow<U>,
3375        P: ToRedisArgs,
3376    {
3377        let mut pubsub = self.as_pubsub();
3378        pubsub.psubscribe(patterns)?;
3379
3380        loop {
3381            let msg = pubsub.get_message()?;
3382            match func(msg) {
3383                ControlFlow::Continue => continue,
3384                ControlFlow::Break(value) => return Ok(value),
3385            }
3386        }
3387    }
3388}
3389
3390/// Options for the [SCAN](https://redis.io/commands/scan) command
3391///
3392/// # Example
3393///
3394/// ```rust
3395/// use redis::{Commands, RedisResult, ScanOptions, Iter};
3396/// fn force_fetching_every_matching_key<'a, T: redis::FromRedisValue>(
3397///     con: &'a mut redis::Connection,
3398///     pattern: &'a str,
3399///     count: usize,
3400/// ) -> RedisResult<Iter<'a, T>> {
3401///     let opts = ScanOptions::default()
3402///         .with_pattern(pattern)
3403///         .with_count(count);
3404///     con.scan_options(opts)
3405/// }
3406/// ```
3407#[derive(Default)]
3408pub struct ScanOptions {
3409    pattern: Option<String>,
3410    count: Option<usize>,
3411    scan_type: Option<String>,
3412}
3413
3414impl ScanOptions {
3415    /// Limit the results to the first N matching items.
3416    pub fn with_count(mut self, n: usize) -> Self {
3417        self.count = Some(n);
3418        self
3419    }
3420
3421    /// Pattern for scan
3422    pub fn with_pattern(mut self, p: impl Into<String>) -> Self {
3423        self.pattern = Some(p.into());
3424        self
3425    }
3426
3427    /// Limit the results to those with the given Redis type
3428    pub fn with_type(mut self, t: impl Into<String>) -> Self {
3429        self.scan_type = Some(t.into());
3430        self
3431    }
3432}
3433
3434impl ToRedisArgs for ScanOptions {
3435    fn write_redis_args<W>(&self, out: &mut W)
3436    where
3437        W: ?Sized + RedisWrite,
3438    {
3439        if let Some(p) = &self.pattern {
3440            out.write_arg(b"MATCH");
3441            out.write_arg_fmt(p);
3442        }
3443
3444        if let Some(n) = self.count {
3445            out.write_arg(b"COUNT");
3446            out.write_arg_fmt(n);
3447        }
3448
3449        if let Some(t) = &self.scan_type {
3450            out.write_arg(b"TYPE");
3451            out.write_arg_fmt(t);
3452        }
3453    }
3454
3455    fn num_of_args(&self) -> usize {
3456        let mut len = 0;
3457        if self.pattern.is_some() {
3458            len += 2;
3459        }
3460        if self.count.is_some() {
3461            len += 2;
3462        }
3463        if self.scan_type.is_some() {
3464            len += 2;
3465        }
3466        len
3467    }
3468}
3469
3470/// Options for the [LPOS](https://redis.io/commands/lpos) command
3471///
3472/// # Example
3473///
3474/// ```rust,no_run
3475/// use redis::{Commands, RedisResult, LposOptions};
3476/// fn fetch_list_position(
3477///     con: &mut redis::Connection,
3478///     key: &str,
3479///     value: &str,
3480///     count: usize,
3481///     rank: isize,
3482///     maxlen: usize,
3483/// ) -> RedisResult<Vec<usize>> {
3484///     let opts = LposOptions::default()
3485///         .count(count)
3486///         .rank(rank)
3487///         .maxlen(maxlen);
3488///     con.lpos(key, value, opts)
3489/// }
3490/// ```
3491#[derive(Default)]
3492pub struct LposOptions {
3493    count: Option<usize>,
3494    maxlen: Option<usize>,
3495    rank: Option<isize>,
3496}
3497
3498impl LposOptions {
3499    /// Limit the results to the first N matching items.
3500    pub fn count(mut self, n: usize) -> Self {
3501        self.count = Some(n);
3502        self
3503    }
3504
3505    /// Return the value of N from the matching items.
3506    pub fn rank(mut self, n: isize) -> Self {
3507        self.rank = Some(n);
3508        self
3509    }
3510
3511    /// Limit the search to N items in the list.
3512    pub fn maxlen(mut self, n: usize) -> Self {
3513        self.maxlen = Some(n);
3514        self
3515    }
3516}
3517
3518impl ToRedisArgs for LposOptions {
3519    fn write_redis_args<W>(&self, out: &mut W)
3520    where
3521        W: ?Sized + RedisWrite,
3522    {
3523        if let Some(n) = self.count {
3524            out.write_arg(b"COUNT");
3525            out.write_arg_fmt(n);
3526        }
3527
3528        if let Some(n) = self.rank {
3529            out.write_arg(b"RANK");
3530            out.write_arg_fmt(n);
3531        }
3532
3533        if let Some(n) = self.maxlen {
3534            out.write_arg(b"MAXLEN");
3535            out.write_arg_fmt(n);
3536        }
3537    }
3538
3539    fn num_of_args(&self) -> usize {
3540        let mut len = 0;
3541        if self.count.is_some() {
3542            len += 2;
3543        }
3544        if self.rank.is_some() {
3545            len += 2;
3546        }
3547        if self.maxlen.is_some() {
3548            len += 2;
3549        }
3550        len
3551    }
3552}
3553
3554/// Enum for the LEFT | RIGHT args used by some commands
3555#[non_exhaustive]
3556pub enum Direction {
3557    /// Targets the first element (head) of the list
3558    Left,
3559    /// Targets the last element (tail) of the list
3560    Right,
3561}
3562
3563impl ToRedisArgs for Direction {
3564    fn write_redis_args<W>(&self, out: &mut W)
3565    where
3566        W: ?Sized + RedisWrite,
3567    {
3568        let s: &[u8] = match self {
3569            Self::Left => b"LEFT",
3570            Self::Right => b"RIGHT",
3571        };
3572        out.write_arg(s);
3573    }
3574}
3575
3576impl ToSingleRedisArg for Direction {}
3577
3578/// Options for the [COPY](https://redis.io/commands/copy) command
3579///
3580/// # Example
3581/// ```rust,no_run
3582/// use redis::{Commands, RedisResult, CopyOptions, SetExpiry, ExistenceCheck};
3583/// fn copy_value(
3584///     con: &mut redis::Connection,
3585///     old: &str,
3586///     new: &str,
3587/// ) -> RedisResult<Vec<usize>> {
3588///     let opts = CopyOptions::default()
3589///         .db("my_other_db")
3590///         .replace(true);
3591///     con.copy(old, new, opts)
3592/// }
3593/// ```
3594#[derive(Clone, Copy, Debug)]
3595pub struct CopyOptions<Db: ToString> {
3596    db: Option<Db>,
3597    replace: bool,
3598}
3599
3600impl Default for CopyOptions<&'static str> {
3601    fn default() -> Self {
3602        CopyOptions {
3603            db: None,
3604            replace: false,
3605        }
3606    }
3607}
3608
3609impl<Db: ToString> CopyOptions<Db> {
3610    /// Set the target database for the copy operation
3611    pub fn db<Db2: ToString>(self, db: Db2) -> CopyOptions<Db2> {
3612        CopyOptions {
3613            db: Some(db),
3614            replace: self.replace,
3615        }
3616    }
3617
3618    /// Set the replace option for the copy operation
3619    pub fn replace(mut self, replace: bool) -> Self {
3620        self.replace = replace;
3621        self
3622    }
3623}
3624
3625impl<Db: ToString> ToRedisArgs for CopyOptions<Db> {
3626    fn write_redis_args<W>(&self, out: &mut W)
3627    where
3628        W: ?Sized + RedisWrite,
3629    {
3630        if let Some(db) = &self.db {
3631            out.write_arg(b"DB");
3632            out.write_arg(db.to_string().as_bytes());
3633        }
3634        if self.replace {
3635            out.write_arg(b"REPLACE");
3636        }
3637    }
3638}
3639
3640impl<Db: ToString> ToSingleRedisArg for CopyOptions<Db> {}
3641
3642/// Options for the [SET](https://redis.io/commands/set) command
3643///
3644/// # Example
3645/// ```rust,no_run
3646/// use redis::{Commands, RedisResult, SetOptions, SetExpiry, ExistenceCheck, ValueComparison};
3647/// fn set_key_value(
3648///     con: &mut redis::Connection,
3649///     key: &str,
3650///     value: &str,
3651/// ) -> RedisResult<Vec<usize>> {
3652///     let opts = SetOptions::default()
3653///         .conditional_set(ExistenceCheck::NX)
3654///         .value_comparison(ValueComparison::ifeq("old_value"))
3655///         .get(true)
3656///         .with_expiration(SetExpiry::EX(60));
3657///     con.set_options(key, value, opts)
3658/// }
3659/// ```
3660#[derive(Clone, Default)]
3661pub struct SetOptions {
3662    conditional_set: Option<ExistenceCheck>,
3663    /// IFEQ `match-value` - Set the key's value and expiration only if its current value is equal to `match-value`.
3664    /// If the key doesn't exist, it won't be created.
3665    /// IFNE `match-value` - Set the key's value and expiration only if its current value is not equal to `match-value`.
3666    /// If the key doesn't exist, it will be created.
3667    /// IFDEQ `match-digest` - Set the key's value and expiration only if the digest of its current value is equal to `match-digest`.
3668    /// If the key doesn't exist, it won't be created.
3669    /// IFDNE `match-digest` - Set the key's value and expiration only if the digest of its current value is not equal to `match-digest`.
3670    /// If the key doesn't exist, it will be created.
3671    value_comparison: Option<ValueComparison>,
3672    get: bool,
3673    expiration: Option<SetExpiry>,
3674}
3675
3676impl SetOptions {
3677    /// Set the existence check for the SET command
3678    pub fn conditional_set(mut self, existence_check: ExistenceCheck) -> Self {
3679        self.conditional_set = Some(existence_check);
3680        self
3681    }
3682
3683    /// Set the value comparison for the SET command
3684    pub fn value_comparison(mut self, value_comparison: ValueComparison) -> Self {
3685        self.value_comparison = Some(value_comparison);
3686        self
3687    }
3688
3689    /// Set the GET option for the SET command
3690    pub fn get(mut self, get: bool) -> Self {
3691        self.get = get;
3692        self
3693    }
3694
3695    /// Set the expiration for the SET command
3696    pub fn with_expiration(mut self, expiration: SetExpiry) -> Self {
3697        self.expiration = Some(expiration);
3698        self
3699    }
3700}
3701
3702impl ToRedisArgs for SetOptions {
3703    fn write_redis_args<W>(&self, out: &mut W)
3704    where
3705        W: ?Sized + RedisWrite,
3706    {
3707        if let Some(ref conditional_set) = self.conditional_set {
3708            conditional_set.write_redis_args(out);
3709        }
3710        if let Some(ref value_comparison) = self.value_comparison {
3711            value_comparison.write_redis_args(out);
3712        }
3713        if self.get {
3714            out.write_arg(b"GET");
3715        }
3716        if let Some(ref expiration) = self.expiration {
3717            expiration.write_redis_args(out);
3718        }
3719    }
3720}
3721
3722/// Options for the [MSETEX](https://redis.io/commands/msetex) command
3723///
3724/// # Example
3725/// ```rust,no_run
3726/// use redis::{Commands, RedisResult, MSetOptions, SetExpiry, ExistenceCheck};
3727/// fn set_multiple_key_values(
3728///     con: &mut redis::Connection,
3729/// ) -> RedisResult<bool> {
3730///     let opts = MSetOptions::default()
3731///         .conditional_set(ExistenceCheck::NX)
3732///         .with_expiration(SetExpiry::EX(60));
3733///     con.mset_ex(&[("key1", "value1"), ("key2", "value2")], opts)
3734/// }
3735/// ```
3736#[derive(Clone, Copy, Default)]
3737pub struct MSetOptions {
3738    conditional_set: Option<ExistenceCheck>,
3739    expiration: Option<SetExpiry>,
3740}
3741
3742impl MSetOptions {
3743    /// Set the existence check for the MSETEX command
3744    pub fn conditional_set(mut self, existence_check: ExistenceCheck) -> Self {
3745        self.conditional_set = Some(existence_check);
3746        self
3747    }
3748
3749    /// Set the expiration for the MSETEX command
3750    pub fn with_expiration(mut self, expiration: SetExpiry) -> Self {
3751        self.expiration = Some(expiration);
3752        self
3753    }
3754}
3755
3756impl ToRedisArgs for MSetOptions {
3757    fn write_redis_args<W>(&self, out: &mut W)
3758    where
3759        W: ?Sized + RedisWrite,
3760    {
3761        if let Some(ref conditional_set) = self.conditional_set {
3762            conditional_set.write_redis_args(out);
3763        }
3764        if let Some(ref expiration) = self.expiration {
3765            expiration.write_redis_args(out);
3766        }
3767    }
3768}
3769
3770/// Options for the [FLUSHALL](https://redis.io/commands/flushall) command
3771///
3772/// # Example
3773/// ```rust,no_run
3774/// use redis::{Commands, RedisResult, FlushAllOptions};
3775/// fn flushall_sync(
3776///     con: &mut redis::Connection,
3777/// ) -> RedisResult<()> {
3778///     let opts = FlushAllOptions{blocking: true};
3779///     con.flushall_options(&opts)
3780/// }
3781/// ```
3782#[derive(Clone, Copy, Default)]
3783pub struct FlushAllOptions {
3784    /// Blocking (`SYNC`) waits for completion, non-blocking (`ASYNC`) runs in the background
3785    pub blocking: bool,
3786}
3787
3788impl FlushAllOptions {
3789    /// Set whether to run blocking (`SYNC`) or non-blocking (`ASYNC`) flush
3790    pub fn blocking(mut self, blocking: bool) -> Self {
3791        self.blocking = blocking;
3792        self
3793    }
3794}
3795
3796impl ToRedisArgs for FlushAllOptions {
3797    fn write_redis_args<W>(&self, out: &mut W)
3798    where
3799        W: ?Sized + RedisWrite,
3800    {
3801        if self.blocking {
3802            out.write_arg(b"SYNC");
3803        } else {
3804            out.write_arg(b"ASYNC");
3805        }
3806    }
3807}
3808impl ToSingleRedisArg for FlushAllOptions {}
3809
3810/// Options for the [FLUSHDB](https://redis.io/commands/flushdb) command
3811pub type FlushDbOptions = FlushAllOptions;
3812
3813/// Options for the HSETEX command
3814#[derive(Clone, Copy, Default)]
3815pub struct HashFieldExpirationOptions {
3816    existence_check: Option<FieldExistenceCheck>,
3817    expiration: Option<SetExpiry>,
3818}
3819
3820impl HashFieldExpirationOptions {
3821    /// Set the field(s) existence check for the HSETEX command
3822    pub fn set_existence_check(mut self, field_existence_check: FieldExistenceCheck) -> Self {
3823        self.existence_check = Some(field_existence_check);
3824        self
3825    }
3826
3827    /// Set the expiration option for the field(s) in the HSETEX command
3828    pub fn set_expiration(mut self, expiration: SetExpiry) -> Self {
3829        self.expiration = Some(expiration);
3830        self
3831    }
3832}
3833
3834impl ToRedisArgs for HashFieldExpirationOptions {
3835    fn write_redis_args<W>(&self, out: &mut W)
3836    where
3837        W: ?Sized + RedisWrite,
3838    {
3839        if let Some(ref existence_check) = self.existence_check {
3840            existence_check.write_redis_args(out);
3841        }
3842
3843        if let Some(ref expiration) = self.expiration {
3844            expiration.write_redis_args(out);
3845        }
3846    }
3847}
3848
3849impl ToRedisArgs for Expiry {
3850    fn write_redis_args<W>(&self, out: &mut W)
3851    where
3852        W: ?Sized + RedisWrite,
3853    {
3854        let mut buf = ::itoa::Buffer::new();
3855        match self {
3856            Self::EX(sec) => {
3857                out.write_arg(b"EX");
3858                out.write_arg(buf.format(*sec).as_bytes());
3859            }
3860            Self::PX(ms) => {
3861                out.write_arg(b"PX");
3862                out.write_arg(buf.format(*ms).as_bytes());
3863            }
3864            Self::EXAT(timestamp_sec) => {
3865                out.write_arg(b"EXAT");
3866                out.write_arg(buf.format(*timestamp_sec).as_bytes());
3867            }
3868            Self::PXAT(timestamp_ms) => {
3869                out.write_arg(b"PXAT");
3870                out.write_arg(buf.format(*timestamp_ms).as_bytes());
3871            }
3872            Self::PERSIST => {
3873                out.write_arg(b"PERSIST");
3874            }
3875        }
3876    }
3877}
3878
3879/// Helper enum that is used to define update checks
3880#[derive(Clone, Copy)]
3881#[non_exhaustive]
3882pub enum UpdateCheck {
3883    /// LT -- Only update if the new score is less than the current.
3884    LT,
3885    /// GT -- Only update if the new score is greater than the current.
3886    GT,
3887}
3888
3889impl ToRedisArgs for UpdateCheck {
3890    fn write_redis_args<W>(&self, out: &mut W)
3891    where
3892        W: ?Sized + RedisWrite,
3893    {
3894        match self {
3895            Self::LT => {
3896                out.write_arg(b"LT");
3897            }
3898            Self::GT => {
3899                out.write_arg(b"GT");
3900            }
3901        }
3902    }
3903}
3904
3905/// Options for the [ZADD](https://redis.io/commands/zadd) command
3906#[derive(Clone, Copy, Default)]
3907pub struct SortedSetAddOptions {
3908    conditional_set: Option<ExistenceCheck>,
3909    conditional_update: Option<UpdateCheck>,
3910    include_changed: bool,
3911    increment: bool,
3912}
3913
3914impl SortedSetAddOptions {
3915    /// Sets the NX option for the ZADD command
3916    /// Only add a member if it does not already exist.
3917    pub fn add_only() -> Self {
3918        Self {
3919            conditional_set: Some(ExistenceCheck::NX),
3920            ..Default::default()
3921        }
3922    }
3923
3924    /// Sets the XX option and optionally the GT/LT option for the ZADD command
3925    /// Only update existing members
3926    pub fn update_only(conditional_update: Option<UpdateCheck>) -> Self {
3927        Self {
3928            conditional_set: Some(ExistenceCheck::XX),
3929            conditional_update,
3930            ..Default::default()
3931        }
3932    }
3933
3934    /// Optionally sets the GT/LT option for the ZADD command
3935    /// Add new member or update existing
3936    pub fn add_or_update(conditional_update: Option<UpdateCheck>) -> Self {
3937        Self {
3938            conditional_update,
3939            ..Default::default()
3940        }
3941    }
3942
3943    /// Sets the CH option for the ZADD command
3944    /// Return the number of elements changed (not just added).
3945    pub fn include_changed_count(mut self) -> Self {
3946        self.include_changed = true;
3947        self
3948    }
3949
3950    /// Sets the INCR option for the ZADD command
3951    /// Increment the score of the member instead of setting it.
3952    pub fn increment_score(mut self) -> Self {
3953        self.increment = true;
3954        self
3955    }
3956}
3957
3958impl ToRedisArgs for SortedSetAddOptions {
3959    fn write_redis_args<W>(&self, out: &mut W)
3960    where
3961        W: ?Sized + RedisWrite,
3962    {
3963        if let Some(ref conditional_set) = self.conditional_set {
3964            conditional_set.write_redis_args(out);
3965        }
3966
3967        if let Some(ref conditional_update) = self.conditional_update {
3968            conditional_update.write_redis_args(out);
3969        }
3970        if self.include_changed {
3971            out.write_arg(b"CH");
3972        }
3973        if self.increment {
3974            out.write_arg(b"INCR");
3975        }
3976    }
3977}
3978
3979/// Creates HELLO command for RESP3 with RedisConnectionInfo
3980/// [Redis Docs](https://redis.io/commands/HELLO)
3981pub fn resp3_hello(connection_info: &RedisConnectionInfo) -> Cmd {
3982    let mut hello_cmd = cmd("HELLO");
3983    hello_cmd.arg("3");
3984    if let Some(password) = &connection_info.password {
3985        let username: &str = match connection_info.username.as_ref() {
3986            None => "default",
3987            Some(username) => username,
3988        };
3989        hello_cmd.arg("AUTH").arg(username).arg(password.as_bytes());
3990    }
3991
3992    hello_cmd
3993}
3994
3995/// HOTKEYS commands for tracking hot keys on standalone connections (Redis 8.6.0+).
3996///
3997/// HOTKEYS is a stateful, node-local command requiring session affinity.
3998/// It is ONLY implemented for standalone connection types (Connection, MultiplexedConnection, ConnectionManager)
3999/// and NOT for cluster clients (ClusterConnection) to prevent session affinity issues.
4000///
4001/// # Example (Standalone)
4002///
4003/// ```rust,no_run
4004/// use redis::{HotkeysCommands, HotkeysOptions};
4005///
4006/// # fn example() -> redis::RedisResult<()> {
4007/// let client = redis::Client::open("redis://127.0.0.1/")?;
4008/// let mut con = client.get_connection()?;
4009///
4010/// // Start tracking hot keys by CPU time percentage for 60 seconds
4011/// let opts = HotkeysOptions::new_with_cpu()
4012///     .with_duration_secs(60);
4013/// con.hotkeys_start(opts)?;
4014///
4015/// // ... perform operations ...
4016///
4017/// // Get hot keys metrics. `None` means no tracking session state is available
4018/// // (e.g. reset or never started).
4019/// if let Some(response) = con.hotkeys_get()? {
4020///     if let Some(cpu_keys) = response.by_cpu_time_us.as_ref() {
4021///         for entry in cpu_keys {
4022///             println!("Key: {}, CPU time: {}", entry.key, entry.value);
4023///         }
4024///     }
4025/// }
4026///
4027/// // Stop tracking
4028/// con.hotkeys_stop()?;
4029/// # Ok(())
4030/// # }
4031/// ```
4032///
4033/// # Using HOTKEYS in Cluster Mode
4034///
4035/// For cluster connections, use `route_command` with explicit node routing.
4036/// See the documentation on [`HotkeysOptions`](hotkeys::HotkeysOptions) for a complete example.
4037pub trait HotkeysCommands: ConnectionLike {
4038    /// Start tracking hot keys with the given options.
4039    ///
4040    /// ```text
4041    /// HOTKEYS START METRICS count [CPU] [NET] [COUNT k] [DURATION seconds] [SAMPLE ratio] [SLOTS count slot [slot ...]]
4042    /// ```
4043    /// [Redis Docs](https://redis.io/commands/hotkeys-start/)
4044    fn hotkeys_start(&mut self, opts: hotkeys::HotkeysOptions) -> RedisResult<()>
4045    where
4046        Self: Sized,
4047    {
4048        cmd("HOTKEYS").arg("START").arg(opts).query(self)
4049    }
4050
4051    /// Get the current hot keys metrics.
4052    ///
4053    /// Returns `Some(response)` when a tracking session state is available
4054    /// (when there is an active tracking session or when the tracking session has been stopped but not reset)
4055    /// and `None` when there is no session state to report (Redis replies `Nil` in that case).
4056    ///
4057    /// ```text
4058    /// HOTKEYS GET
4059    /// ```
4060    /// [Redis Docs](https://redis.io/commands/hotkeys-get/)
4061    fn hotkeys_get(&mut self) -> RedisResult<Option<hotkeys::HotkeysResponse>>
4062    where
4063        Self: Sized,
4064    {
4065        cmd("HOTKEYS").arg("GET").query(self)
4066    }
4067
4068    /// Stop tracking hot keys.
4069    ///
4070    /// Returns `true` if a tracking session was running and has been stopped,
4071    /// or `false` if no session was active (Redis replies `Nil` in that case).
4072    ///
4073    /// ```text
4074    /// HOTKEYS STOP
4075    /// ```
4076    /// [Redis Docs](https://redis.io/commands/hotkeys-stop/)
4077    fn hotkeys_stop(&mut self) -> RedisResult<bool>
4078    where
4079        Self: Sized,
4080    {
4081        cmd("HOTKEYS").arg("STOP").query(self)
4082    }
4083
4084    /// Reset the hot keys tracking state.
4085    ///
4086    /// Only valid when no tracking session is currently running.
4087    /// Calling RESET while a session is active returns a server error.
4088    ///
4089    /// ```text
4090    /// HOTKEYS RESET
4091    /// ```
4092    /// [Redis Docs](https://redis.io/commands/hotkeys-reset/)
4093    fn hotkeys_reset(&mut self) -> RedisResult<()>
4094    where
4095        Self: Sized,
4096    {
4097        cmd("HOTKEYS").arg("RESET").query(self)
4098    }
4099}
4100
4101// Implement ONLY for Connection (standalone sync)
4102impl HotkeysCommands for Connection {}
4103
4104/// Async version of HOTKEYS commands for standalone async connections (Redis 8.6.0+).
4105///
4106/// HOTKEYS is a stateful, node-local command requiring session affinity.
4107/// It is ONLY implemented for standalone connection types and NOT for cluster clients.
4108#[cfg(feature = "aio")]
4109pub trait AsyncHotkeysCommands: crate::aio::ConnectionLike + Send + Sync + Sized {
4110    /// Start tracking hot keys with the given options.
4111    ///
4112    /// ```text
4113    /// HOTKEYS START METRICS count [CPU] [NET] [COUNT k] [DURATION seconds] [SAMPLE ratio] [SLOTS count slot [slot ...]]
4114    /// ```
4115    /// [Redis Docs](https://redis.io/commands/hotkeys-start/)
4116    fn hotkeys_start(
4117        &mut self,
4118        opts: hotkeys::HotkeysOptions,
4119    ) -> crate::types::RedisFuture<'_, ()> {
4120        Box::pin(async move {
4121            cmd("HOTKEYS")
4122                .arg("START")
4123                .arg(opts)
4124                .query_async(self)
4125                .await
4126        })
4127    }
4128
4129    /// Get the current hot keys metrics.
4130    ///
4131    /// Returns `Some(response)` when a tracking session state is available
4132    /// (when there is an active tracking session or when the tracking session has been stopped but not reset)
4133    /// and `None` when there is no session state to report (Redis replies `Nil` in that case).
4134    ///
4135    /// ```text
4136    /// HOTKEYS GET
4137    /// ```
4138    /// [Redis Docs](https://redis.io/commands/hotkeys-get/)
4139    fn hotkeys_get(&mut self) -> crate::types::RedisFuture<'_, Option<hotkeys::HotkeysResponse>> {
4140        Box::pin(async move { cmd("HOTKEYS").arg("GET").query_async(self).await })
4141    }
4142
4143    /// Stop tracking hot keys.
4144    ///
4145    /// Returns `true` if a tracking session was running and has been stopped,
4146    /// or `false` if no session was active (Redis replies `Nil` in that case).
4147    ///
4148    /// ```text
4149    /// HOTKEYS STOP
4150    /// ```
4151    /// [Redis Docs](https://redis.io/commands/hotkeys-stop/)
4152    fn hotkeys_stop(&mut self) -> crate::types::RedisFuture<'_, bool> {
4153        Box::pin(async move { cmd("HOTKEYS").arg("STOP").query_async(self).await })
4154    }
4155
4156    /// Reset the hot keys tracking state.
4157    ///
4158    /// Only valid when no tracking session is currently running.
4159    /// Calling RESET while a session is active returns a server error.
4160    ///
4161    /// ```text
4162    /// HOTKEYS RESET
4163    /// ```
4164    /// [Redis Docs](https://redis.io/commands/hotkeys-reset/)
4165    fn hotkeys_reset(&mut self) -> crate::types::RedisFuture<'_, ()> {
4166        Box::pin(async move { cmd("HOTKEYS").arg("RESET").query_async(self).await })
4167    }
4168}
4169
4170// Implement ONLY for standalone async connection types
4171#[cfg(feature = "aio")]
4172impl AsyncHotkeysCommands for crate::aio::MultiplexedConnection {}
4173
4174#[cfg(all(feature = "aio", feature = "connection-manager"))]
4175impl AsyncHotkeysCommands for crate::aio::ConnectionManager {}