use std::{
any::Any,
fmt::Debug,
sync::{Arc, Mutex, PoisonError},
time::Duration,
};
use crate::{EventMessage, SubscriptionInfo};
#[async_trait::async_trait]
pub trait TransportHandle: Any + Debug + Send + Sync {
async fn settle(&self, settlement: &Settlement) -> anyhow::Result<()>;
async fn on_retry(&self, _attempt: u32, _delay: Duration) -> anyhow::Result<()> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeliveryOutcome {
Handled,
DeadLettered,
Failed,
Aborted,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SubscriptionOutcome {
subscription: Arc<SubscriptionInfo>,
outcome: DeliveryOutcome,
}
impl SubscriptionOutcome {
pub fn subscription(&self) -> &SubscriptionInfo {
&self.subscription
}
pub fn outcome(&self) -> DeliveryOutcome {
self.outcome
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Settlement {
outcomes: Vec<SubscriptionOutcome>,
}
impl Settlement {
pub fn outcomes(&self) -> &[SubscriptionOutcome] {
&self.outcomes
}
pub fn is_unrouted(&self) -> bool {
self.outcomes.is_empty()
}
pub fn all_handled(&self) -> bool {
!self.is_unrouted()
&& self
.outcomes
.iter()
.all(|outcome| outcome.outcome == DeliveryOutcome::Handled)
}
pub fn any(&self, outcome: DeliveryOutcome) -> bool {
self.outcomes
.iter()
.any(|candidate| candidate.outcome == outcome)
}
}
#[derive(Debug)]
pub(crate) struct Settler {
message: Arc<EventMessage>,
outcomes: Mutex<Vec<SubscriptionOutcome>>,
}
impl Settler {
pub(crate) fn new(message: &Arc<EventMessage>, deliveries: usize) -> Option<Self> {
message.transport_handle()?;
Some(Self {
message: message.clone(),
outcomes: Mutex::new(Vec::with_capacity(deliveries)),
})
}
pub(crate) fn record(&self, subscription: &Arc<SubscriptionInfo>, outcome: DeliveryOutcome) {
self.outcomes
.lock()
.unwrap_or_else(PoisonError::into_inner)
.push(SubscriptionOutcome {
subscription: subscription.clone(),
outcome,
});
}
pub(crate) async fn settle(self) {
let settlement = Settlement {
outcomes: self
.outcomes
.into_inner()
.unwrap_or_else(PoisonError::into_inner),
};
if let Some(handle) = self.message.transport_handle()
&& let Err(e) = handle.settle(&settlement).await
{
tracing::error!(
"Failed to settle event on `{}`: {e:#}",
self.message.topic()
);
}
}
}