use std::fmt;
use std::sync::Arc;
use bytes::Bytes;
use futures::Stream;
use ruststream::{AckError, HeaderMap, Subscriber};
use super::bus::Bus;
use super::router::{Delivery, DeliveryReceiver, SubscriptionId};
use crate::error::AmqpError;
use crate::message::{AmqpMessage, SettleKind};
pub(crate) struct BusDeliveries {
bus: Arc<Bus>,
id: SubscriptionId,
address: String,
receiver: DeliveryReceiver,
at_most_once: bool,
}
impl BusDeliveries {
pub(crate) fn new(
bus: Arc<Bus>,
id: SubscriptionId,
address: String,
receiver: DeliveryReceiver,
at_most_once: bool,
) -> Self {
Self {
bus,
id,
address,
receiver,
at_most_once,
}
}
}
impl Drop for BusDeliveries {
fn drop(&mut self) {
self.receiver.close();
let mut pending = Vec::new();
while let Ok(delivery) = self.receiver.try_recv() {
if let Some(coordinator) = self.bus.coordinator() {
coordinator.consumed();
}
if !self.at_most_once {
pending.push(delivery);
}
}
self.bus.unsubscribe(self.id, pending);
}
}
impl fmt::Debug for BusDeliveries {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BusDeliveries")
.field("address", &self.address)
.finish_non_exhaustive()
}
}
impl Subscriber for BusDeliveries {
type Message = AmqpMessage;
type Error = AmqpError;
fn stream(&mut self) -> impl Stream<Item = Result<Self::Message, Self::Error>> + Send + '_ {
let Self {
bus,
id,
address,
receiver,
at_most_once,
} = self;
futures::stream::poll_fn(move |cx| {
receiver.poll_recv(cx).map(|next| {
next.map(|delivery| {
let origin = (!*at_most_once).then(|| Origin {
id: *id,
address: address.clone(),
delivery: delivery.clone(),
});
let settlement = Settlement::new(Arc::clone(bus), origin);
Ok(AmqpMessage::in_process(delivery, settlement))
})
})
})
}
}
#[derive(Debug)]
struct Origin {
id: SubscriptionId,
address: String,
delivery: Delivery,
}
pub(crate) struct Settlement {
bus: Arc<Bus>,
origin: Option<Origin>,
}
impl Settlement {
fn new(bus: Arc<Bus>, origin: Option<Origin>) -> Self {
Self { bus, origin }
}
pub(crate) fn settled(bus: Arc<Bus>) -> Self {
Self::new(bus, None)
}
pub(crate) fn settle(
mut self,
kind: SettleKind,
payload: Bytes,
headers: HeaderMap,
delivery_count: Option<u32>,
) -> Result<(), AckError> {
let Some(origin) = self.origin.take() else {
return Err(AckError::Unsupported);
};
if self.bus.is_closed() {
return Err(AckError::Broker(Box::from(
"the subscription's session ended with the connection",
)));
}
if matches!(kind, SettleKind::Modify) {
self.bus
.requeue(
origin.id,
&origin.address,
Delivery {
payload,
headers,
count: Some(delivery_count.unwrap_or(0) + 1),
},
)
.map_err(|_| {
AckError::Broker(Box::from(
"the subscription's session ended with the connection",
))
})?;
}
Ok(())
}
}
impl Drop for Settlement {
fn drop(&mut self) {
if let Some(origin) = self.origin.take() {
self.bus
.release(origin.id, &origin.address, origin.delivery);
}
if let Some(coordinator) = self.bus.coordinator() {
coordinator.consumed();
}
}
}
impl fmt::Debug for Settlement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Settlement")
.field("settled", &self.origin.is_none())
.finish_non_exhaustive()
}
}