use std::sync::Arc;
use std::task::Poll;
use futures::Stream;
use ruststream::testing::Coordinator;
use super::Bus;
use super::log::{Delivery, DeliveryReceiver, DeliverySender, SubscriptionId};
use super::seek::{IN_PROCESS_SHARD, LogSeeker, SeekControl, sequence_of};
use crate::error::KinesisError;
use crate::message::{KPL_MAGIC, KinesisMessage, Settle};
use crate::subscriber::KinesisSeeker;
pub(crate) struct LogDeliveries {
bus: Arc<Bus>,
id: SubscriptionId,
stream: Arc<str>,
shard: Arc<str>,
rx: DeliveryReceiver,
replay: DeliverySender,
log: LogSeeker,
seeker: KinesisSeeker,
coordinator: Option<Coordinator>,
}
impl std::fmt::Debug for LogDeliveries {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LogDeliveries")
.field("stream", &self.stream)
.finish_non_exhaustive()
}
}
impl LogDeliveries {
pub(crate) fn open(bus: &Arc<Bus>, stream: &str) -> Self {
let stream: Arc<str> = Arc::from(stream);
let registration = bus.log().subscribe(Arc::clone(&stream));
let log = LogSeeker::new(
Arc::clone(bus),
Arc::clone(&stream),
Arc::clone(®istration.control),
);
Self {
bus: Arc::clone(bus),
id: registration.id,
stream,
shard: Arc::from(IN_PROCESS_SHARD),
rx: registration.deliveries,
replay: registration.replay,
seeker: KinesisSeeker::in_process(log.clone()),
log,
coordinator: bus.coordinator().cloned(),
}
}
pub(crate) fn seeker(&self) -> KinesisSeeker {
self.seeker.clone()
}
fn control(&self) -> &SeekControl {
self.log.control()
}
fn apply_pending(&mut self) {
let Some(plan) = self.control().take_pending() else {
return;
};
self.bus.log().reposition(
&self.stream,
plan,
&mut self.rx,
&self.replay,
self.coordinator.as_ref(),
);
}
fn deliver(&self, delivery: &Delivery) -> Result<KinesisMessage, KinesisError> {
let data = delivery.record.data.as_ref();
if data.len() > 4 && data[0..4] == KPL_MAGIC {
if let Some(coordinator) = &self.coordinator {
coordinator.consumed();
}
return Err(KinesisError::AggregatedRecord {
shard: self.shard.to_string(),
});
}
let release = Release {
shard: Arc::clone(&self.shard),
coordinator: self.coordinator.clone(),
};
Ok(KinesisMessage::new(
data,
&delivery.record.partition_key,
&sequence_of(delivery.index),
self.seeker.clone(),
Settle::InProcess(release),
))
}
pub(crate) fn stream(
&mut self,
) -> impl Stream<Item = Result<KinesisMessage, KinesisError>> + Send + '_ {
futures::stream::poll_fn(move |cx| {
self.control().waker.register(cx.waker());
self.apply_pending();
match self.rx.poll_recv(cx) {
Poll::Ready(Some(delivery)) => Poll::Ready(Some(self.deliver(&delivery))),
Poll::Ready(None) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
}
})
}
}
impl Drop for LogDeliveries {
fn drop(&mut self) {
self.bus.log().unsubscribe(self.id);
}
}
pub(crate) struct Release {
shard: Arc<str>,
coordinator: Option<Coordinator>,
}
impl Release {
pub(crate) fn shard(&self) -> &Arc<str> {
&self.shard
}
}
impl Drop for Release {
fn drop(&mut self) {
if let Some(coordinator) = &self.coordinator {
coordinator.consumed();
}
}
}