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