use std::{fmt::Display, sync::Arc};
use crate::{
EventMessage, EventTopic, SubscriptionInfo, TransportHandle,
transport::{DeliveryOutcome, Settler},
type_states::Pattern,
};
#[derive(Debug)]
pub struct Delivery {
pub(crate) message: Arc<EventMessage>,
pub(crate) subscription: Arc<SubscriptionInfo>,
pub(crate) attempt: u32,
pub(crate) settler: Option<Arc<Settler>>,
}
impl Delivery {
pub fn new(message: impl Into<Arc<EventMessage>>, pattern: EventTopic<Pattern>) -> Self {
Self {
message: message.into(),
subscription: Arc::new(SubscriptionInfo {
pattern,
consumer: "",
}),
attempt: 1,
settler: None,
}
}
pub fn with_attempt(mut self, attempt: u32) -> Self {
self.attempt = attempt;
self
}
pub fn message(&self) -> &Arc<EventMessage> {
&self.message
}
pub fn subscription(&self) -> &SubscriptionInfo {
&self.subscription
}
pub fn attempt(&self) -> u32 {
self.attempt
}
pub fn transport<T: TransportHandle>(&self) -> Option<&T> {
self.message.transport()
}
pub(crate) async fn finish(mut self, outcome: DeliveryOutcome) {
let Some(settler) = self.settler.take() else {
return;
};
settler.record(&self.subscription, outcome);
if let Some(settler) = Arc::into_inner(settler) {
settler.settle().await;
}
}
}
impl Drop for Delivery {
fn drop(&mut self) {
let Some(settler) = self.settler.take() else {
return;
};
settler.record(&self.subscription, DeliveryOutcome::Aborted);
if let Some(settler) = Arc::into_inner(settler) {
match tokio::runtime::Handle::try_current() {
Ok(runtime) => {
runtime.spawn(settler.settle());
}
Err(_) => tracing::error!(
"Unable to settle aborted event on `{}`: no tokio runtime available",
self.message.topic()
),
}
}
}
}
#[derive(Debug)]
pub struct HandlerError {
error: anyhow::Error,
permanent: bool,
}
impl HandlerError {
pub fn transient(error: impl Into<anyhow::Error>) -> Self {
Self {
error: error.into(),
permanent: false,
}
}
pub fn permanent(error: impl Into<anyhow::Error>) -> Self {
Self {
error: error.into(),
permanent: true,
}
}
pub fn is_permanent(&self) -> bool {
self.permanent
}
pub fn error(&self) -> &anyhow::Error {
&self.error
}
pub fn into_error(self) -> anyhow::Error {
self.error
}
}
impl<E> From<E> for HandlerError
where
E: Into<anyhow::Error>,
{
fn from(error: E) -> Self {
Self::transient(error)
}
}
impl Display for HandlerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.error, f)
}
}