blob: 64bbdf82c18007e12260873990e84a1035b4710a [file] [log] [blame]
// can't use rustfmt here because it screws up the file.
#![cfg_attr(rustfmt, rustfmt_skip)]
use crate::cmd::{cmd, Cmd, Iter};
use crate::connection::{Connection, ConnectionLike, Msg};
use crate::pipeline::Pipeline;
use crate::types::{FromRedisValue, NumericBehavior, RedisResult, ToRedisArgs, RedisWrite, Expiry};
#[macro_use]
mod macros;
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
mod json;
#[cfg(feature = "json")]
pub use json::JsonCommands;
#[cfg(all(feature = "json", feature = "aio"))]
pub use json::JsonAsyncCommands;
#[cfg(feature = "cluster")]
use crate::cluster_pipeline::ClusterPipeline;
#[cfg(feature = "geospatial")]
use crate::geo;
#[cfg(feature = "streams")]
use crate::streams;
#[cfg(feature = "acl")]
use crate::acl;
#[cfg(feature = "cluster")]
pub(crate) fn is_readonly_cmd(cmd: &[u8]) -> bool {
matches!(
cmd,
// @admin
b"LASTSAVE" |
// @bitmap
b"BITCOUNT" | b"BITFIELD_RO" | b"BITPOS" | b"GETBIT" |
// @connection
b"CLIENT" | b"ECHO" |
// @geo
b"GEODIST" | b"GEOHASH" | b"GEOPOS" | b"GEORADIUSBYMEMBER_RO" | b"GEORADIUS_RO" | b"GEOSEARCH" |
// @hash
b"HEXISTS" | b"HGET" | b"HGETALL" | b"HKEYS" | b"HLEN" | b"HMGET" | b"HRANDFIELD" | b"HSCAN" | b"HSTRLEN" | b"HVALS" |
// @hyperloglog
b"PFCOUNT" |
// @keyspace
b"DBSIZE" | b"DUMP" | b"EXISTS" | b"EXPIRETIME" | b"KEYS" | b"OBJECT" | b"PEXPIRETIME" | b"PTTL" | b"RANDOMKEY" | b"SCAN" | b"TOUCH" | b"TTL" | b"TYPE" |
// @list
b"LINDEX" | b"LLEN" | b"LPOS" | b"LRANGE" | b"SORT_RO" |
// @scripting
b"EVALSHA_RO" | b"EVAL_RO" | b"FCALL_RO" |
// @set
b"SCARD" | b"SDIFF" | b"SINTER" | b"SINTERCARD" | b"SISMEMBER" | b"SMEMBERS" | b"SMISMEMBER" | b"SRANDMEMBER" | b"SSCAN" | b"SUNION" |
// @sortedset
b"ZCARD" | b"ZCOUNT" | b"ZDIFF" | b"ZINTER" | b"ZINTERCARD" | b"ZLEXCOUNT" | b"ZMSCORE" | b"ZRANDMEMBER" | b"ZRANGE" | b"ZRANGEBYLEX" | b"ZRANGEBYSCORE" | b"ZRANK" | b"ZREVRANGE" | b"ZREVRANGEBYLEX" | b"ZREVRANGEBYSCORE" | b"ZREVRANK" | b"ZSCAN" | b"ZSCORE" | b"ZUNION" |
// @stream
b"XINFO" | b"XLEN" | b"XPENDING" | b"XRANGE" | b"XREAD" | b"XREVRANGE" |
// @string
b"GET" | b"GETRANGE" | b"LCS" | b"MGET" | b"STRALGO" | b"STRLEN" | b"SUBSTR"
)
}
implement_commands! {
'a
// most common operations
/// Get the value of a key. If key is a vec this becomes an `MGET`.
fn get<K: ToRedisArgs>(key: K) {
cmd(if key.is_single_arg() { "GET" } else { "MGET" }).arg(key)
}
/// Gets all keys matching pattern
fn keys<K: ToRedisArgs>(key: K) {
cmd("KEYS").arg(key)
}
/// Set the string value of a key.
fn set<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
cmd("SET").arg(key).arg(value)
}
/// Sets multiple keys to their values.
fn set_multiple<K: ToRedisArgs, V: ToRedisArgs>(items: &'a [(K, V)]) {
cmd("MSET").arg(items)
}
/// Set the value and expiration of a key.
fn set_ex<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V, seconds: usize) {
cmd("SETEX").arg(key).arg(seconds).arg(value)
}
/// Set the value and expiration in milliseconds of a key.
fn pset_ex<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V, milliseconds: usize) {
cmd("PSETEX").arg(key).arg(milliseconds).arg(value)
}
/// Set the value of a key, only if the key does not exist
fn set_nx<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
cmd("SETNX").arg(key).arg(value)
}
/// Sets multiple keys to their values failing if at least one already exists.
fn mset_nx<K: ToRedisArgs, V: ToRedisArgs>(items: &'a [(K, V)]) {
cmd("MSETNX").arg(items)
}
/// Set the string value of a key and return its old value.
fn getset<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
cmd("GETSET").arg(key).arg(value)
}
/// Get a range of bytes/substring from the value of a key. Negative values provide an offset from the end of the value.
fn getrange<K: ToRedisArgs>(key: K, from: isize, to: isize) {
cmd("GETRANGE").arg(key).arg(from).arg(to)
}
/// Overwrite the part of the value stored in key at the specified offset.
fn setrange<K: ToRedisArgs, V: ToRedisArgs>(key: K, offset: isize, value: V) {
cmd("SETRANGE").arg(key).arg(offset).arg(value)
}
/// Delete one or more keys.
fn del<K: ToRedisArgs>(key: K) {
cmd("DEL").arg(key)
}
/// Determine if a key exists.
fn exists<K: ToRedisArgs>(key: K) {
cmd("EXISTS").arg(key)
}
/// Set a key's time to live in seconds.
fn expire<K: ToRedisArgs>(key: K, seconds: usize) {
cmd("EXPIRE").arg(key).arg(seconds)
}
/// Set the expiration for a key as a UNIX timestamp.
fn expire_at<K: ToRedisArgs>(key: K, ts: usize) {
cmd("EXPIREAT").arg(key).arg(ts)
}
/// Set a key's time to live in milliseconds.
fn pexpire<K: ToRedisArgs>(key: K, ms: usize) {
cmd("PEXPIRE").arg(key).arg(ms)
}
/// Set the expiration for a key as a UNIX timestamp in milliseconds.
fn pexpire_at<K: ToRedisArgs>(key: K, ts: usize) {
cmd("PEXPIREAT").arg(key).arg(ts)
}
/// Remove the expiration from a key.
fn persist<K: ToRedisArgs>(key: K) {
cmd("PERSIST").arg(key)
}
/// Get the expiration time of a key.
fn ttl<K: ToRedisArgs>(key: K) {
cmd("TTL").arg(key)
}
/// Get the expiration time of a key in milliseconds.
fn pttl<K: ToRedisArgs>(key: K) {
cmd("PTTL").arg(key)
}
/// Get the value of a key and set expiration
fn get_ex<K: ToRedisArgs>(key: K, expire_at: Expiry) {
let (option, time_arg) = match expire_at {
Expiry::EX(sec) => ("EX", Some(sec)),
Expiry::PX(ms) => ("PX", Some(ms)),
Expiry::EXAT(timestamp_sec) => ("EXAT", Some(timestamp_sec)),
Expiry::PXAT(timestamp_ms) => ("PXAT", Some(timestamp_ms)),
Expiry::PERSIST => ("PERSIST", None),
};
cmd("GETEX").arg(key).arg(option).arg(time_arg)
}
/// Get the value of a key and delete it
fn get_del<K: ToRedisArgs>(key: K) {
cmd("GETDEL").arg(key)
}
/// Rename a key.
fn rename<K: ToRedisArgs>(key: K, new_key: K) {
cmd("RENAME").arg(key).arg(new_key)
}
/// Rename a key, only if the new key does not exist.
fn rename_nx<K: ToRedisArgs>(key: K, new_key: K) {
cmd("RENAMENX").arg(key).arg(new_key)
}
/// Unlink one or more keys.
fn unlink<K: ToRedisArgs>(key: K) {
cmd("UNLINK").arg(key)
}
// common string operations
/// Append a value to a key.
fn append<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
cmd("APPEND").arg(key).arg(value)
}
/// Increment the numeric value of a key by the given amount. This
/// issues a `INCRBY` or `INCRBYFLOAT` depending on the type.
fn incr<K: ToRedisArgs, V: ToRedisArgs>(key: K, delta: V) {
cmd(if delta.describe_numeric_behavior() == NumericBehavior::NumberIsFloat {
"INCRBYFLOAT"
} else {
"INCRBY"
}).arg(key).arg(delta)
}
/// Decrement the numeric value of a key by the given amount.
fn decr<K: ToRedisArgs, V: ToRedisArgs>(key: K, delta: V) {
cmd("DECRBY").arg(key).arg(delta)
}
/// Sets or clears the bit at offset in the string value stored at key.
fn setbit<K: ToRedisArgs>(key: K, offset: usize, value: bool) {
cmd("SETBIT").arg(key).arg(offset).arg(if value {1} else {0})
}
/// Returns the bit value at offset in the string value stored at key.
fn getbit<K: ToRedisArgs>(key: K, offset: usize) {
cmd("GETBIT").arg(key).arg(offset)
}
/// Count set bits in a string.
fn bitcount<K: ToRedisArgs>(key: K) {
cmd("BITCOUNT").arg(key)
}
/// Count set bits in a string in a range.
fn bitcount_range<K: ToRedisArgs>(key: K, start: usize, end: usize) {
cmd("BITCOUNT").arg(key).arg(start).arg(end)
}
/// Perform a bitwise AND between multiple keys (containing string values)
/// and store the result in the destination key.
fn bit_and<K: ToRedisArgs>(dstkey: K, srckeys: K) {
cmd("BITOP").arg("AND").arg(dstkey).arg(srckeys)
}
/// Perform a bitwise OR between multiple keys (containing string values)
/// and store the result in the destination key.
fn bit_or<K: ToRedisArgs>(dstkey: K, srckeys: K) {
cmd("BITOP").arg("OR").arg(dstkey).arg(srckeys)
}
/// Perform a bitwise XOR between multiple keys (containing string values)
/// and store the result in the destination key.
fn bit_xor<K: ToRedisArgs>(dstkey: K, srckeys: K) {
cmd("BITOP").arg("XOR").arg(dstkey).arg(srckeys)
}
/// Perform a bitwise NOT of the key (containing string values)
/// and store the result in the destination key.
fn bit_not<K: ToRedisArgs>(dstkey: K, srckey: K) {
cmd("BITOP").arg("NOT").arg(dstkey).arg(srckey)
}
/// Get the length of the value stored in a key.
fn strlen<K: ToRedisArgs>(key: K) {
cmd("STRLEN").arg(key)
}
// hash operations
/// Gets a single (or multiple) fields from a hash.
fn hget<K: ToRedisArgs, F: ToRedisArgs>(key: K, field: F) {
cmd(if field.is_single_arg() { "HGET" } else { "HMGET" }).arg(key).arg(field)
}
/// Deletes a single (or multiple) fields from a hash.
fn hdel<K: ToRedisArgs, F: ToRedisArgs>(key: K, field: F) {
cmd("HDEL").arg(key).arg(field)
}
/// Sets a single field in a hash.
fn hset<K: ToRedisArgs, F: ToRedisArgs, V: ToRedisArgs>(key: K, field: F, value: V) {
cmd("HSET").arg(key).arg(field).arg(value)
}
/// Sets a single field in a hash if it does not exist.
fn hset_nx<K: ToRedisArgs, F: ToRedisArgs, V: ToRedisArgs>(key: K, field: F, value: V) {
cmd("HSETNX").arg(key).arg(field).arg(value)
}
/// Sets a multiple fields in a hash.
fn hset_multiple<K: ToRedisArgs, F: ToRedisArgs, V: ToRedisArgs>(key: K, items: &'a [(F, V)]) {
cmd("HMSET").arg(key).arg(items)
}
/// Increments a value.
fn hincr<K: ToRedisArgs, F: ToRedisArgs, D: ToRedisArgs>(key: K, field: F, delta: D) {
cmd(if delta.describe_numeric_behavior() == NumericBehavior::NumberIsFloat {
"HINCRBYFLOAT"
} else {
"HINCRBY"
}).arg(key).arg(field).arg(delta)
}
/// Checks if a field in a hash exists.
fn hexists<K: ToRedisArgs, F: ToRedisArgs>(key: K, field: F) {
cmd("HEXISTS").arg(key).arg(field)
}
/// Gets all the keys in a hash.
fn hkeys<K: ToRedisArgs>(key: K) {
cmd("HKEYS").arg(key)
}
/// Gets all the values in a hash.
fn hvals<K: ToRedisArgs>(key: K) {
cmd("HVALS").arg(key)
}
/// Gets all the fields and values in a hash.
fn hgetall<K: ToRedisArgs>(key: K) {
cmd("HGETALL").arg(key)
}
/// Gets the length of a hash.
fn hlen<K: ToRedisArgs>(key: K) {
cmd("HLEN").arg(key)
}
// list operations
/// Pop an element from a list, push it to another list
/// and return it; or block until one is available
fn blmove<K: ToRedisArgs>(srckey: K, dstkey: K, src_dir: Direction, dst_dir: Direction, timeout: usize) {
cmd("BLMOVE").arg(srckey).arg(dstkey).arg(src_dir).arg(dst_dir).arg(timeout)
}
/// Pops `count` elements from the first non-empty list key from the list of
/// provided key names; or blocks until one is available.
fn blmpop<K: ToRedisArgs>(timeout: usize, numkeys: usize, key: K, dir: Direction, count: usize){
cmd("BLMPOP").arg(timeout).arg(numkeys).arg(key).arg(dir).arg("COUNT").arg(count)
}
/// Remove and get the first element in a list, or block until one is available.
fn blpop<K: ToRedisArgs>(key: K, timeout: usize) {
cmd("BLPOP").arg(key).arg(timeout)
}
/// Remove and get the last element in a list, or block until one is available.
fn brpop<K: ToRedisArgs>(key: K, timeout: usize) {
cmd("BRPOP").arg(key).arg(timeout)
}
/// Pop a value from a list, push it to another list and return it;
/// or block until one is available.
fn brpoplpush<K: ToRedisArgs>(srckey: K, dstkey: K, timeout: usize) {
cmd("BRPOPLPUSH").arg(srckey).arg(dstkey).arg(timeout)
}
/// Get an element from a list by its index.
fn lindex<K: ToRedisArgs>(key: K, index: isize) {
cmd("LINDEX").arg(key).arg(index)
}
/// Insert an element before another element in a list.
fn linsert_before<K: ToRedisArgs, P: ToRedisArgs, V: ToRedisArgs>(
key: K, pivot: P, value: V) {
cmd("LINSERT").arg(key).arg("BEFORE").arg(pivot).arg(value)
}
/// Insert an element after another element in a list.
fn linsert_after<K: ToRedisArgs, P: ToRedisArgs, V: ToRedisArgs>(
key: K, pivot: P, value: V) {
cmd("LINSERT").arg(key).arg("AFTER").arg(pivot).arg(value)
}
/// Returns the length of the list stored at key.
fn llen<K: ToRedisArgs>(key: K) {
cmd("LLEN").arg(key)
}
/// Pop an element a list, push it to another list and return it
fn lmove<K: ToRedisArgs>(srckey: K, dstkey: K, src_dir: Direction, dst_dir: Direction) {
cmd("LMOVE").arg(srckey).arg(dstkey).arg(src_dir).arg(dst_dir)
}
/// Pops `count` elements from the first non-empty list key from the list of
/// provided key names.
fn lmpop<K: ToRedisArgs>( numkeys: usize, key: K, dir: Direction, count: usize) {
cmd("LMPOP").arg(numkeys).arg(key).arg(dir).arg("COUNT").arg(count)
}
/// Removes and returns the up to `count` first elements of the list stored at key.
///
/// If `count` is not specified, then defaults to first element.
fn lpop<K: ToRedisArgs>(key: K, count: Option<core::num::NonZeroUsize>) {
cmd("LPOP").arg(key).arg(count)
}
/// Returns the index of the first matching value of the list stored at key.
fn lpos<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V, options: LposOptions) {
cmd("LPOS").arg(key).arg(value).arg(options)
}
/// Insert all the specified values at the head of the list stored at key.
fn lpush<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
cmd("LPUSH").arg(key).arg(value)
}
/// Inserts a value at the head of the list stored at key, only if key
/// already exists and holds a list.
fn lpush_exists<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
cmd("LPUSHX").arg(key).arg(value)
}
/// Returns the specified elements of the list stored at key.
fn lrange<K: ToRedisArgs>(key: K, start: isize, stop: isize) {
cmd("LRANGE").arg(key).arg(start).arg(stop)
}
/// Removes the first count occurrences of elements equal to value
/// from the list stored at key.
fn lrem<K: ToRedisArgs, V: ToRedisArgs>(key: K, count: isize, value: V) {
cmd("LREM").arg(key).arg(count).arg(value)
}
/// Trim an existing list so that it will contain only the specified
/// range of elements specified.
fn ltrim<K: ToRedisArgs>(key: K, start: isize, stop: isize) {
cmd("LTRIM").arg(key).arg(start).arg(stop)
}
/// Sets the list element at index to value
fn lset<K: ToRedisArgs, V: ToRedisArgs>(key: K, index: isize, value: V) {
cmd("LSET").arg(key).arg(index).arg(value)
}
/// Removes and returns the up to `count` last elements of the list stored at key
///
/// If `count` is not specified, then defaults to last element.
fn rpop<K: ToRedisArgs>(key: K, count: Option<core::num::NonZeroUsize>) {
cmd("RPOP").arg(key).arg(count)
}
/// Pop a value from a list, push it to another list and return it.
fn rpoplpush<K: ToRedisArgs>(key: K, dstkey: K) {
cmd("RPOPLPUSH").arg(key).arg(dstkey)
}
/// Insert all the specified values at the tail of the list stored at key.
fn rpush<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
cmd("RPUSH").arg(key).arg(value)
}
/// Inserts value at the tail of the list stored at key, only if key
/// already exists and holds a list.
fn rpush_exists<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
cmd("RPUSHX").arg(key).arg(value)
}
// set commands
/// Add one or more members to a set.
fn sadd<K: ToRedisArgs, M: ToRedisArgs>(key: K, member: M) {
cmd("SADD").arg(key).arg(member)
}
/// Get the number of members in a set.
fn scard<K: ToRedisArgs>(key: K) {
cmd("SCARD").arg(key)
}
/// Subtract multiple sets.
fn sdiff<K: ToRedisArgs>(keys: K) {
cmd("SDIFF").arg(keys)
}
/// Subtract multiple sets and store the resulting set in a key.
fn sdiffstore<K: ToRedisArgs>(dstkey: K, keys: K) {
cmd("SDIFFSTORE").arg(dstkey).arg(keys)
}
/// Intersect multiple sets.
fn sinter<K: ToRedisArgs>(keys: K) {
cmd("SINTER").arg(keys)
}
/// Intersect multiple sets and store the resulting set in a key.
fn sinterstore<K: ToRedisArgs>(dstkey: K, keys: K) {
cmd("SINTERSTORE").arg(dstkey).arg(keys)
}
/// Determine if a given value is a member of a set.
fn sismember<K: ToRedisArgs, M: ToRedisArgs>(key: K, member: M) {
cmd("SISMEMBER").arg(key).arg(member)
}
/// Get all the members in a set.
fn smembers<K: ToRedisArgs>(key: K) {
cmd("SMEMBERS").arg(key)
}
/// Move a member from one set to another.
fn smove<K: ToRedisArgs, M: ToRedisArgs>(srckey: K, dstkey: K, member: M) {
cmd("SMOVE").arg(srckey).arg(dstkey).arg(member)
}
/// Remove and return a random member from a set.
fn spop<K: ToRedisArgs>(key: K) {
cmd("SPOP").arg(key)
}
/// Get one random member from a set.
fn srandmember<K: ToRedisArgs>(key: K) {
cmd("SRANDMEMBER").arg(key)
}
/// Get multiple random members from a set.
fn srandmember_multiple<K: ToRedisArgs>(key: K, count: usize) {
cmd("SRANDMEMBER").arg(key).arg(count)
}
/// Remove one or more members from a set.
fn srem<K: ToRedisArgs, M: ToRedisArgs>(key: K, member: M) {
cmd("SREM").arg(key).arg(member)
}
/// Add multiple sets.
fn sunion<K: ToRedisArgs>(keys: K) {
cmd("SUNION").arg(keys)
}
/// Add multiple sets and store the resulting set in a key.
fn sunionstore<K: ToRedisArgs>(dstkey: K, keys: K) {
cmd("SUNIONSTORE").arg(dstkey).arg(keys)
}
// sorted set commands
/// Add one member to a sorted set, or update its score if it already exists.
fn zadd<K: ToRedisArgs, S: ToRedisArgs, M: ToRedisArgs>(key: K, member: M, score: S) {
cmd("ZADD").arg(key).arg(score).arg(member)
}
/// Add multiple members to a sorted set, or update its score if it already exists.
fn zadd_multiple<K: ToRedisArgs, S: ToRedisArgs, M: ToRedisArgs>(key: K, items: &'a [(S, M)]) {
cmd("ZADD").arg(key).arg(items)
}
/// Get the number of members in a sorted set.
fn zcard<K: ToRedisArgs>(key: K) {
cmd("ZCARD").arg(key)
}
/// Count the members in a sorted set with scores within the given values.
fn zcount<K: ToRedisArgs, M: ToRedisArgs, MM: ToRedisArgs>(key: K, min: M, max: MM) {
cmd("ZCOUNT").arg(key).arg(min).arg(max)
}
/// Increments the member in a sorted set at key by delta.
/// If the member does not exist, it is added with delta as its score.
fn zincr<K: ToRedisArgs, M: ToRedisArgs, D: ToRedisArgs>(key: K, member: M, delta: D) {
cmd("ZINCRBY").arg(key).arg(delta).arg(member)
}
/// Intersect multiple sorted sets and store the resulting sorted set in
/// a new key using SUM as aggregation function.
fn zinterstore<K: ToRedisArgs>(dstkey: K, keys: &'a [K]) {
cmd("ZINTERSTORE").arg(dstkey).arg(keys.len()).arg(keys)
}
/// Intersect multiple sorted sets and store the resulting sorted set in
/// a new key using MIN as aggregation function.
fn zinterstore_min<K: ToRedisArgs>(dstkey: K, keys: &'a [K]) {
cmd("ZINTERSTORE").arg(dstkey).arg(keys.len()).arg(keys).arg("AGGREGATE").arg("MIN")
}
/// Intersect multiple sorted sets and store the resulting sorted set in
/// a new key using MAX as aggregation function.
fn zinterstore_max<K: ToRedisArgs>(dstkey: K, keys: &'a [K]) {
cmd("ZINTERSTORE").arg(dstkey).arg(keys.len()).arg(keys).arg("AGGREGATE").arg("MAX")
}
/// [`Commands::zinterstore`], but with the ability to specify a
/// multiplication factor for each sorted set by pairing one with each key
/// in a tuple.
fn zinterstore_weights<K: ToRedisArgs, W: ToRedisArgs>(dstkey: K, keys: &'a [(K, W)]) {
let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight)| (key, weight)).unzip();
cmd("ZINTERSTORE").arg(dstkey).arg(keys.len()).arg(keys).arg("WEIGHTS").arg(weights)
}
/// [`Commands::zinterstore_min`], but with the ability to specify a
/// multiplication factor for each sorted set by pairing one with each key
/// in a tuple.
fn zinterstore_min_weights<K: ToRedisArgs, W: ToRedisArgs>(dstkey: K, keys: &'a [(K, W)]) {
let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight)| (key, weight)).unzip();
cmd("ZINTERSTORE").arg(dstkey).arg(keys.len()).arg(keys).arg("AGGREGATE").arg("MIN").arg("WEIGHTS").arg(weights)
}
/// [`Commands::zinterstore_max`], but with the ability to specify a
/// multiplication factor for each sorted set by pairing one with each key
/// in a tuple.
fn zinterstore_max_weights<K: ToRedisArgs, W: ToRedisArgs>(dstkey: K, keys: &'a [(K, W)]) {
let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight)| (key, weight)).unzip();
cmd("ZINTERSTORE").arg(dstkey).arg(keys.len()).arg(keys).arg("AGGREGATE").arg("MAX").arg("WEIGHTS").arg(weights)
}
/// Count the number of members in a sorted set between a given lexicographical range.
fn zlexcount<K: ToRedisArgs, L: ToRedisArgs>(key: K, min: L, max: L) {
cmd("ZLEXCOUNT").arg(key).arg(min).arg(max)
}
/// Removes and returns up to count members with the highest scores in a sorted set
fn zpopmax<K: ToRedisArgs>(key: K, count: isize) {
cmd("ZPOPMAX").arg(key).arg(count)
}
/// Removes and returns up to count members with the lowest scores in a sorted set
fn zpopmin<K: ToRedisArgs>(key: K, count: isize) {
cmd("ZPOPMIN").arg(key).arg(count)
}
/// Removes and returns up to count members with the highest scores,
/// from the first non-empty sorted set in the provided list of key names.
fn zmpop_max<K: ToRedisArgs>(keys: &'a [K], count: isize) {
cmd("ZMPOP").arg(keys.len()).arg(keys).arg("MAX").arg("COUNT").arg(count)
}
/// Removes and returns up to count members with the lowest scores,
/// from the first non-empty sorted set in the provided list of key names.
fn zmpop_min<K: ToRedisArgs>(keys: &'a [K], count: isize) {
cmd("ZMPOP").arg(keys.len()).arg(keys).arg("MIN").arg("COUNT").arg(count)
}
/// Return up to count random members in a sorted set (or 1 if `count == None`)
fn zrandmember<K: ToRedisArgs>(key: K, count: Option<isize>) {
cmd("ZRANDMEMBER").arg(key).arg(count)
}
/// Return up to count random members in a sorted set with scores
fn zrandmember_withscores<K: ToRedisArgs>(key: K, count: isize) {
cmd("ZRANDMEMBER").arg(key).arg(count).arg("WITHSCORES")
}
/// Return a range of members in a sorted set, by index
fn zrange<K: ToRedisArgs>(key: K, start: isize, stop: isize) {
cmd("ZRANGE").arg(key).arg(start).arg(stop)
}
/// Return a range of members in a sorted set, by index with scores.
fn zrange_withscores<K: ToRedisArgs>(key: K, start: isize, stop: isize) {
cmd("ZRANGE").arg(key).arg(start).arg(stop).arg("WITHSCORES")
}
/// Return a range of members in a sorted set, by lexicographical range.
fn zrangebylex<K: ToRedisArgs, M: ToRedisArgs, MM: ToRedisArgs>(key: K, min: M, max: MM) {
cmd("ZRANGEBYLEX").arg(key).arg(min).arg(max)
}
/// Return a range of members in a sorted set, by lexicographical
/// range with offset and limit.
fn zrangebylex_limit<K: ToRedisArgs, M: ToRedisArgs, MM: ToRedisArgs>(
key: K, min: M, max: MM, offset: isize, count: isize) {
cmd("ZRANGEBYLEX").arg(key).arg(min).arg(max).arg("LIMIT").arg(offset).arg(count)
}
/// Return a range of members in a sorted set, by lexicographical range.
fn zrevrangebylex<K: ToRedisArgs, MM: ToRedisArgs, M: ToRedisArgs>(key: K, max: MM, min: M) {
cmd("ZREVRANGEBYLEX").arg(key).arg(max).arg(min)
}
/// Return a range of members in a sorted set, by lexicographical
/// range with offset and limit.
fn zrevrangebylex_limit<K: ToRedisArgs, MM: ToRedisArgs, M: ToRedisArgs>(
key: K, max: MM, min: M, offset: isize, count: isize) {
cmd("ZREVRANGEBYLEX").arg(key).arg(max).arg(min).arg("LIMIT").arg(offset).arg(count)
}
/// Return a range of members in a sorted set, by score.
fn zrangebyscore<K: ToRedisArgs, M: ToRedisArgs, MM: ToRedisArgs>(key: K, min: M, max: MM) {
cmd("ZRANGEBYSCORE").arg(key).arg(min).arg(max)
}
/// Return a range of members in a sorted set, by score with scores.
fn zrangebyscore_withscores<K: ToRedisArgs, M: ToRedisArgs, MM: ToRedisArgs>(key: K, min: M, max: MM) {
cmd("ZRANGEBYSCORE").arg(key).arg(min).arg(max).arg("WITHSCORES")
}
/// Return a range of members in a sorted set, by score with limit.
fn zrangebyscore_limit<K: ToRedisArgs, M: ToRedisArgs, MM: ToRedisArgs>
(key: K, min: M, max: MM, offset: isize, count: isize) {
cmd("ZRANGEBYSCORE").arg(key).arg(min).arg(max).arg("LIMIT").arg(offset).arg(count)
}
/// Return a range of members in a sorted set, by score with limit with scores.
fn zrangebyscore_limit_withscores<K: ToRedisArgs, M: ToRedisArgs, MM: ToRedisArgs>
(key: K, min: M, max: MM, offset: isize, count: isize) {
cmd("ZRANGEBYSCORE").arg(key).arg(min).arg(max).arg("WITHSCORES")
.arg("LIMIT").arg(offset).arg(count)
}
/// Determine the index of a member in a sorted set.
fn zrank<K: ToRedisArgs, M: ToRedisArgs>(key: K, member: M) {
cmd("ZRANK").arg(key).arg(member)
}
/// Remove one or more members from a sorted set.
fn zrem<K: ToRedisArgs, M: ToRedisArgs>(key: K, members: M) {
cmd("ZREM").arg(key).arg(members)
}
/// Remove all members in a sorted set between the given lexicographical range.
fn zrembylex<K: ToRedisArgs, M: ToRedisArgs, MM: ToRedisArgs>(key: K, min: M, max: MM) {
cmd("ZREMRANGEBYLEX").arg(key).arg(min).arg(max)
}
/// Remove all members in a sorted set within the given indexes.
fn zremrangebyrank<K: ToRedisArgs>(key: K, start: isize, stop: isize) {
cmd("ZREMRANGEBYRANK").arg(key).arg(start).arg(stop)
}
/// Remove all members in a sorted set within the given scores.
fn zrembyscore<K: ToRedisArgs, M: ToRedisArgs, MM: ToRedisArgs>(key: K, min: M, max: MM) {
cmd("ZREMRANGEBYSCORE").arg(key).arg(min).arg(max)
}
/// Return a range of members in a sorted set, by index, with scores
/// ordered from high to low.
fn zrevrange<K: ToRedisArgs>(key: K, start: isize, stop: isize) {
cmd("ZREVRANGE").arg(key).arg(start).arg(stop)
}
/// Return a range of members in a sorted set, by index, with scores
/// ordered from high to low.
fn zrevrange_withscores<K: ToRedisArgs>(key: K, start: isize, stop: isize) {
cmd("ZREVRANGE").arg(key).arg(start).arg(stop).arg("WITHSCORES")
}
/// Return a range of members in a sorted set, by score.
fn zrevrangebyscore<K: ToRedisArgs, MM: ToRedisArgs, M: ToRedisArgs>(key: K, max: MM, min: M) {
cmd("ZREVRANGEBYSCORE").arg(key).arg(max).arg(min)
}
/// Return a range of members in a sorted set, by score with scores.
fn zrevrangebyscore_withscores<K: ToRedisArgs, MM: ToRedisArgs, M: ToRedisArgs>(key: K, max: MM, min: M) {
cmd("ZREVRANGEBYSCORE").arg(key).arg(max).arg(min).arg("WITHSCORES")
}
/// Return a range of members in a sorted set, by score with limit.
fn zrevrangebyscore_limit<K: ToRedisArgs, MM: ToRedisArgs, M: ToRedisArgs>
(key: K, max: MM, min: M, offset: isize, count: isize) {
cmd("ZREVRANGEBYSCORE").arg(key).arg(max).arg(min).arg("LIMIT").arg(offset).arg(count)
}
/// Return a range of members in a sorted set, by score with limit with scores.
fn zrevrangebyscore_limit_withscores<K: ToRedisArgs, MM: ToRedisArgs, M: ToRedisArgs>
(key: K, max: MM, min: M, offset: isize, count: isize) {
cmd("ZREVRANGEBYSCORE").arg(key).arg(max).arg(min).arg("WITHSCORES")
.arg("LIMIT").arg(offset).arg(count)
}
/// Determine the index of a member in a sorted set, with scores ordered from high to low.
fn zrevrank<K: ToRedisArgs, M: ToRedisArgs>(key: K, member: M) {
cmd("ZREVRANK").arg(key).arg(member)
}
/// Get the score associated with the given member in a sorted set.
fn zscore<K: ToRedisArgs, M: ToRedisArgs>(key: K, member: M) {
cmd("ZSCORE").arg(key).arg(member)
}
/// Get the scores associated with multiple members in a sorted set.
fn zscore_multiple<K: ToRedisArgs, M: ToRedisArgs>(key: K, members: &'a [M]) {
cmd("ZMSCORE").arg(key).arg(members)
}
/// Unions multiple sorted sets and store the resulting sorted set in
/// a new key using SUM as aggregation function.
fn zunionstore<K: ToRedisArgs>(dstkey: K, keys: &'a [K]) {
cmd("ZUNIONSTORE").arg(dstkey).arg(keys.len()).arg(keys)
}
/// Unions multiple sorted sets and store the resulting sorted set in
/// a new key using MIN as aggregation function.
fn zunionstore_min<K: ToRedisArgs>(dstkey: K, keys: &'a [K]) {
cmd("ZUNIONSTORE").arg(dstkey).arg(keys.len()).arg(keys).arg("AGGREGATE").arg("MIN")
}
/// Unions multiple sorted sets and store the resulting sorted set in
/// a new key using MAX as aggregation function.
fn zunionstore_max<K: ToRedisArgs>(dstkey: K, keys: &'a [K]) {
cmd("ZUNIONSTORE").arg(dstkey).arg(keys.len()).arg(keys).arg("AGGREGATE").arg("MAX")
}
/// [`Commands::zunionstore`], but with the ability to specify a
/// multiplication factor for each sorted set by pairing one with each key
/// in a tuple.
fn zunionstore_weights<K: ToRedisArgs, W: ToRedisArgs>(dstkey: K, keys: &'a [(K, W)]) {
let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight)| (key, weight)).unzip();
cmd("ZUNIONSTORE").arg(dstkey).arg(keys.len()).arg(keys).arg("WEIGHTS").arg(weights)
}
/// [`Commands::zunionstore_min`], but with the ability to specify a
/// multiplication factor for each sorted set by pairing one with each key
/// in a tuple.
fn zunionstore_min_weights<K: ToRedisArgs, W: ToRedisArgs>(dstkey: K, keys: &'a [(K, W)]) {
let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight)| (key, weight)).unzip();
cmd("ZUNIONSTORE").arg(dstkey).arg(keys.len()).arg(keys).arg("AGGREGATE").arg("MIN").arg("WEIGHTS").arg(weights)
}
/// [`Commands::zunionstore_max`], but with the ability to specify a
/// multiplication factor for each sorted set by pairing one with each key
/// in a tuple.
fn zunionstore_max_weights<K: ToRedisArgs, W: ToRedisArgs>(dstkey: K, keys: &'a [(K, W)]) {
let (keys, weights): (Vec<&K>, Vec<&W>) = keys.iter().map(|(key, weight)| (key, weight)).unzip();
cmd("ZUNIONSTORE").arg(dstkey).arg(keys.len()).arg(keys).arg("AGGREGATE").arg("MAX").arg("WEIGHTS").arg(weights)
}
// hyperloglog commands
/// Adds the specified elements to the specified HyperLogLog.
fn pfadd<K: ToRedisArgs, E: ToRedisArgs>(key: K, element: E) {
cmd("PFADD").arg(key).arg(element)
}
/// Return the approximated cardinality of the set(s) observed by the
/// HyperLogLog at key(s).
fn pfcount<K: ToRedisArgs>(key: K) {
cmd("PFCOUNT").arg(key)
}
/// Merge N different HyperLogLogs into a single one.
fn pfmerge<K: ToRedisArgs>(dstkey: K, srckeys: K) {
cmd("PFMERGE").arg(dstkey).arg(srckeys)
}
/// Posts a message to the given channel.
fn publish<K: ToRedisArgs, E: ToRedisArgs>(channel: K, message: E) {
cmd("PUBLISH").arg(channel).arg(message)
}
// Object commands
/// Returns the encoding of a key.
fn object_encoding<K: ToRedisArgs>(key: K) {
cmd("OBJECT").arg("ENCODING").arg(key)
}
/// Returns the time in seconds since the last access of a key.
fn object_idletime<K: ToRedisArgs>(key: K) {
cmd("OBJECT").arg("IDLETIME").arg(key)
}
/// Returns the logarithmic access frequency counter of a key.
fn object_freq<K: ToRedisArgs>(key: K) {
cmd("OBJECT").arg("FREQ").arg(key)
}
/// Returns the reference count of a key.
fn object_refcount<K: ToRedisArgs>(key: K) {
cmd("OBJECT").arg("REFCOUNT").arg(key)
}
// ACL commands
/// When Redis is configured to use an ACL file (with the aclfile
/// configuration option), this command will reload the ACLs from the file,
/// replacing all the current ACL rules with the ones defined in the file.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_load<>() {
cmd("ACL").arg("LOAD")
}
/// When Redis is configured to use an ACL file (with the aclfile
/// configuration option), this command will save the currently defined
/// ACLs from the server memory to the ACL file.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_save<>() {
cmd("ACL").arg("SAVE")
}
/// Shows the currently active ACL rules in the Redis server.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_list<>() {
cmd("ACL").arg("LIST")
}
/// Shows a list of all the usernames of the currently configured users in
/// the Redis ACL system.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_users<>() {
cmd("ACL").arg("USERS")
}
/// Returns all the rules defined for an existing ACL user.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_getuser<K: ToRedisArgs>(username: K) {
cmd("ACL").arg("GETUSER").arg(username)
}
/// Creates an ACL user without any privilege.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_setuser<K: ToRedisArgs>(username: K) {
cmd("ACL").arg("SETUSER").arg(username)
}
/// Creates an ACL user with the specified rules or modify the rules of
/// an existing user.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_setuser_rules<K: ToRedisArgs>(username: K, rules: &'a [acl::Rule]) {
cmd("ACL").arg("SETUSER").arg(username).arg(rules)
}
/// Delete all the specified ACL users and terminate all the connections
/// that are authenticated with such users.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_deluser<K: ToRedisArgs>(usernames: &'a [K]) {
cmd("ACL").arg("DELUSER").arg(usernames)
}
/// Shows the available ACL categories.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_cat<>() {
cmd("ACL").arg("CAT")
}
/// Shows all the Redis commands in the specified category.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_cat_categoryname<K: ToRedisArgs>(categoryname: K) {
cmd("ACL").arg("CAT").arg(categoryname)
}
/// Generates a 256-bits password starting from /dev/urandom if available.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_genpass<>() {
cmd("ACL").arg("GENPASS")
}
/// Generates a 1-to-1024-bits password starting from /dev/urandom if available.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_genpass_bits<>(bits: isize) {
cmd("ACL").arg("GENPASS").arg(bits)
}
/// Returns the username the current connection is authenticated with.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_whoami<>() {
cmd("ACL").arg("WHOAMI")
}
/// Shows a list of recent ACL security events
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_log<>(count: isize) {
cmd("ACL").arg("LOG").arg(count)
}
/// Clears the ACL log.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_log_reset<>() {
cmd("ACL").arg("LOG").arg("RESET")
}
/// Returns a helpful text describing the different subcommands.
#[cfg(feature = "acl")]
#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
fn acl_help<>() {
cmd("ACL").arg("HELP")
}
//
// geospatial commands
//
/// Adds the specified geospatial items to the specified key.
///
/// Every member has to be written as a tuple of `(longitude, latitude,
/// member_name)`. It can be a single tuple, or a vector of tuples.
///
/// `longitude, latitude` can be set using [`redis::geo::Coord`][1].
///
/// [1]: ./geo/struct.Coord.html
///
/// Returns the number of elements added to the sorted set, not including
/// elements already existing for which the score was updated.
///
/// # Example
///
/// ```rust,no_run
/// use redis::{Commands, Connection, RedisResult};
/// use redis::geo::Coord;
///
/// fn add_point(con: &mut Connection) -> RedisResult<isize> {
/// con.geo_add("my_gis", (Coord::lon_lat(13.361389, 38.115556), "Palermo"))
/// }
///
/// fn add_point_with_tuples(con: &mut Connection) -> RedisResult<isize> {
/// con.geo_add("my_gis", ("13.361389", "38.115556", "Palermo"))
/// }
///
/// fn add_many_points(con: &mut Connection) -> RedisResult<isize> {
/// con.geo_add("my_gis", &[
/// ("13.361389", "38.115556", "Palermo"),
/// ("15.087269", "37.502669", "Catania")
/// ])
/// }
/// ```
#[cfg(feature = "geospatial")]
#[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
fn geo_add<K: ToRedisArgs, M: ToRedisArgs>(key: K, members: M) {
cmd("GEOADD").arg(key).arg(members)
}
/// Return the distance between two members in the geospatial index
/// represented by the sorted set.
///
/// If one or both the members are missing, the command returns NULL, so
/// it may be convenient to parse its response as either `Option<f64>` or
/// `Option<String>`.
///
/// # Example
///
/// ```rust,no_run
/// use redis::{Commands, RedisResult};
/// use redis::geo::Unit;
///
/// fn get_dists(con: &mut redis::Connection) {
/// let x: RedisResult<f64> = con.geo_dist(
/// "my_gis",
/// "Palermo",
/// "Catania",
/// Unit::Kilometers
/// );
/// // x is Ok(166.2742)
///
/// let x: RedisResult<Option<f64>> = con.geo_dist(
/// "my_gis",
/// "Palermo",
/// "Atlantis",
/// Unit::Meters
/// );
/// // x is Ok(None)
/// }
/// ```
#[cfg(feature = "geospatial")]
#[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
fn geo_dist<K: ToRedisArgs, M1: ToRedisArgs, M2: ToRedisArgs>(
key: K,
member1: M1,
member2: M2,
unit: geo::Unit
) {
cmd("GEODIST")
.arg(key)
.arg(member1)
.arg(member2)
.arg(unit)
}
/// Return valid [Geohash][1] strings representing the position of one or
/// more members of the geospatial index represented by the sorted set at
/// key.
///
/// [1]: https://en.wikipedia.org/wiki/Geohash
///
/// # Example
///
/// ```rust,no_run
/// use redis::{Commands, RedisResult};
///
/// fn get_hash(con: &mut redis::Connection) {
/// let x: RedisResult<Vec<String>> = con.geo_hash("my_gis", "Palermo");
/// // x is vec!["sqc8b49rny0"]
///
/// let x: RedisResult<Vec<String>> = con.geo_hash("my_gis", &["Palermo", "Catania"]);
/// // x is vec!["sqc8b49rny0", "sqdtr74hyu0"]
/// }
/// ```
#[cfg(feature = "geospatial")]
#[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
fn geo_hash<K: ToRedisArgs, M: ToRedisArgs>(key: K, members: M) {
cmd("GEOHASH").arg(key).arg(members)
}
/// Return the positions of all the specified members of the geospatial
/// index represented by the sorted set at key.
///
/// Every position is a pair of `(longitude, latitude)`. [`redis::geo::Coord`][1]
/// can be used to convert these value in a struct.
///
/// [1]: ./geo/struct.Coord.html
///
/// # Example
///
/// ```rust,no_run
/// use redis::{Commands, RedisResult};
/// use redis::geo::Coord;
///
/// fn get_position(con: &mut redis::Connection) {
/// let x: RedisResult<Vec<Vec<f64>>> = con.geo_pos("my_gis", &["Palermo", "Catania"]);
/// // x is [ [ 13.361389, 38.115556 ], [ 15.087269, 37.502669 ] ];
///
/// let x: Vec<Coord<f64>> = con.geo_pos("my_gis", "Palermo").unwrap();
/// // x[0].longitude is 13.361389
/// // x[0].latitude is 38.115556
/// }
/// ```
#[cfg(feature = "geospatial")]
#[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
fn geo_pos<K: ToRedisArgs, M: ToRedisArgs>(key: K, members: M) {
cmd("GEOPOS").arg(key).arg(members)
}
/// Return the members of a sorted set populated with geospatial information
/// using [`geo_add`](#method.geo_add), which are within the borders of the area
/// specified with the center location and the maximum distance from the center
/// (the radius).
///
/// Every item in the result can be read with [`redis::geo::RadiusSearchResult`][1],
/// which support the multiple formats returned by `GEORADIUS`.
///
/// [1]: ./geo/struct.RadiusSearchResult.html
///
/// ```rust,no_run
/// use redis::{Commands, RedisResult};
/// use redis::geo::{RadiusOptions, RadiusSearchResult, RadiusOrder, Unit};
///
/// fn radius(con: &mut redis::Connection) -> Vec<RadiusSearchResult> {
/// let opts = RadiusOptions::default().with_dist().order(RadiusOrder::Asc);
/// con.geo_radius("my_gis", 15.90, 37.21, 51.39, Unit::Kilometers, opts).unwrap()
/// }
/// ```
#[cfg(feature = "geospatial")]
#[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
fn geo_radius<K: ToRedisArgs>(
key: K,
longitude: f64,
latitude: f64,
radius: f64,
unit: geo::Unit,
options: geo::RadiusOptions
) {
cmd("GEORADIUS")
.arg(key)
.arg(longitude)
.arg(latitude)
.arg(radius)
.arg(unit)
.arg(options)
}
/// Retrieve members selected by distance with the center of `member`. The
/// member itself is always contained in the results.
#[cfg(feature = "geospatial")]
#[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
fn geo_radius_by_member<K: ToRedisArgs, M: ToRedisArgs>(
key: K,
member: M,
radius: f64,
unit: geo::Unit,
options: geo::RadiusOptions
) {
cmd("GEORADIUSBYMEMBER")
.arg(key)
.arg(member)
.arg(radius)
.arg(unit)
.arg(options)
}
//
// streams commands
//
/// Ack pending stream messages checked out by a consumer.
///
/// ```text
/// XACK <key> <group> <id> <id> ... <id>
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xack<K: ToRedisArgs, G: ToRedisArgs, I: ToRedisArgs>(
key: K,
group: G,
ids: &'a [I]) {
cmd("XACK")
.arg(key)
.arg(group)
.arg(ids)
}
/// Add a stream message by `key`. Use `*` as the `id` for the current timestamp.
///
/// ```text
/// XADD key <ID or *> [field value] [field value] ...
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xadd<K: ToRedisArgs, ID: ToRedisArgs, F: ToRedisArgs, V: ToRedisArgs>(
key: K,
id: ID,
items: &'a [(F, V)]
) {
cmd("XADD").arg(key).arg(id).arg(items)
}
/// BTreeMap variant for adding a stream message by `key`.
/// Use `*` as the `id` for the current timestamp.
///
/// ```text
/// XADD key <ID or *> [rust BTreeMap] ...
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xadd_map<K: ToRedisArgs, ID: ToRedisArgs, BTM: ToRedisArgs>(
key: K,
id: ID,
map: BTM
) {
cmd("XADD").arg(key).arg(id).arg(map)
}
/// Add a stream message while capping the stream at a maxlength.
///
/// ```text
/// XADD key [MAXLEN [~|=] <count>] <ID or *> [field value] [field value] ...
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xadd_maxlen<
K: ToRedisArgs,
ID: ToRedisArgs,
F: ToRedisArgs,
V: ToRedisArgs
>(
key: K,
maxlen: streams::StreamMaxlen,
id: ID,
items: &'a [(F, V)]
) {
cmd("XADD")
.arg(key)
.arg(maxlen)
.arg(id)
.arg(items)
}
/// BTreeMap variant for adding a stream message while capping the stream at a maxlength.
///
/// ```text
/// XADD key [MAXLEN [~|=] <count>] <ID or *> [rust BTreeMap] ...
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xadd_maxlen_map<K: ToRedisArgs, ID: ToRedisArgs, BTM: ToRedisArgs>(
key: K,
maxlen: streams::StreamMaxlen,
id: ID,
map: BTM
) {
cmd("XADD")
.arg(key)
.arg(maxlen)
.arg(id)
.arg(map)
}
/// Claim pending, unacked messages, after some period of time,
/// currently checked out by another consumer.
///
/// This method only accepts the must-have arguments for claiming messages.
/// If optional arguments are required, see `xclaim_options` below.
///
/// ```text
/// XCLAIM <key> <group> <consumer> <min-idle-time> [<ID-1> <ID-2>]
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xclaim<K: ToRedisArgs, G: ToRedisArgs, C: ToRedisArgs, MIT: ToRedisArgs, ID: ToRedisArgs>(
key: K,
group: G,
consumer: C,
min_idle_time: MIT,
ids: &'a [ID]
) {
cmd("XCLAIM")
.arg(key)
.arg(group)
.arg(consumer)
.arg(min_idle_time)
.arg(ids)
}
/// This is the optional arguments version for claiming unacked, pending messages
/// currently checked out by another consumer.
///
/// ```no_run
/// use redis::{Connection,Commands,RedisResult};
/// use redis::streams::{StreamClaimOptions,StreamClaimReply};
/// let client = redis::Client::open("redis://127.0.0.1/0").unwrap();
/// let mut con = client.get_connection().unwrap();
///
/// // Claim all pending messages for key "k1",
/// // from group "g1", checked out by consumer "c1"
/// // for 10ms with RETRYCOUNT 2 and FORCE
///
/// let opts = StreamClaimOptions::default()
/// .with_force()
/// .retry(2);
/// let results: RedisResult<StreamClaimReply> =
/// con.xclaim_options("k1", "g1", "c1", 10, &["0"], opts);
///
/// // All optional arguments return a `Result<StreamClaimReply>` with one exception:
/// // Passing JUSTID returns only the message `id` and omits the HashMap for each message.
///
/// let opts = StreamClaimOptions::default()
/// .with_justid();
/// let results: RedisResult<Vec<String>> =
/// con.xclaim_options("k1", "g1", "c1", 10, &["0"], opts);
/// ```
///
/// ```text
/// XCLAIM <key> <group> <consumer> <min-idle-time> <ID-1> <ID-2>
/// [IDLE <milliseconds>] [TIME <mstime>] [RETRYCOUNT <count>]
/// [FORCE] [JUSTID]
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xclaim_options<
K: ToRedisArgs,
G: ToRedisArgs,
C: ToRedisArgs,
MIT: ToRedisArgs,
ID: ToRedisArgs
>(
key: K,
group: G,
consumer: C,
min_idle_time: MIT,
ids: &'a [ID],
options: streams::StreamClaimOptions
) {
cmd("XCLAIM")
.arg(key)
.arg(group)
.arg(consumer)
.arg(min_idle_time)
.arg(ids)
.arg(options)
}
/// Deletes a list of `id`s for a given stream `key`.
///
/// ```text
/// XDEL <key> [<ID1> <ID2> ... <IDN>]
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xdel<K: ToRedisArgs, ID: ToRedisArgs>(
key: K,
ids: &'a [ID]
) {
cmd("XDEL").arg(key).arg(ids)
}
/// This command is used for creating a consumer `group`. It expects the stream key
/// to already exist. Otherwise, use `xgroup_create_mkstream` if it doesn't.
/// The `id` is the starting message id all consumers should read from. Use `$` If you want
/// all consumers to read from the last message added to stream.
///
/// ```text
/// XGROUP CREATE <key> <groupname> <id or $>
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xgroup_create<K: ToRedisArgs, G: ToRedisArgs, ID: ToRedisArgs>(
key: K,
group: G,
id: ID
) {
cmd("XGROUP")
.arg("CREATE")
.arg(key)
.arg(group)
.arg(id)
}
/// This is the alternate version for creating a consumer `group`
/// which makes the stream if it doesn't exist.
///
/// ```text
/// XGROUP CREATE <key> <groupname> <id or $> [MKSTREAM]
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xgroup_create_mkstream<
K: ToRedisArgs,
G: ToRedisArgs,
ID: ToRedisArgs
>(
key: K,
group: G,
id: ID
) {
cmd("XGROUP")
.arg("CREATE")
.arg(key)
.arg(group)
.arg(id)
.arg("MKSTREAM")
}
/// Alter which `id` you want consumers to begin reading from an existing
/// consumer `group`.
///
/// ```text
/// XGROUP SETID <key> <groupname> <id or $>
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xgroup_setid<K: ToRedisArgs, G: ToRedisArgs, ID: ToRedisArgs>(
key: K,
group: G,
id: ID
) {
cmd("XGROUP")
.arg("SETID")
.arg(key)
.arg(group)
.arg(id)
}
/// Destroy an existing consumer `group` for a given stream `key`
///
/// ```text
/// XGROUP SETID <key> <groupname> <id or $>
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xgroup_destroy<K: ToRedisArgs, G: ToRedisArgs>(
key: K,
group: G
) {
cmd("XGROUP").arg("DESTROY").arg(key).arg(group)
}
/// This deletes a `consumer` from an existing consumer `group`
/// for given stream `key.
///
/// ```text
/// XGROUP DELCONSUMER <key> <groupname> <consumername>
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xgroup_delconsumer<K: ToRedisArgs, G: ToRedisArgs, C: ToRedisArgs>(
key: K,
group: G,
consumer: C
) {
cmd("XGROUP")
.arg("DELCONSUMER")
.arg(key)
.arg(group)
.arg(consumer)
}
/// This returns all info details about
/// which consumers have read messages for given consumer `group`.
/// Take note of the StreamInfoConsumersReply return type.
///
/// *It's possible this return value might not contain new fields
/// added by Redis in future versions.*
///
/// ```text
/// XINFO CONSUMERS <key> <group>
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xinfo_consumers<K: ToRedisArgs, G: ToRedisArgs>(
key: K,
group: G
) {
cmd("XINFO")
.arg("CONSUMERS")
.arg(key)
.arg(group)
}
/// Returns all consumer `group`s created for a given stream `key`.
/// Take note of the StreamInfoGroupsReply return type.
///
/// *It's possible this return value might not contain new fields
/// added by Redis in future versions.*
///
/// ```text
/// XINFO GROUPS <key>
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xinfo_groups<K: ToRedisArgs>(key: K) {
cmd("XINFO").arg("GROUPS").arg(key)
}
/// Returns info about high-level stream details
/// (first & last message `id`, length, number of groups, etc.)
/// Take note of the StreamInfoStreamReply return type.
///
/// *It's possible this return value might not contain new fields
/// added by Redis in future versions.*
///
/// ```text
/// XINFO STREAM <key>
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xinfo_stream<K: ToRedisArgs>(key: K) {
cmd("XINFO").arg("STREAM").arg(key)
}
/// Returns the number of messages for a given stream `key`.
///
/// ```text
/// XLEN <key>
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xlen<K: ToRedisArgs>(key: K) {
cmd("XLEN").arg(key)
}
/// This is a basic version of making XPENDING command calls which only
/// passes a stream `key` and consumer `group` and it
/// returns details about which consumers have pending messages
/// that haven't been acked.
///
/// You can use this method along with
/// `xclaim` or `xclaim_options` for determining which messages
/// need to be retried.
///
/// Take note of the StreamPendingReply return type.
///
/// ```text
/// XPENDING <key> <group> [<start> <stop> <count> [<consumer>]]
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xpending<K: ToRedisArgs, G: ToRedisArgs>(
key: K,
group: G
) {
cmd("XPENDING").arg(key).arg(group)
}
/// This XPENDING version returns a list of all messages over the range.
/// You can use this for paginating pending messages (but without the message HashMap).
///
/// Start and end follow the same rules `xrange` args. Set start to `-`
/// and end to `+` for the entire stream.
///
/// Take note of the StreamPendingCountReply return type.
///
/// ```text
/// XPENDING <key> <group> <start> <stop> <count>
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xpending_count<
K: ToRedisArgs,
G: ToRedisArgs,
S: ToRedisArgs,
E: ToRedisArgs,
C: ToRedisArgs
>(
key: K,
group: G,
start: S,
end: E,
count: C
) {
cmd("XPENDING")
.arg(key)
.arg(group)
.arg(start)
.arg(end)
.arg(count)
}
/// An alternate version of `xpending_count` which filters by `consumer` name.
///
/// Start and end follow the same rules `xrange` args. Set start to `-`
/// and end to `+` for the entire stream.
///
/// Take note of the StreamPendingCountReply return type.
///
/// ```text
/// XPENDING <key> <group> <start> <stop> <count> <consumer>
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xpending_consumer_count<
K: ToRedisArgs,
G: ToRedisArgs,
S: ToRedisArgs,
E: ToRedisArgs,
C: ToRedisArgs,
CN: ToRedisArgs
>(
key: K,
group: G,
start: S,
end: E,
count: C,
consumer: CN
) {
cmd("XPENDING")
.arg(key)
.arg(group)
.arg(start)
.arg(end)
.arg(count)
.arg(consumer)
}
/// Returns a range of messages in a given stream `key`.
///
/// Set `start` to `-` to begin at the first message.
/// Set `end` to `+` to end the most recent message.
/// You can pass message `id` to both `start` and `end`.
///
/// Take note of the StreamRangeReply return type.
///
/// ```text
/// XRANGE key start end
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xrange<K: ToRedisArgs, S: ToRedisArgs, E: ToRedisArgs>(
key: K,
start: S,
end: E
) {
cmd("XRANGE").arg(key).arg(start).arg(end)
}
/// A helper method for automatically returning all messages in a stream by `key`.
/// **Use with caution!**
///
/// ```text
/// XRANGE key - +
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xrange_all<K: ToRedisArgs>(key: K) {
cmd("XRANGE").arg(key).arg("-").arg("+")
}
/// A method for paginating a stream by `key`.
///
/// ```text
/// XRANGE key start end [COUNT <n>]
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xrange_count<K: ToRedisArgs, S: ToRedisArgs, E: ToRedisArgs, C: ToRedisArgs>(
key: K,
start: S,
end: E,
count: C
) {
cmd("XRANGE")
.arg(key)
.arg(start)
.arg(end)
.arg("COUNT")
.arg(count)
}
/// Read a list of `id`s for each stream `key`.
/// This is the basic form of reading streams.
/// For more advanced control, like blocking, limiting, or reading by consumer `group`,
/// see `xread_options`.
///
/// ```text
/// XREAD STREAMS key_1 key_2 ... key_N ID_1 ID_2 ... ID_N
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xread<K: ToRedisArgs, ID: ToRedisArgs>(
keys: &'a [K],
ids: &'a [ID]
) {
cmd("XREAD").arg("STREAMS").arg(keys).arg(ids)
}
/// This method handles setting optional arguments for
/// `XREAD` or `XREADGROUP` Redis commands.
/// ```no_run
/// use redis::{Connection,RedisResult,Commands};
/// use redis::streams::{StreamReadOptions,StreamReadReply};
/// let client = redis::Client::open("redis://127.0.0.1/0").unwrap();
/// let mut con = client.get_connection().unwrap();
///
/// // Read 10 messages from the start of the stream,
/// // without registering as a consumer group.
///
/// let opts = StreamReadOptions::default()
/// .count(10);
/// let results: RedisResult<StreamReadReply> =
/// con.xread_options(&["k1"], &["0"], &opts);
///
/// // Read all undelivered messages for a given
/// // consumer group. Be advised: the consumer group must already
/// // exist before making this call. Also note: we're passing
/// // '>' as the id here, which means all undelivered messages.
///
/// let opts = StreamReadOptions::default()
/// .group("group-1", "consumer-1");
/// let results: RedisResult<StreamReadReply> =
/// con.xread_options(&["k1"], &[">"], &opts);
/// ```
///
/// ```text
/// XREAD [BLOCK <milliseconds>] [COUNT <count>]
/// STREAMS key_1 key_2 ... key_N
/// ID_1 ID_2 ... ID_N
///
/// XREADGROUP [BLOCK <milliseconds>] [COUNT <count>] [NOACK] [GROUP group-name consumer-name]
/// STREAMS key_1 key_2 ... key_N
/// ID_1 ID_2 ... ID_N
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xread_options<K: ToRedisArgs, ID: ToRedisArgs>(
keys: &'a [K],
ids: &'a [ID],
options: &'a streams::StreamReadOptions
) {
cmd(if options.read_only() {
"XREAD"
} else {
"XREADGROUP"
})
.arg(options)
.arg("STREAMS")
.arg(keys)
.arg(ids)
}
/// This is the reverse version of `xrange`.
/// The same rules apply for `start` and `end` here.
///
/// ```text
/// XREVRANGE key end start
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xrevrange<K: ToRedisArgs, E: ToRedisArgs, S: ToRedisArgs>(
key: K,
end: E,
start: S
) {
cmd("XREVRANGE").arg(key).arg(end).arg(start)
}
/// This is the reverse version of `xrange_all`.
/// The same rules apply for `start` and `end` here.
///
/// ```text
/// XREVRANGE key + -
/// ```
fn xrevrange_all<K: ToRedisArgs>(key: K) {
cmd("XREVRANGE").arg(key).arg("+").arg("-")
}
/// This is the reverse version of `xrange_count`.
/// The same rules apply for `start` and `end` here.
///
/// ```text
/// XREVRANGE key end start [COUNT <n>]
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xrevrange_count<K: ToRedisArgs, E: ToRedisArgs, S: ToRedisArgs, C: ToRedisArgs>(
key: K,
end: E,
start: S,
count: C
) {
cmd("XREVRANGE")
.arg(key)
.arg(end)
.arg(start)
.arg("COUNT")
.arg(count)
}
/// Trim a stream `key` to a MAXLEN count.
///
/// ```text
/// XTRIM <key> MAXLEN [~|=] <count> (Same as XADD MAXLEN option)
/// ```
#[cfg(feature = "streams")]
#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
fn xtrim<K: ToRedisArgs>(
key: K,
maxlen: streams::StreamMaxlen
) {
cmd("XTRIM").arg(key).arg(maxlen)
}
}
/// Allows pubsub callbacks to stop receiving messages.
///
/// Arbitrary data may be returned from `Break`.
pub enum ControlFlow<U> {
/// Continues.
Continue,
/// Breaks with a value.
Break(U),
}
/// The PubSub trait allows subscribing to one or more channels
/// and receiving a callback whenever a message arrives.
///
/// Each method handles subscribing to the list of keys, waiting for
/// messages, and unsubscribing from the same list of channels once
/// a ControlFlow::Break is encountered.
///
/// Once (p)subscribe returns Ok(U), the connection is again safe to use
/// for calling other methods.
///
/// # Examples
///
/// ```rust,no_run
/// # fn do_something() -> redis::RedisResult<()> {
/// use redis::{PubSubCommands, ControlFlow};
/// let client = redis::Client::open("redis://127.0.0.1/")?;
/// let mut con = client.get_connection()?;
/// let mut count = 0;
/// con.subscribe(&["foo"], |msg| {
/// // do something with message
/// assert_eq!(msg.get_channel(), Ok(String::from("foo")));
///
/// // increment messages seen counter
/// count += 1;
/// match count {
/// // stop after receiving 10 messages
/// 10 => ControlFlow::Break(()),
/// _ => ControlFlow::Continue,
/// }
/// });
/// # Ok(()) }
/// ```
// TODO In the future, it would be nice to implement Try such that `?` will work
// within the closure.
pub trait PubSubCommands: Sized {
/// Subscribe to a list of channels using SUBSCRIBE and run the provided
/// closure for each message received.
///
/// For every `Msg` passed to the provided closure, either
/// `ControlFlow::Break` or `ControlFlow::Continue` must be returned. This
/// method will not return until `ControlFlow::Break` is observed.
fn subscribe<C, F, U>(&mut self, _: C, _: F) -> RedisResult<U>
where
F: FnMut(Msg) -> ControlFlow<U>,
C: ToRedisArgs;
/// Subscribe to a list of channels using PSUBSCRIBE and run the provided
/// closure for each message received.
///
/// For every `Msg` passed to the provided closure, either
/// `ControlFlow::Break` or `ControlFlow::Continue` must be returned. This
/// method will not return until `ControlFlow::Break` is observed.
fn psubscribe<P, F, U>(&mut self, _: P, _: F) -> RedisResult<U>
where
F: FnMut(Msg) -> ControlFlow<U>,
P: ToRedisArgs;
}
impl<T> Commands for T where T: ConnectionLike {}
#[cfg(feature = "aio")]
impl<T> AsyncCommands for T where T: crate::aio::ConnectionLike + Send + Sized {}
impl PubSubCommands for Connection {
fn subscribe<C, F, U>(&mut self, channels: C, mut func: F) -> RedisResult<U>
where
F: FnMut(Msg) -> ControlFlow<U>,
C: ToRedisArgs,
{
let mut pubsub = self.as_pubsub();
pubsub.subscribe(channels)?;
loop {
let msg = pubsub.get_message()?;
match func(msg) {
ControlFlow::Continue => continue,
ControlFlow::Break(value) => return Ok(value),
}
}
}
fn psubscribe<P, F, U>(&mut self, patterns: P, mut func: F) -> RedisResult<U>
where
F: FnMut(Msg) -> ControlFlow<U>,
P: ToRedisArgs,
{
let mut pubsub = self.as_pubsub();
pubsub.psubscribe(patterns)?;
loop {
let msg = pubsub.get_message()?;
match func(msg) {
ControlFlow::Continue => continue,
ControlFlow::Break(value) => return Ok(value),
}
}
}
}
/// Options for the [LPOS](https://redis.io/commands/lpos) command
///
/// # Example
///
/// ```rust,no_run
/// use redis::{Commands, RedisResult, LposOptions};
/// fn fetch_list_position(
/// con: &mut redis::Connection,
/// key: &str,
/// value: &str,
/// count: usize,
/// rank: isize,
/// maxlen: usize,
/// ) -> RedisResult<Vec<usize>> {
/// let opts = LposOptions::default()
/// .count(count)
/// .rank(rank)
/// .maxlen(maxlen);
/// con.lpos(key, value, opts)
/// }
/// ```
#[derive(Default)]
pub struct LposOptions {
count: Option<usize>,
maxlen: Option<usize>,
rank: Option<isize>,
}
impl LposOptions {
/// Limit the results to the first N matching items.
pub fn count(mut self, n: usize) -> Self {
self.count = Some(n);
self
}
/// Return the value of N from the matching items.
pub fn rank(mut self, n: isize) -> Self {
self.rank = Some(n);
self
}
/// Limit the search to N items in the list.
pub fn maxlen(mut self, n: usize) -> Self {
self.maxlen = Some(n);
self
}
}
impl ToRedisArgs for LposOptions {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
if let Some(n) = self.count {
out.write_arg(b"COUNT");
out.write_arg_fmt(n);
}
if let Some(n) = self.rank {
out.write_arg(b"RANK");
out.write_arg_fmt(n);
}
if let Some(n) = self.maxlen {
out.write_arg(b"MAXLEN");
out.write_arg_fmt(n);
}
}
fn is_single_arg(&self) -> bool {
false
}
}
/// Enum for the LEFT | RIGHT args used by some commands
pub enum Direction {
/// Targets the first element (head) of the list
Left,
/// Targets the last element (tail) of the list
Right,
}
impl ToRedisArgs for Direction {
fn write_redis_args<W>(&self, out: &mut W)
where
W: ?Sized + RedisWrite,
{
let s: &[u8] = match self {
Direction::Left => b"LEFT",
Direction::Right => b"RIGHT",
};
out.write_arg(s);
}
}