use crate::ValkeyResult;
use crate::cmd::Cmd;
use crate::executor::CommandExecutor;
use crate::value::FromValkeyValue;
use crate::value::ValkeyValue;
use crate::write::ToValkeyArgs;
use async_trait::async_trait;
use bytes::Bytes;
#[async_trait]
pub trait HashCommands: CommandExecutor {
async fn hstrlen<K: ToValkeyArgs + Send, F: ToValkeyArgs + Send>(
&self,
key: K,
field: F,
) -> ValkeyResult<i64> {
let mut cmd = Cmd::new();
cmd.arg("HSTRLEN").arg(key).arg(field);
i64::from_owned_valkey_value(self.execute_command(cmd, None).await?)
}
async fn hrandfield<K: ToValkeyArgs + Send>(&self, key: K) -> ValkeyResult<Option<Bytes>> {
let mut cmd = Cmd::new();
cmd.arg("HRANDFIELD").arg(key);
Option::<Bytes>::from_owned_valkey_value(self.execute_command(cmd, None).await?)
}
async fn hrandfield_count<K: ToValkeyArgs + Send>(
&self,
key: K,
count: i64,
) -> ValkeyResult<Vec<Bytes>> {
let mut cmd = Cmd::new();
cmd.arg("HRANDFIELD").arg(key).arg(count);
collect_bytes(self.execute_command(cmd, None).await?)
}
async fn hrandfield_withvalues<K: ToValkeyArgs + Send>(
&self,
key: K,
count: i64,
) -> ValkeyResult<Vec<(Bytes, Bytes)>> {
let mut cmd = Cmd::new();
cmd.arg("HRANDFIELD").arg(key).arg(count).arg("WITHVALUES");
collect_pairs(self.execute_command(cmd, None).await?)
}
async fn hscan_novalues<K: ToValkeyArgs + Send>(
&self,
key: K,
cursor: &str,
pattern: Option<&[u8]>,
count: Option<i64>,
) -> ValkeyResult<(String, Vec<Bytes>)> {
let mut cmd = Cmd::new();
cmd.arg("HSCAN").arg(key).arg(cursor);
if let Some(p) = pattern {
cmd.arg("MATCH").arg(p);
}
if let Some(c) = count {
cmd.arg("COUNT").arg(c);
}
cmd.arg("NOVALUES");
crate::commands::generic::parse_scan_reply(self.execute_command(cmd, None).await?)
}
}
fn collect_pairs(v: ValkeyValue) -> ValkeyResult<Vec<(Bytes, Bytes)>> {
match v {
ValkeyValue::Nil => Ok(Vec::new()),
ValkeyValue::Map(pairs) => pairs
.into_iter()
.map(|(a, b)| {
Ok((
Bytes::from_owned_valkey_value(a)?,
Bytes::from_owned_valkey_value(b)?,
))
})
.collect(),
ValkeyValue::Array(items) => {
if items
.iter()
.all(|it| matches!(it, ValkeyValue::Array(inner) if inner.len() == 2))
{
let mut out = Vec::with_capacity(items.len());
for it in items {
if let ValkeyValue::Array(mut pair) = it {
let b = Bytes::from_owned_valkey_value(pair.pop().unwrap())?;
let a = Bytes::from_owned_valkey_value(pair.pop().unwrap())?;
out.push((a, b));
}
}
Ok(out)
} else {
let mut out = Vec::with_capacity(items.len() / 2);
let mut iter = items.into_iter();
while let (Some(a), Some(b)) = (iter.next(), iter.next()) {
out.push((
Bytes::from_owned_valkey_value(a)?,
Bytes::from_owned_valkey_value(b)?,
));
}
Ok(out)
}
}
other => Ok(vec![(Bytes::from_owned_valkey_value(other)?, Bytes::new())]),
}
}
fn collect_bytes(v: ValkeyValue) -> ValkeyResult<Vec<Bytes>> {
match v {
ValkeyValue::Array(items) => items
.into_iter()
.map(Bytes::from_owned_valkey_value)
.collect(),
ValkeyValue::Nil => Ok(Vec::new()),
other => Ok(vec![Bytes::from_owned_valkey_value(other)?]),
}
}
impl<T: CommandExecutor + ?Sized> HashCommands for T {}