use lapin::types::ShortString;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("Lapin error")]
Lapin(#[from] lapin::Error),
#[error("Serde JSON error")]
SerdeJson(#[from] serde_json::Error),
#[error("UTF-8 conversion error")]
Utf8(#[from] std::str::Utf8Error),
#[error("Publish acknowledgement error: code {code}, text {text}")]
PublishAck { code: u16, text: ShortString },
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AckAction {
Ack,
Requeue,
Discard,
}
pub trait IntoAckAction {
fn ack_action(&self) -> AckAction;
}
#[derive(Debug)]
pub struct ProcessingError<E> {
pub error: E,
pub action: AckAction,
}
impl<E> ProcessingError<E> {
pub fn new(error: E, action: AckAction) -> Self {
Self { error, action }
}
pub fn retryable(error: E) -> Self {
Self {
error,
action: AckAction::Requeue,
}
}
pub fn permanent(error: E) -> Self {
Self {
error,
action: AckAction::Discard,
}
}
}
impl<E: std::fmt::Display> IntoAckAction for ProcessingError<E> {
fn ack_action(&self) -> AckAction {
self.action
}
}
impl<E: std::fmt::Display> std::fmt::Display for ProcessingError<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.error.fmt(f)
}
}
pub trait ResultExt<T, E> {
fn requeue_on_err(self) -> std::result::Result<T, ProcessingError<E>>;
fn discard_on_err(self) -> std::result::Result<T, ProcessingError<E>>;
fn with_ack_action(self, action: AckAction) -> std::result::Result<T, ProcessingError<E>>;
fn into_processing_error(self) -> std::result::Result<T, ProcessingError<E>>
where
E: IntoAckAction;
}
impl<T, E> ResultExt<T, E> for std::result::Result<T, E> {
fn requeue_on_err(self) -> std::result::Result<T, ProcessingError<E>> {
self.map_err(ProcessingError::retryable)
}
fn discard_on_err(self) -> std::result::Result<T, ProcessingError<E>> {
self.map_err(ProcessingError::permanent)
}
fn with_ack_action(self, action: AckAction) -> std::result::Result<T, ProcessingError<E>> {
self.map_err(|e| ProcessingError::new(e, action))
}
fn into_processing_error(self) -> std::result::Result<T, ProcessingError<E>>
where
E: IntoAckAction,
{
self.map_err(|e| {
let action = e.ack_action();
ProcessingError::new(e, action)
})
}
}