use crate::{
client::{prepare_command, PreparedCommand},
resp::{
cmd, deserialize_vec_of_pairs, CollectionResponse, CommandArgs, KeyValueArgsCollection,
KeyValueCollectionResponse, PrimitiveResponse, SingleArg, SingleArgCollection, ToArgs,
},
};
use serde::{de::DeserializeOwned, Deserialize};
/// A group of Redis commands related to [`Hashes`](https://redis.io/docs/data-types/hashes/)
///
/// # See Also
/// [Redis Hash Commands](https://redis.io/commands/?group=hash)
pub trait HashCommands<'a> {
/// Removes the specified fields from the hash stored at key.
///
/// # Return
/// the number of fields that were removed from the hash, not including specified but non existing fields.
///
/// # See Also
/// [](https://redis.io/commands/hdel/)
#[must_use]
fn hdel(self, key: K, fields: C) -> PreparedCommand<'a, Self, usize>
where
Self: Sized,
K: SingleArg,
F: SingleArg,
C: SingleArgCollection,
{
prepare_command(self, cmd("HDEL").arg(key).arg(fields))
}
/// Returns if field is an existing field in the hash stored at key.
///
/// # Return
/// * `true` - if the hash contains field.
/// * `false` - if the hash does not contain field, or key does not exist.
///
/// # See Also
/// [](https://redis.io/commands/hexists/)
#[must_use]
fn hexists(self, key: K, field: F) -> PreparedCommand<'a, Self, bool>
where
Self: Sized,
K: SingleArg,
F: SingleArg,
{
prepare_command(self, cmd("HEXISTS").arg(key).arg(field))
}
/// Returns the value associated with field in the hash stored at key.
///
/// # Return
/// The value associated with field, or nil when field is not present in the hash or key does not exist.
///
/// # See Also
/// [](https://redis.io/commands/hget/)
#[must_use]
fn hget(self, key: K, field: F) -> PreparedCommand<'a, Self, V>
where
Self: Sized,
K: SingleArg,
F: SingleArg,
V: PrimitiveResponse,
{
prepare_command(self, cmd("HGET").arg(key).arg(field))
}
/// Returns all fields and values of the hash stored at key.
///
/// # Return
/// The list of fields and their values stored in the hash, or an empty list when key does not exist.
///
/// # See Also
/// [](https://redis.io/commands/hgetall/)
#[must_use]
fn hgetall(self, key: K) -> PreparedCommand<'a, Self, A>
where
Self: Sized,
K: SingleArg,
F: PrimitiveResponse,
V: PrimitiveResponse,
A: KeyValueCollectionResponse,
{
prepare_command(self, cmd("HGETALL").arg(key))
}
/// Increments the number stored at field in the hash stored at key by increment.
///
/// # Return
/// The value at field after the increment operation.
///
/// # See Also
/// [](https://redis.io/commands/hincrby/)
#[must_use]
fn hincrby(self, key: K, field: F, increment: i64) -> PreparedCommand<'a, Self, i64>
where
Self: Sized,
K: SingleArg,
F: SingleArg,
{
prepare_command(self, cmd("HINCRBY").arg(key).arg(field).arg(increment))
}
/// Increment the specified field of a hash stored at key,
/// and representing a floating point number, by the specified increment.
///
/// # Return
/// The value at field after the increment operation.
///
/// # See Also
/// [](https://redis.io/commands/hincrbyfloat/)
#[must_use]
fn hincrbyfloat(self, key: K, field: F, increment: f64) -> PreparedCommand<'a, Self, f64>
where
Self: Sized,
K: SingleArg,
F: SingleArg,
{
prepare_command(self, cmd("HINCRBYFLOAT").arg(key).arg(field).arg(increment))
}
/// Returns all field names in the hash stored at key.
///
/// # Return
/// The list of fields in the hash, or an empty list when key does not exist.
///
/// # See Also
/// [](https://redis.io/commands/hkeys/)
#[must_use]
fn hkeys(self, key: K) -> PreparedCommand<'a, Self, A>
where
Self: Sized,
K: SingleArg,
F: PrimitiveResponse + DeserializeOwned,
A: CollectionResponse + DeserializeOwned,
{
prepare_command(self, cmd("HKEYS").arg(key))
}
/// Returns the number of fields contained in the hash stored at key.
///
/// # Return
/// The number of fields in the hash, or 0 when key does not exist.
///
/// # See Also
/// [](https://redis.io/commands/hlen/)
#[must_use]
fn hlen(self, key: K) -> PreparedCommand<'a, Self, usize>
where
Self: Sized,
K: SingleArg,
{
prepare_command(self, cmd("HLEN").arg(key))
}
/// Returns the values associated with the specified fields in the hash stored at key.
///
/// # Return
/// The list of values associated with the given fields, in the same order as they are requested.
///
/// # See Also
/// [](https://redis.io/commands/hmget/)
#[must_use]
fn hmget(self, key: K, fields: C) -> PreparedCommand<'a, Self, A>
where
Self: Sized,
K: SingleArg,
F: SingleArg,
C: SingleArgCollection,
V: PrimitiveResponse + DeserializeOwned,
A: CollectionResponse + DeserializeOwned,
{
prepare_command(self, cmd("HMGET").arg(key).arg(fields))
}
/// return random fields from the hash value stored at key.
///
/// # Return
/// * When called with just the key argument, return a random field from the hash value stored at key.
///
/// # See Also
/// [](https://redis.io/commands/hrandfield/)
#[must_use]
fn hrandfield(self, key: K) -> PreparedCommand<'a, Self, F>
where
Self: Sized,
K: SingleArg,
F: PrimitiveResponse,
{
prepare_command(self, cmd("HRANDFIELD").arg(key))
}
/// return random fields from the hash value stored at key.
///
/// # Return
/// * If the provided count argument is positive, return an array of distinct fields.
/// The array's length is either count or the hash's number of fields (HLEN), whichever is lower.
/// * If called with a negative count, the behavior changes and the command is allowed to return the same field multiple times.
/// In this case, the number of returned fields is the absolute value of the specified count.
///
/// # See Also
/// [](https://redis.io/commands/hrandfield/)
#[must_use]
fn hrandfields(self, key: K, count: isize) -> PreparedCommand<'a, Self, A>
where
Self: Sized,
K: SingleArg,
F: PrimitiveResponse + DeserializeOwned,
A: CollectionResponse + DeserializeOwned,
{
prepare_command(self, cmd("HRANDFIELD").arg(key).arg(count))
}
/// return random fields from the hash value stored at key.
///
/// # Return
/// * If the provided count argument is positive, return an array of distinct fields.
/// The array's length is either count or the hash's number of fields (HLEN), whichever is lower.
/// * If called with a negative count, the behavior changes and the command is allowed to return the same field multiple times.
/// In this case, the number of returned fields is the absolute value of the specified count.
/// The optional WITHVALUES modifier changes the reply so it includes the respective values of the randomly selected hash fields.
///
/// # See Also
/// [](https://redis.io/commands/hrandfield/)
#[must_use]
fn hrandfields_with_values(
self,
key: K,
count: isize,
) -> PreparedCommand<'a, Self, A>
where
Self: Sized,
K: SingleArg,
F: PrimitiveResponse,
V: PrimitiveResponse,
A: KeyValueCollectionResponse,
{
prepare_command(
self,
cmd("HRANDFIELD").arg(key).arg(count).arg("WITHVALUES"),
)
}
/// Iterates fields of Hash types and their associated values.
///
/// # Return
/// array of elements contain two elements, a field and a value,
/// for every returned element of the Hash.
///
/// # See Also
/// [](https://redis.io/commands/hscan/)
#[must_use]
fn hscan(
self,
key: K,
cursor: u64,
options: HScanOptions,
) -> PreparedCommand<'a, Self, HScanResult>
where
Self: Sized,
K: SingleArg,
F: PrimitiveResponse + DeserializeOwned,
V: PrimitiveResponse + DeserializeOwned,
{
prepare_command(self, cmd("HSCAN").arg(key).arg(cursor).arg(options))
}
/// Sets field in the hash stored at key to value.
///
/// # Return
/// The number of fields that were added.
///
/// # See Also
/// [](https://redis.io/commands/hset/)
#[must_use]
fn hset(self, key: K, items: I) -> PreparedCommand<'a, Self, usize>
where
Self: Sized,
K: SingleArg,
F: SingleArg,
V: SingleArg,
I: KeyValueArgsCollection,
{
prepare_command(self, cmd("HSET").arg(key).arg(items))
}
/// Sets field in the hash stored at key to value, only if field does not yet exist.
///
/// # Return
/// * `true` - if field is a new field in the hash and value was set.
/// * `false` - if field already exists in the hash and no operation was performed.
///
/// # See Also
/// [](https://redis.io/commands/hsetnx/)
#[must_use]
fn hsetnx(self, key: K, field: F, value: V) -> PreparedCommand<'a, Self, bool>
where
Self: Sized,
K: SingleArg,
F: SingleArg,
V: SingleArg,
{
prepare_command(self, cmd("HSETNX").arg(key).arg(field).arg(value))
}
/// Returns the string length of the value associated with field in the hash stored at key.
///
/// # Return
/// the string length of the value associated with field,
/// or zero when field is not present in the hash or key does not exist at all.
///
/// # See Also
/// [](https://redis.io/commands/hstrlen/)
#[must_use]
fn hstrlen(self, key: K, field: F) -> PreparedCommand<'a, Self, usize>
where
Self: Sized,
K: SingleArg,
F: SingleArg,
{
prepare_command(self, cmd("HSTRLEN").arg(key).arg(field))
}
/// list of values in the hash, or an empty list when key does not exist.
///
/// # Return
/// The list of values in the hash, or an empty list when key does not exist.
///
/// # See Also
/// [](https://redis.io/commands/hvals/)
#[must_use]
fn hvals(self, key: K) -> PreparedCommand<'a, Self, A>
where
Self: Sized,
K: SingleArg,
V: PrimitiveResponse + DeserializeOwned,
A: CollectionResponse + DeserializeOwned,
{
prepare_command(self, cmd("HVALS").arg(key))
}
}
/// Options for the [`hscan`](HashCommands::hscan) command
#[derive(Default)]
pub struct HScanOptions {
command_args: CommandArgs,
}
impl HScanOptions {
#[must_use]
pub fn match_pattern(mut self, match_pattern: P) -> Self {
Self {
command_args: self.command_args.arg("MATCH").arg(match_pattern).build(),
}
}
#[must_use]
pub fn count(mut self, count: usize) -> Self {
Self {
command_args: self.command_args.arg("COUNT").arg(count).build(),
}
}
}
impl ToArgs for HScanOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Result for the [`hscan`](HashCommands::hscan) command.
#[derive(Debug, Deserialize)]
pub struct HScanResult
where
F: PrimitiveResponse + DeserializeOwned,
V: PrimitiveResponse + DeserializeOwned,
{
pub cursor: u64,
#[serde(deserialize_with = "deserialize_vec_of_pairs")]
pub elements: Vec<(F, V)>,
}