redis/commands/
mod.rs

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