use crate::{
client::{PreparedCommand, prepare_command},
resp::{Response, cmd, serialize_flag},
};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use std::collections::HashMap;
pub trait StreamCommands<'a>: Sized {
fn xack(
self,
key: impl Serialize,
group: impl Serialize,
ids: impl Serialize,
) -> PreparedCommand<'a, Self, usize> {
prepare_command(self, cmd("XACK").key(key).arg(group).arg(ids))
}
fn xnack(
self,
key: impl Serialize,
group: impl Serialize,
mode: XNackMode,
ids: impl Serialize,
options: XNackOptions,
) -> PreparedCommand<'a, Self, usize> {
prepare_command(
self,
cmd("XNACK")
.key(key)
.arg(group)
.arg(mode)
.arg("IDS")
.arg_with_count(ids)
.arg(options),
)
}
fn xadd<R: Response>(
self,
key: impl Serialize,
stream_id: impl Serialize,
items: impl Serialize,
options: XAddOptions,
) -> PreparedCommand<'a, Self, R> {
prepare_command(
self,
cmd("XADD").key(key).arg(options).arg(stream_id).arg(items),
)
}
fn xsetid(
self,
key: impl Serialize,
last_id: impl Serialize,
options: XSetIdOptions,
) -> PreparedCommand<'a, Self, ()> {
prepare_command(self, cmd("XSETID").key(key).arg(last_id).arg(options))
}
fn xcfgset(
self,
key: impl Serialize,
options: XCfgSetOptions,
) -> PreparedCommand<'a, Self, ()> {
prepare_command(self, cmd("XCFGSET").key(key).arg(options))
}
fn xautoclaim<R: Response + DeserializeOwned>(
self,
key: impl Serialize,
group: impl Serialize,
consumer: impl Serialize,
min_idle_time: u64,
start: impl Serialize,
options: XAutoClaimOptions,
) -> PreparedCommand<'a, Self, XAutoClaimResult<R>> {
prepare_command(
self,
cmd("XAUTOCLAIM")
.key(key)
.arg(group)
.arg(consumer)
.arg(min_idle_time)
.arg(start)
.arg(options),
)
}
fn xclaim<R: Response>(
self,
key: impl Serialize,
group: impl Serialize,
consumer: impl Serialize,
min_idle_time: u64,
ids: impl Serialize,
options: XClaimOptions,
) -> PreparedCommand<'a, Self, R> {
prepare_command(
self,
cmd("XCLAIM")
.key(key)
.arg(group)
.arg(consumer)
.arg(min_idle_time)
.arg(ids)
.arg(options),
)
}
fn xdel(self, key: impl Serialize, ids: impl Serialize) -> PreparedCommand<'a, Self, usize> {
prepare_command(self, cmd("XDEL").key(key).arg(ids))
}
fn xdelex(
self,
key: impl Serialize,
policy: impl Into<Option<StreamEntryDeletionPolicy>>,
ids: impl Serialize,
) -> PreparedCommand<'a, Self, Vec<i64>> {
prepare_command(
self,
cmd("XDELEX")
.key(key)
.arg(policy.into())
.arg("IDS")
.arg_with_count(ids),
)
}
fn xackdel(
self,
key: impl Serialize,
group: impl Serialize,
policy: impl Into<Option<StreamEntryDeletionPolicy>>,
ids: impl Serialize,
) -> PreparedCommand<'a, Self, Vec<i64>> {
prepare_command(
self,
cmd("XACKDEL")
.key(key)
.arg(group)
.arg(policy.into())
.arg("IDS")
.arg_with_count(ids),
)
}
fn xgroup_create(
self,
key: impl Serialize,
groupname: impl Serialize,
id: impl Serialize,
options: XGroupCreateOptions,
) -> PreparedCommand<'a, Self, bool> {
prepare_command(
self,
cmd("XGROUP")
.arg("CREATE")
.key(key)
.arg(groupname)
.arg(id)
.arg(options),
)
}
fn xgroup_createconsumer(
self,
key: impl Serialize,
groupname: impl Serialize,
consumername: impl Serialize,
) -> PreparedCommand<'a, Self, bool> {
prepare_command(
self,
cmd("XGROUP")
.arg("CREATECONSUMER")
.key(key)
.arg(groupname)
.arg(consumername),
)
}
fn xgroup_delconsumer(
self,
key: impl Serialize,
groupname: impl Serialize,
consumername: impl Serialize,
) -> PreparedCommand<'a, Self, usize> {
prepare_command(
self,
cmd("XGROUP")
.arg("DELCONSUMER")
.key(key)
.arg(groupname)
.arg(consumername),
)
}
fn xgroup_destroy(
self,
key: impl Serialize,
groupname: impl Serialize,
) -> PreparedCommand<'a, Self, bool> {
prepare_command(self, cmd("XGROUP").arg("DESTROY").key(key).arg(groupname))
}
#[must_use]
fn xgroup_help(self) -> PreparedCommand<'a, Self, Vec<String>>
where
Self: Sized,
{
prepare_command(self, cmd("XGROUP").arg("HELP"))
}
fn xgroup_setid(
self,
key: impl Serialize,
groupname: impl Serialize,
id: impl Serialize,
entries_read: Option<usize>,
) -> PreparedCommand<'a, Self, ()> {
prepare_command(
self,
cmd("XGROUP")
.arg("SETID")
.key(key)
.arg(groupname)
.arg(id)
.arg(entries_read.map(|e| ("ENTRIESREAD", e))),
)
}
fn xinfo_consumers(
self,
key: impl Serialize,
groupname: impl Serialize,
) -> PreparedCommand<'a, Self, Vec<XConsumerInfo>> {
prepare_command(
self,
cmd("XINFO")
.arg("CONSUMERS")
.key(key)
.arg(groupname)
.readonly(),
)
}
fn xinfo_groups(self, key: impl Serialize) -> PreparedCommand<'a, Self, Vec<XGroupInfo>> {
prepare_command(self, cmd("XINFO").arg("GROUPS").key(key).readonly())
}
#[must_use]
fn xinfo_help(self) -> PreparedCommand<'a, Self, Vec<String>>
where
Self: Sized,
{
prepare_command(self, cmd("XINFO").arg("HELP"))
}
fn xinfo_stream(
self,
key: impl Serialize,
options: XInfoStreamOptions,
) -> PreparedCommand<'a, Self, XStreamInfo> {
prepare_command(
self,
cmd("XINFO").arg("STREAM").key(key).arg(options).readonly(),
)
}
fn xlen(self, key: impl Serialize) -> PreparedCommand<'a, Self, usize> {
prepare_command(self, cmd("XLEN").key(key).readonly())
}
fn xpending(
self,
key: impl Serialize,
group: impl Serialize,
) -> PreparedCommand<'a, Self, XPendingResult> {
prepare_command(self, cmd("XPENDING").key(key).arg(group).readonly())
}
fn xpending_with_options<R: Response>(
self,
key: impl Serialize,
group: impl Serialize,
options: XPendingOptions,
) -> PreparedCommand<'a, Self, R> {
prepare_command(
self,
cmd("XPENDING").key(key).arg(group).arg(options).readonly(),
)
}
fn xrange<R: Response>(
self,
key: impl Serialize,
start: impl Serialize,
end: impl Serialize,
count: Option<usize>,
) -> PreparedCommand<'a, Self, R> {
prepare_command(
self,
cmd("XRANGE")
.key(key)
.arg(start)
.arg(end)
.arg(count.map(|c| ("COUNT", c)))
.readonly(),
)
}
fn xread<R: Response>(
self,
options: XReadOptions,
keys: impl Serialize,
ids: impl Serialize,
) -> PreparedCommand<'a, Self, R> {
prepare_command(
self,
cmd("XREAD")
.arg(options)
.arg("STREAMS")
.key(keys)
.arg(ids)
.readonly(),
)
}
fn xreadgroup<R: Response>(
self,
group: impl Serialize,
consumer: impl Serialize,
options: XReadGroupOptions,
keys: impl Serialize,
ids: impl Serialize,
) -> PreparedCommand<'a, Self, R> {
prepare_command(
self,
cmd("XREADGROUP")
.arg("GROUP")
.arg(group)
.arg(consumer)
.arg(options)
.arg("STREAMS")
.key(keys)
.arg(ids),
)
}
fn xrevrange<R: Response>(
self,
key: impl Serialize,
end: impl Serialize,
start: impl Serialize,
count: Option<usize>,
) -> PreparedCommand<'a, Self, R> {
prepare_command(
self,
cmd("XREVRANGE")
.key(key)
.arg(end)
.arg(start)
.arg(count.map(|c| ("COUNT", c)))
.readonly(),
)
}
fn xtrim(self, key: impl Serialize, options: XTrimOptions) -> PreparedCommand<'a, Self, usize> {
prepare_command(self, cmd("XTRIM").key(key).arg(options))
}
}
#[derive(Serialize)]
#[serde(rename_all = "UPPERCASE")]
#[non_exhaustive]
pub enum ConsumerGroupOptions {
KeepRef,
DelRef,
Acked,
}
#[derive(Default, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct XAddOptions<'a> {
#[serde(
skip_serializing_if = "std::ops::Not::not",
serialize_with = "serialize_flag"
)]
nomkstream: bool,
#[serde(rename = "", skip_serializing_if = "Option::is_none")]
consumer_group_options: Option<ConsumerGroupOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
idmpauto: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
idmp: Option<(&'a str, &'a str)>,
#[serde(rename = "", skip_serializing_if = "Option::is_none")]
trim_options: Option<XTrimOptions<'a>>,
}
impl<'a> XAddOptions<'a> {
#[must_use]
pub fn no_mk_stream(mut self) -> Self {
self.nomkstream = true;
self
}
#[must_use]
pub fn consumer_group_options(mut self, consumer_group_options: ConsumerGroupOptions) -> Self {
self.consumer_group_options = Some(consumer_group_options);
self
}
#[must_use]
pub fn idmp(mut self, pid: &'a str, iid: &'a str) -> Self {
self.idmp = Some((pid, iid));
self.idmpauto = None;
self
}
#[must_use]
pub fn idmp_auto(mut self, pid: &'a str) -> Self {
self.idmpauto = Some(pid);
self.idmp = None;
self
}
#[must_use]
pub fn trim_options(mut self, trim_options: XTrimOptions<'a>) -> Self {
self.trim_options = Some(trim_options);
self
}
}
#[derive(Default, Serialize)]
pub struct XSetIdOptions {
#[serde(rename = "ENTRIESADDED", skip_serializing_if = "Option::is_none")]
entries_added: Option<u64>,
#[serde(rename = "MAXDELETEDID", skip_serializing_if = "Option::is_none")]
max_deleted_id: Option<String>,
}
impl XSetIdOptions {
#[must_use]
pub fn entries_added(mut self, entries_added: u64) -> Self {
self.entries_added = Some(entries_added);
self
}
#[must_use]
pub fn max_deleted_id(mut self, id: impl Into<String>) -> Self {
self.max_deleted_id = Some(id.into());
self
}
}
#[derive(Serialize)]
#[serde(rename_all = "UPPERCASE")]
#[non_exhaustive]
pub enum XNackMode {
Silent,
Fail,
Fatal,
}
#[derive(Default, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct XNackOptions {
#[serde(skip_serializing_if = "Option::is_none")]
retrycount: Option<i64>,
#[serde(
skip_serializing_if = "std::ops::Not::not",
serialize_with = "serialize_flag"
)]
force: bool,
}
impl XNackOptions {
#[must_use]
pub fn retry_count(mut self, count: i64) -> Self {
self.retrycount = Some(count);
self
}
#[must_use]
pub fn force(mut self) -> Self {
self.force = true;
self
}
}
#[derive(Default, Serialize)]
pub struct XCfgSetOptions {
#[serde(rename = "IDMP-DURATION", skip_serializing_if = "Option::is_none")]
idmp_duration: Option<u64>,
#[serde(rename = "IDMP-MAXSIZE", skip_serializing_if = "Option::is_none")]
idmp_maxsize: Option<u64>,
}
impl XCfgSetOptions {
#[must_use]
pub fn idmp_duration(mut self, seconds: u64) -> Self {
self.idmp_duration = Some(seconds);
self
}
#[must_use]
pub fn idmp_maxsize(mut self, entries: u64) -> Self {
self.idmp_maxsize = Some(entries);
self
}
}
#[derive(Serialize)]
#[serde(rename_all = "UPPERCASE")]
#[non_exhaustive]
pub enum XTrimOperator {
Equal,
Approximately,
}
#[derive(Default, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct XTrimOptions<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
maxlen: Option<(Option<XTrimOperator>, i64)>,
#[serde(skip_serializing_if = "Option::is_none")]
minid: Option<(Option<XTrimOperator>, &'a str)>,
#[serde(skip_serializing_if = "Option::is_none")]
limit: Option<u32>,
#[serde(rename = "", skip_serializing_if = "Option::is_none")]
entries_deletion: Option<StreamEntryDeletionPolicy>,
}
impl<'a> XTrimOptions<'a> {
#[must_use]
pub fn max_len(operator: impl Into<Option<XTrimOperator>>, threshold: i64) -> Self {
Self {
maxlen: Some((operator.into(), threshold)),
..Default::default()
}
}
#[must_use]
pub fn min_id(operator: impl Into<Option<XTrimOperator>>, threshold_id: &'a str) -> Self {
Self {
minid: Some((operator.into(), threshold_id)),
..Default::default()
}
}
#[must_use]
pub fn limit(mut self, count: u32) -> Self {
self.limit = Some(count);
self
}
#[must_use]
pub fn entries_deletion(mut self, policy: StreamEntryDeletionPolicy) -> Self {
self.entries_deletion = Some(policy);
self
}
}
#[derive(Serialize)]
#[serde(rename_all = "UPPERCASE")]
#[non_exhaustive]
pub enum StreamEntryDeletionPolicy {
KeepRef,
DelRef,
Acked,
}
#[derive(Default, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct XAutoClaimOptions {
#[serde(skip_serializing_if = "Option::is_none")]
count: Option<u32>,
#[serde(
skip_serializing_if = "std::ops::Not::not",
serialize_with = "serialize_flag"
)]
justid: bool,
}
impl XAutoClaimOptions {
#[must_use]
pub fn count(mut self, count: u32) -> Self {
self.count = Some(count);
self
}
#[must_use]
pub fn just_id(mut self) -> Self {
self.justid = true;
self
}
}
#[derive(Deserialize)]
#[serde(bound = "V: DeserializeOwned")]
#[non_exhaustive]
pub struct StreamEntry<V>
where
V: Response,
{
pub stream_id: String,
pub items: HashMap<String, V>,
}
#[derive(Deserialize)]
#[serde(bound = "V: DeserializeOwned")]
#[non_exhaustive]
pub struct XAutoClaimResult<V>
where
V: Response,
{
pub start_stream_id: String,
pub entries: Vec<StreamEntry<V>>,
pub deleted_ids: Vec<String>,
}
#[derive(Default, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct XClaimOptions {
#[serde(skip_serializing_if = "Option::is_none")]
idle: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
time: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
retrycount: Option<u32>,
#[serde(
skip_serializing_if = "std::ops::Not::not",
serialize_with = "serialize_flag"
)]
force: bool,
#[serde(
skip_serializing_if = "std::ops::Not::not",
serialize_with = "serialize_flag"
)]
justid: bool,
#[serde(rename = "LASTID", skip_serializing_if = "Option::is_none")]
lastid: Option<String>,
}
impl XClaimOptions {
#[must_use]
pub fn idle_time(mut self, ms: u64) -> Self {
self.idle = Some(ms);
self
}
#[must_use]
pub fn time(mut self, unix_time_milliseconds: u64) -> Self {
self.time = Some(unix_time_milliseconds);
self
}
#[must_use]
pub fn retry_count(mut self, count: u32) -> Self {
self.retrycount = Some(count);
self
}
#[must_use]
pub fn force(mut self) -> Self {
self.force = true;
self
}
#[must_use]
pub fn just_id(mut self) -> Self {
self.justid = true;
self
}
#[must_use]
pub fn last_id(mut self, id: impl Into<String>) -> Self {
self.lastid = Some(id.into());
self
}
}
#[derive(Default, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct XGroupCreateOptions {
#[serde(
skip_serializing_if = "std::ops::Not::not",
serialize_with = "serialize_flag"
)]
mkstream: bool,
#[serde(skip_serializing_if = "Option::is_none")]
entriesread: Option<u32>,
}
impl XGroupCreateOptions {
#[must_use]
pub fn mk_stream(mut self) -> Self {
self.mkstream = true;
self
}
#[must_use]
pub fn entries_read(mut self, entries_read: u32) -> Self {
self.entriesread = Some(entries_read);
self
}
}
#[derive(Deserialize)]
#[non_exhaustive]
pub struct XConsumerInfo {
pub name: String,
pub pending: usize,
#[serde(rename = "idle")]
pub idle_millis: u64,
}
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct XGroupInfo {
pub name: String,
pub consumers: usize,
pub pending: usize,
pub last_delivered_id: String,
pub entries_read: Option<usize>,
pub lag: Option<usize>,
}
#[derive(Default, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct XInfoStreamOptions {
#[serde(
skip_serializing_if = "std::ops::Not::not",
serialize_with = "serialize_flag"
)]
full: bool,
#[serde(skip_serializing_if = "Option::is_none")]
count: Option<u32>,
}
impl XInfoStreamOptions {
#[must_use]
pub fn full() -> Self {
Self {
full: true,
..Default::default()
}
}
#[must_use]
pub fn count(mut self, count: u32) -> Self {
self.count = Some(count);
self
}
}
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct XStreamInfo {
pub length: usize,
pub radix_tree_keys: usize,
pub radix_tree_nodes: usize,
pub groups: usize,
pub last_generated_id: String,
pub max_deleted_entry_id: String,
pub entries_added: usize,
pub first_entry: StreamEntry<String>,
pub last_entry: StreamEntry<String>,
pub recorded_first_entry_id: String,
#[serde(default)]
pub idmp_duration: Option<u64>,
#[serde(default)]
pub idmp_maxsize: Option<u64>,
#[serde(default)]
pub pids_tracked: Option<usize>,
#[serde(default)]
pub iids_tracked: Option<usize>,
#[serde(default)]
pub iids_added: Option<usize>,
#[serde(default)]
pub iids_duplicates: Option<usize>,
}
#[derive(Default, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct XReadOptions {
#[serde(skip_serializing_if = "Option::is_none")]
count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
block: Option<u64>,
}
impl XReadOptions {
#[must_use]
pub fn count(mut self, count: u32) -> Self {
self.count = Some(count);
self
}
#[must_use]
pub fn block(mut self, milliseconds: u64) -> Self {
self.block = Some(milliseconds);
self
}
}
#[derive(Default, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct XReadGroupOptions {
#[serde(skip_serializing_if = "Option::is_none")]
count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
block: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
claim: Option<u64>,
#[serde(
skip_serializing_if = "std::ops::Not::not",
serialize_with = "serialize_flag"
)]
noack: bool,
}
impl XReadGroupOptions {
#[must_use]
pub fn count(mut self, count: u32) -> Self {
self.count = Some(count);
self
}
#[must_use]
pub fn block(mut self, milliseconds: u64) -> Self {
self.block = Some(milliseconds);
self
}
#[must_use]
pub fn claim(mut self, min_idle_time: u64) -> Self {
self.claim = Some(min_idle_time);
self
}
#[must_use]
pub fn no_ack(mut self) -> Self {
self.noack = true;
self
}
}
#[derive(Default, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct XPendingOptions<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
idle: Option<u64>,
#[serde(rename = "", skip_serializing_if = "Option::is_none")]
start: Option<&'a str>,
#[serde(rename = "", skip_serializing_if = "Option::is_none")]
end: Option<&'a str>,
#[serde(rename = "", skip_serializing_if = "Option::is_none")]
count: Option<u32>,
#[serde(rename = "", skip_serializing_if = "Option::is_none")]
consumer: Option<&'a str>,
}
impl<'a> XPendingOptions<'a> {
#[must_use]
pub fn idle(mut self, min_idle_time: u64) -> Self {
self.idle = Some(min_idle_time);
self
}
#[must_use]
pub fn start(mut self, start: &'a str) -> Self {
self.start = Some(start);
self
}
#[must_use]
pub fn end(mut self, end: &'a str) -> Self {
self.end = Some(end);
self
}
#[must_use]
pub fn count(mut self, count: u32) -> Self {
self.count = Some(count);
self
}
#[must_use]
pub fn consumer(mut self, consumer: &'a str) -> Self {
self.consumer = Some(consumer);
self
}
}
#[derive(Deserialize)]
#[non_exhaustive]
pub struct XPendingResult {
pub num_pending_messages: usize,
pub smallest_id: String,
pub greatest_id: String,
pub consumers: Vec<XPendingConsumer>,
}
#[derive(Deserialize)]
#[non_exhaustive]
pub struct XPendingConsumer {
pub consumer: String,
pub num_messages: usize,
}
#[derive(Deserialize)]
#[non_exhaustive]
pub struct XPendingMessageResult {
pub message_id: String,
pub consumer: String,
pub elapsed_millis: i64,
pub times_delivered: usize,
}