use crate::ValkeyResult;
use crate::cmd::Cmd;
use crate::executor::CommandExecutor;
use crate::value::FromValkeyValue;
use crate::value::ValkeyValue;
use crate::value::to_glide_error;
use crate::write::ToValkeyArgs;
use async_trait::async_trait;
use bytes::Bytes;
#[async_trait]
pub trait PubSubCommands: CommandExecutor {
async fn subscribe<C: ToValkeyArgs + Send + Sync>(&self, channels: &[C]) -> ValkeyResult<()> {
self.pubsub_subscribe_impl("SUBSCRIBE", channels).await
}
async fn unsubscribe<C: ToValkeyArgs + Send + Sync>(&self, channels: &[C]) -> ValkeyResult<()> {
self.pubsub_subscribe_impl("UNSUBSCRIBE", channels).await
}
async fn psubscribe<C: ToValkeyArgs + Send + Sync>(&self, patterns: &[C]) -> ValkeyResult<()> {
self.pubsub_subscribe_impl("PSUBSCRIBE", patterns).await
}
async fn punsubscribe<C: ToValkeyArgs + Send + Sync>(
&self,
patterns: &[C],
) -> ValkeyResult<()> {
self.pubsub_subscribe_impl("PUNSUBSCRIBE", patterns).await
}
async fn ssubscribe<C: ToValkeyArgs + Send + Sync>(&self, channels: &[C]) -> ValkeyResult<()> {
self.pubsub_subscribe_impl("SSUBSCRIBE", channels).await
}
async fn sunsubscribe<C: ToValkeyArgs + Send + Sync>(
&self,
channels: &[C],
) -> ValkeyResult<()> {
self.pubsub_subscribe_impl("SUNSUBSCRIBE", channels).await
}
#[doc(hidden)]
async fn pubsub_subscribe_impl<C: ToValkeyArgs + Send + Sync>(
&self,
keyword: &'static str,
channels: &[C],
) -> ValkeyResult<()> {
let mut cmd = Cmd::new();
cmd.arg(keyword);
for c in channels {
cmd.arg(c);
}
self.execute_command(cmd, None).await?;
Ok(())
}
async fn pubsub_channels(&self, pattern: Option<&[u8]>) -> ValkeyResult<Vec<Bytes>> {
self.pubsub_channels_impl("CHANNELS", pattern).await
}
async fn pubsub_shardchannels(&self, pattern: Option<&[u8]>) -> ValkeyResult<Vec<Bytes>> {
self.pubsub_channels_impl("SHARDCHANNELS", pattern).await
}
async fn pubsub_numpat(&self) -> ValkeyResult<i64> {
let mut cmd = Cmd::new();
cmd.arg("PUBSUB").arg("NUMPAT");
i64::from_owned_valkey_value(self.execute_command(cmd, None).await?)
}
async fn pubsub_numsub<C: ToValkeyArgs + Send + Sync>(
&self,
channels: &[C],
) -> ValkeyResult<Vec<(Bytes, i64)>> {
self.pubsub_numsub_impl("NUMSUB", channels).await
}
async fn pubsub_shardnumsub<C: ToValkeyArgs + Send + Sync>(
&self,
channels: &[C],
) -> ValkeyResult<Vec<(Bytes, i64)>> {
self.pubsub_numsub_impl("SHARDNUMSUB", channels).await
}
#[doc(hidden)]
async fn pubsub_channels_impl(
&self,
sub: &'static str,
pattern: Option<&[u8]>,
) -> ValkeyResult<Vec<Bytes>> {
let mut cmd = Cmd::new();
cmd.arg("PUBSUB").arg(sub);
if let Some(p) = pattern {
cmd.arg(p);
}
match self.execute_command(cmd, None).await? {
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)?]),
}
}
#[doc(hidden)]
async fn pubsub_numsub_impl<C: ToValkeyArgs + Send + Sync>(
&self,
sub: &'static str,
channels: &[C],
) -> ValkeyResult<Vec<(Bytes, i64)>> {
let mut cmd = Cmd::new();
cmd.arg("PUBSUB").arg(sub);
for c in channels {
cmd.arg(c);
}
parse_numsub(self.execute_command(cmd, None).await?)
}
}
fn parse_numsub(v: ValkeyValue) -> ValkeyResult<Vec<(Bytes, i64)>> {
match v {
ValkeyValue::Nil => Ok(Vec::new()),
ValkeyValue::Map(pairs) => pairs
.into_iter()
.map(|(c, n)| {
Ok((
Bytes::from_owned_valkey_value(c)?,
i64::from_owned_valkey_value(n)?,
))
})
.collect(),
ValkeyValue::Array(items) => {
let mut out = Vec::with_capacity(items.len() / 2);
let mut iter = items.into_iter();
while let (Some(c), Some(n)) = (iter.next(), iter.next()) {
out.push((
Bytes::from_owned_valkey_value(c)?,
i64::from_owned_valkey_value(n)?,
));
}
Ok(out)
}
other => Err(to_glide_error(other, "Unexpected PUBSUB NUMSUB reply.")),
}
}
impl<T: CommandExecutor + ?Sized> PubSubCommands for T {}