use std::fmt::{Debug, Formatter};
use std::future::Future;
use std::num::NonZeroUsize;
use std::sync::Arc;
use futures::Stream;
#[cfg(feature = "asyncapi")]
use ruststream::asyncapi::Bindings;
use ruststream::{
AddressedCopies, BatchSubscriber, NamedCopies, RedeliveryAddress, RedeliveryAddressed,
RetryDeclaration, Seekable, Subscriber, SubscriptionSource,
};
use super::forms::{ListForm, PubSubForm, StreamForm};
use super::window::{Form, Window};
use super::{Pipelined, RoundMessage, WindowMode};
use crate::broker::ConnectedRedisBroker;
use crate::error::RedisError;
use crate::list::{ListReader, RedisList};
use crate::pubsub::{PubSubWire, RedisPubSub, RedisPubSubPattern};
use crate::seek::RedisGroupSeeker;
use crate::stream::RedisStream;
use crate::subscriber::{PREFETCH, RedisSubscriber};
fn prefetch() -> usize {
usize::try_from(PREFETCH).unwrap_or(usize::MAX)
}
pub struct PipelinedSubscriber<Inner, F: Form> {
inner: Inner,
window: Arc<Window<F>>,
}
impl<Inner, F: Form> Debug for PipelinedSubscriber<Inner, F> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PipelinedSubscriber")
.field("window", &self.window)
.finish_non_exhaustive()
}
}
impl<Inner, F: Form> PipelinedSubscriber<Inner, F> {
fn new(inner: Inner, window: Window<F>) -> Self {
Self {
inner,
window: Arc::new(window),
}
}
}
impl<Inner, F: Form> Drop for PipelinedSubscriber<Inner, F> {
fn drop(&mut self) {
let window = Arc::clone(&self.window);
self.window.spawn(async move { window.drain().await });
}
}
impl Subscriber for PipelinedSubscriber<RedisSubscriber, StreamForm> {
type Message = RoundMessage<StreamForm>;
type Error = RedisError;
fn stream(&mut self) -> impl Stream<Item = Result<Self::Message, Self::Error>> + Send + '_ {
self.inner.round_stream(&self.window)
}
}
impl BatchSubscriber for PipelinedSubscriber<RedisSubscriber, StreamForm> {
type Batch = Vec<RoundMessage<StreamForm>>;
fn batches(
&mut self,
size: NonZeroUsize,
) -> impl Stream<Item = Result<Self::Batch, Self::Error>> + Send + '_ {
self.inner.round_batches(&self.window, size)
}
}
impl Seekable for PipelinedSubscriber<RedisSubscriber, StreamForm> {
type Seeker = RedisGroupSeeker;
fn seeker(&self) -> RedisGroupSeeker {
self.inner.seeker()
}
}
pub struct ChannelReader(PubSubWire);
impl Debug for ChannelReader {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChannelReader").finish_non_exhaustive()
}
}
impl Subscriber for PipelinedSubscriber<ChannelReader, PubSubForm> {
type Message = RoundMessage<PubSubForm>;
type Error = RedisError;
fn stream(&mut self) -> impl Stream<Item = Result<Self::Message, Self::Error>> + Send + '_ {
self.inner.0.round_stream(&self.window)
}
}
impl Subscriber for PipelinedSubscriber<ListReader, ListForm> {
type Message = RoundMessage<ListForm>;
type Error = RedisError;
fn stream(&mut self) -> impl Stream<Item = Result<Self::Message, Self::Error>> + Send + '_ {
self.inner.round_stream(&self.window, prefetch())
}
}
impl BatchSubscriber for PipelinedSubscriber<ChannelReader, PubSubForm> {
type Batch = Vec<RoundMessage<PubSubForm>>;
fn batches(
&mut self,
size: NonZeroUsize,
) -> impl Stream<Item = Result<Self::Batch, Self::Error>> + Send + '_ {
self.inner.0.round_batches(&self.window, size)
}
}
impl BatchSubscriber for PipelinedSubscriber<ListReader, ListForm> {
type Batch = Vec<RoundMessage<ListForm>>;
fn batches(
&mut self,
size: NonZeroUsize,
) -> impl Stream<Item = Result<Self::Batch, Self::Error>> + Send + '_ {
self.inner.round_batches(&self.window, size)
}
}
macro_rules! delegates {
($broker:ty, $name:ident) => {
fn name(&self) -> &str {
self.descriptor().$name()
}
fn declare_retry(self, declaration: &RetryDeclaration) -> Self {
Self::wrap(SubscriptionSource::<$broker>::declare_retry(
self.into_descriptor(),
declaration,
))
}
#[cfg(feature = "asyncapi")]
fn channel_bindings(&self) -> Bindings {
SubscriptionSource::<$broker>::channel_bindings(self.descriptor())
}
};
}
macro_rules! addressed {
($broker:ty, $descriptor:ty) => {
impl<Mode: WindowMode> RedeliveryAddressed<$broker> for Pipelined<$descriptor, Mode> {
fn redelivery_address(
&self,
connected: &$broker,
) -> impl Future<Output = Result<RedeliveryAddress, RedisError>> {
self.descriptor().redelivery_address(connected)
}
}
};
}
impl<Mode: WindowMode> SubscriptionSource<ConnectedRedisBroker> for Pipelined<RedisStream, Mode> {
type Subscriber = PipelinedSubscriber<RedisSubscriber, StreamForm>;
type Copies = AddressedCopies;
delegates!(ConnectedRedisBroker, key);
async fn subscribe(
self,
connected: &ConnectedRedisBroker,
) -> Result<Self::Subscriber, RedisError> {
let inner = connected.subscribe(self.into_descriptor()).await?;
let window = Window::new(
inner.round_form(),
inner.round_client(),
Mode::ATOMIC,
inner.key(),
prefetch(),
connected.window_owner()?,
);
Ok(PipelinedSubscriber::new(inner, window))
}
}
impl<Mode: WindowMode> SubscriptionSource<ConnectedRedisBroker> for Pipelined<RedisPubSub, Mode> {
type Subscriber = PipelinedSubscriber<ChannelReader, PubSubForm>;
type Copies = AddressedCopies;
delegates!(ConnectedRedisBroker, channel);
async fn subscribe(
self,
connected: &ConnectedRedisBroker,
) -> Result<Self::Subscriber, RedisError> {
let channel = self.descriptor().channel().to_owned();
let inner = connected.open_pubsub(self.into_descriptor()).await?;
let window = Window::new(
PubSubForm,
inner.round_client(),
Mode::ATOMIC,
channel,
prefetch(),
connected.window_owner()?,
);
Ok(PipelinedSubscriber::new(ChannelReader(inner), window))
}
}
impl<Mode: WindowMode> SubscriptionSource<ConnectedRedisBroker>
for Pipelined<RedisPubSubPattern, Mode>
{
type Subscriber = PipelinedSubscriber<ChannelReader, PubSubForm>;
type Copies = NamedCopies;
fn name(&self) -> &str {
self.descriptor().pattern()
}
async fn subscribe(
self,
connected: &ConnectedRedisBroker,
) -> Result<Self::Subscriber, RedisError> {
let pattern = self.descriptor().pattern().to_owned();
let inner = connected
.open_pubsub_pattern(self.into_descriptor())
.await?;
let window = Window::new(
PubSubForm,
inner.round_client(),
Mode::ATOMIC,
pattern,
prefetch(),
connected.window_owner()?,
);
Ok(PipelinedSubscriber::new(ChannelReader(inner), window))
}
#[cfg(feature = "asyncapi")]
fn channel_bindings(&self) -> Bindings {
SubscriptionSource::<ConnectedRedisBroker>::channel_bindings(self.descriptor())
}
}
impl<Mode: WindowMode> SubscriptionSource<ConnectedRedisBroker> for Pipelined<RedisList, Mode> {
type Subscriber = PipelinedSubscriber<ListReader, ListForm>;
type Copies = AddressedCopies;
delegates!(ConnectedRedisBroker, key);
async fn subscribe(
self,
connected: &ConnectedRedisBroker,
) -> Result<Self::Subscriber, RedisError> {
let wire = connected.open_list(self.into_descriptor()).await?;
let window = Window::new(
wire.round_form(),
wire.round_client(),
Mode::ATOMIC,
wire.key(),
prefetch(),
connected.window_owner()?,
);
Ok(PipelinedSubscriber::new(ListReader::new(wire), window))
}
}
addressed!(ConnectedRedisBroker, RedisStream);
addressed!(ConnectedRedisBroker, RedisList);
addressed!(ConnectedRedisBroker, RedisPubSub);