use std::{future::Future, sync::Arc, time::Duration};
use super::context::Context;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum HandlerResult {
Ack,
Nack {
requeue: bool,
},
NackAfter {
delay: Duration,
},
}
impl HandlerResult {
#[must_use]
pub const fn retry() -> Self {
Self::Nack { requeue: true }
}
#[must_use]
pub const fn retry_after(delay: Duration) -> Self {
Self::NackAfter { delay }
}
#[must_use]
pub const fn drop() -> Self {
Self::Nack { requeue: false }
}
}
pub trait IntoHandlerResult {
fn into_handler_result(self) -> HandlerResult;
}
impl IntoHandlerResult for HandlerResult {
fn into_handler_result(self) -> HandlerResult {
self
}
}
impl IntoHandlerResult for () {
fn into_handler_result(self) -> HandlerResult {
HandlerResult::Ack
}
}
impl<E> IntoHandlerResult for Result<(), E> {
fn into_handler_result(self) -> HandlerResult {
match self {
Ok(()) => HandlerResult::Ack,
Err(_) => HandlerResult::drop(),
}
}
}
impl<E> IntoHandlerResult for Result<HandlerResult, E> {
fn into_handler_result(self) -> HandlerResult {
self.unwrap_or_else(|_| HandlerResult::drop())
}
}
pub trait Handler<M>: Send + Sync {
fn handle(&self, msg: &M, ctx: &mut Context) -> impl Future<Output = HandlerResult> + Send;
}
impl<M, F, Fut> Handler<M> for F
where
F: Fn(&M, &mut Context) -> Fut + Send + Sync,
Fut: Future<Output = HandlerResult> + Send,
{
fn handle(&self, msg: &M, ctx: &mut Context) -> impl Future<Output = HandlerResult> + Send {
(self)(msg, ctx)
}
}
impl<M, H> Handler<M> for Arc<H>
where
H: Handler<M>,
{
fn handle(&self, msg: &M, ctx: &mut Context) -> impl Future<Output = HandlerResult> + Send {
(**self).handle(msg, ctx)
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::{HandlerResult, IntoHandlerResult};
#[test]
fn convenience_constructors_map_to_variants() {
assert_eq!(
HandlerResult::retry(),
HandlerResult::Nack { requeue: true }
);
assert_eq!(
HandlerResult::drop(),
HandlerResult::Nack { requeue: false }
);
assert_eq!(
HandlerResult::retry_after(Duration::from_secs(2)),
HandlerResult::NackAfter {
delay: Duration::from_secs(2)
}
);
}
#[test]
fn into_handler_result_covers_every_return_shape() {
assert_eq!(HandlerResult::Ack.into_handler_result(), HandlerResult::Ack);
assert_eq!(().into_handler_result(), HandlerResult::Ack);
assert_eq!(Ok::<(), ()>(()).into_handler_result(), HandlerResult::Ack);
assert_eq!(
Err::<(), ()>(()).into_handler_result(),
HandlerResult::drop()
);
assert_eq!(
Ok::<HandlerResult, ()>(HandlerResult::retry()).into_handler_result(),
HandlerResult::retry()
);
assert_eq!(
Err::<HandlerResult, ()>(()).into_handler_result(),
HandlerResult::drop()
);
}
}