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