use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use ruststream::{AckError, HeaderMap, IncomingMessage, Partitioned};
use super::window::{Form, Round, Window};
pub struct RoundMessage<F: Form> {
inner: Option<F::Message>,
window: Arc<Window<F>>,
round: Round,
entered: AtomicBool,
}
impl<F: Form> Debug for RoundMessage<F>
where
F::Message: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RoundMessage")
.field("inner", &self.inner)
.finish_non_exhaustive()
}
}
impl<F: Form> RoundMessage<F> {
pub(crate) const fn new(inner: F::Message, window: Arc<Window<F>>, round: Round) -> Self {
Self {
inner: Some(inner),
window,
round,
entered: AtomicBool::new(false),
}
}
pub(crate) fn inner(&self) -> &F::Message {
self.inner
.as_ref()
.expect("a pipelined delivery is read only before it settles")
}
pub(crate) fn round(&self) -> &Round {
&self.round
}
async fn settle(
mut self,
commit: bool,
settle: impl FnOnce(&F, F::Message, &mut Vec<F::Op>) -> Result<(), AckError>,
) -> Result<(), AckError> {
#[cfg_attr(not(feature = "testing"), allow(unused_mut))]
let mut inner = self
.inner
.take()
.expect("a pipelined delivery settles once");
let window = Arc::clone(&self.window);
#[cfg(feature = "testing")]
window.hold(F::take_flight(&mut inner));
window
.settle(&self.round, commit, |form, ops| settle(form, inner, ops))
.await
}
}
impl<F: Form> Drop for RoundMessage<F> {
fn drop(&mut self) {
if self.inner.is_some() {
self.window.abandon(&self.round);
}
}
}
impl<F: Form> Partitioned for RoundMessage<F>
where
F::Message: Partitioned,
{
fn partition_key(&self) -> Option<&[u8]> {
Partitioned::partition_key(self.inner())
}
}
impl<F: Form> RoundMessage<F> {
fn enter(&self) {
if !self.entered.load(Ordering::Relaxed) && !self.entered.swap(true, Ordering::Relaxed) {
self.round.enter();
}
}
}
impl<F: Form> IncomingMessage for RoundMessage<F> {
fn payload(&self) -> &[u8] {
self.enter();
self.inner().payload()
}
fn headers(&self) -> &HeaderMap {
self.enter();
self.inner().headers()
}
fn partition_key(&self) -> Option<&[u8]> {
self.inner().partition_key()
}
fn redelivery_count(&self) -> Option<u64> {
self.inner().redelivery_count()
}
fn supports_nack_after(&self) -> bool {
self.inner().supports_nack_after()
}
async fn ack(self) -> Result<(), AckError> {
self.settle(true, F::ack).await
}
async fn nack(self, requeue: bool) -> Result<(), AckError> {
self.settle(false, move |form, msg, ops| form.nack(msg, requeue, ops))
.await
}
async fn nack_after(self, delay: Duration) -> Result<(), AckError> {
self.settle(false, move |form, msg, ops| {
form.nack_after(msg, delay, ops)
})
.await
}
}