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