use std::sync::{Arc, OnceLock};
use futures::Stream;
use ruststream::{
AckError, Headers, IncomingMessage, Partitioned, Subscriber, testing::Coordinator,
};
use crate::PARTITION_KEY_HEADER;
use crate::error::PulsarError;
use crate::testing::broker::TestState;
use crate::testing::router::{Delivery, DeliveryReceiver, DeliverySender, SubscriptionId};
pub struct PulsarTestSubscriber {
state: Arc<TestState>,
id: SubscriptionId,
rx: DeliveryReceiver,
requeue: DeliverySender,
coordinator: Option<Coordinator>,
}
impl std::fmt::Debug for PulsarTestSubscriber {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PulsarTestSubscriber")
.finish_non_exhaustive()
}
}
impl PulsarTestSubscriber {
pub(crate) fn new(
state: Arc<TestState>,
id: SubscriptionId,
rx: DeliveryReceiver,
requeue: DeliverySender,
coordinator: Option<Coordinator>,
) -> Self {
Self {
state,
id,
rx,
requeue,
coordinator,
}
}
}
impl Drop for PulsarTestSubscriber {
fn drop(&mut self) {
self.state.router.unsubscribe(self.id);
}
}
impl Subscriber for PulsarTestSubscriber {
type Message = PulsarTestMessage;
type Error = PulsarError;
fn stream(&mut self) -> impl Stream<Item = Result<Self::Message, Self::Error>> + Send + '_ {
let requeue = self.requeue.clone();
let coordinator = self.coordinator.clone();
futures::stream::poll_fn(move |cx| {
self.rx.poll_recv(cx).map(|next| {
next.map(|delivery| {
Ok(PulsarTestMessage::new(
delivery,
requeue.clone(),
coordinator.clone(),
))
})
})
})
}
}
pub struct PulsarTestMessage {
delivery: Option<Delivery>,
requeue: DeliverySender,
coordinator: Option<Coordinator>,
}
impl Drop for PulsarTestMessage {
fn drop(&mut self) {
if let Some(coordinator) = &self.coordinator {
coordinator.consumed();
}
}
}
impl std::fmt::Debug for PulsarTestMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PulsarTestMessage").finish_non_exhaustive()
}
}
impl PulsarTestMessage {
pub(crate) fn new(
delivery: Delivery,
requeue: DeliverySender,
coordinator: Option<Coordinator>,
) -> Self {
Self {
delivery: Some(delivery),
requeue,
coordinator,
}
}
}
impl Partitioned for PulsarTestMessage {
fn partition_key(&self) -> Option<&[u8]> {
self.headers().get(PARTITION_KEY_HEADER)
}
}
impl IncomingMessage for PulsarTestMessage {
fn payload(&self) -> &[u8] {
self.delivery
.as_ref()
.map(|d| d.payload.as_ref())
.unwrap_or_default()
}
fn headers(&self) -> &Headers {
static EMPTY: OnceLock<Headers> = OnceLock::new();
self.delivery
.as_ref()
.map_or_else(|| EMPTY.get_or_init(Headers::new), |d| &d.headers)
}
async fn ack(mut self) -> Result<(), AckError> {
self.delivery.take();
Ok(())
}
async fn nack(mut self, requeue: bool) -> Result<(), AckError> {
let delivery = self
.delivery
.take()
.expect("PulsarTestMessage ack/nack invoked twice");
if requeue {
let sent = self.requeue.send(delivery);
if sent.is_ok()
&& let Some(coordinator) = &self.coordinator
{
coordinator.enqueued();
}
}
Ok(())
}
fn partition_key(&self) -> Option<&[u8]> {
Partitioned::partition_key(self)
}
}