use std::future::Future;
use super::context::Context;
use super::handler::{Handler, HandlerOutcome};
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot wrap `{H}`",
note = "a layer that wraps one registration implements `Layer<H>` for the handler it wraps; \
a layer that wraps every handler in an app or a router implements `BlanketLayer` \
instead and rides `RustStream::layer(..)` or `Router::layer(..)`"
)]
pub trait Layer<H> {
type Handler;
fn layer(&self, inner: H) -> Self::Handler;
}
pub trait BlanketLayer: Send + Sync {
fn apply<M, C, S, H>(&self, handler: H) -> impl Handler<M, C, S> + 'static
where
M: Send + Sync + 'static,
C: Send + 'static,
S: Send + Sync + 'static,
H: Handler<M, C, S> + 'static;
}
pub trait HandlerExt<M, C = (), S = ()>: Handler<M, C, S> + Sized {
fn with<L>(self, layer: L) -> L::Handler
where
L: Layer<Self>,
{
layer.layer(self)
}
}
impl<M, C, S, H> HandlerExt<M, C, S> for H where H: Handler<M, C, S> {}
#[derive(Debug, Clone, Copy, Default)]
pub struct Identity;
impl<H> Layer<H> for Identity {
type Handler = H;
fn layer(&self, inner: H) -> H {
inner
}
}
impl BlanketLayer for Identity {
fn apply<M, C, S, H>(&self, handler: H) -> impl Handler<M, C, S> + 'static
where
M: Send + Sync + 'static,
C: Send + 'static,
S: Send + Sync + 'static,
H: Handler<M, C, S> + 'static,
{
handler
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Stack<Inner, Outer> {
inner: Inner,
outer: Outer,
}
impl<Inner, Outer> Stack<Inner, Outer> {
#[must_use]
pub fn new(inner: Inner, outer: Outer) -> Self {
Self { inner, outer }
}
}
impl<H, Inner, Outer> Layer<H> for Stack<Inner, Outer>
where
Inner: Layer<H>,
Outer: Layer<Inner::Handler>,
{
type Handler = Outer::Handler;
fn layer(&self, inner: H) -> Self::Handler {
self.outer.layer(self.inner.layer(inner))
}
}
impl<Inner, Outer> BlanketLayer for Stack<Inner, Outer>
where
Inner: BlanketLayer,
Outer: BlanketLayer,
{
fn apply<M, C, S, H>(&self, handler: H) -> impl Handler<M, C, S> + 'static
where
M: Send + Sync + 'static,
C: Send + 'static,
S: Send + Sync + 'static,
H: Handler<M, C, S> + 'static,
{
self.outer
.apply::<M, C, S, _>(self.inner.apply::<M, C, S, _>(handler))
}
}
pub mod layers {
use tracing::{debug, info, instrument, warn};
use super::super::handler::HandlerResult;
use super::{BlanketLayer, Context, Future, Handler, HandlerOutcome, Layer};
#[derive(Debug, Clone, Default)]
pub struct TracingLayer {
target: Option<&'static str>,
}
impl TracingLayer {
#[must_use]
pub const fn with_target(target: &'static str) -> Self {
Self {
target: Some(target),
}
}
}
impl<H> Layer<H> for TracingLayer {
type Handler = TracingHandler<H>;
fn layer(&self, inner: H) -> Self::Handler {
TracingHandler {
inner,
target: self.target,
}
}
}
impl BlanketLayer for TracingLayer {
fn apply<M, C, S, H>(&self, handler: H) -> impl Handler<M, C, S> + 'static
where
M: Send + Sync + 'static,
C: Send + 'static,
S: Send + Sync + 'static,
H: Handler<M, C, S> + 'static,
{
self.layer(handler)
}
}
#[derive(Debug, Clone)]
pub struct TracingHandler<H> {
inner: H,
target: Option<&'static str>,
}
impl<M, C, S, H> Handler<M, C, S> for TracingHandler<H>
where
M: Sync,
C: Send,
S: Send + Sync,
H: Handler<M, C, S>,
{
#[instrument(level = "trace", skip(self, msg, ctx), fields(target = self.target))]
fn handle(
&self,
msg: &M,
ctx: &mut Context<'_, C, S>,
) -> impl Future<Output = HandlerOutcome> + Send {
async move {
debug!(target: "ruststream::dispatch", "delivery received");
let settle = self.inner.handle(msg, ctx).await;
match settle.outcome() {
HandlerResult::Ack => {
info!(target: "ruststream::dispatch", "handler ack");
}
HandlerResult::Nack { requeue } => {
warn!(target: "ruststream::dispatch", requeue, "handler nack");
}
HandlerResult::NackAfter { delay } => {
warn!(target: "ruststream::dispatch", ?delay, "handler delayed nack");
}
}
settle
}
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::*;
use crate::HeaderMap;
use crate::runtime::dispatch::Delivery;
struct Fixed(HandlerResult);
impl Handler<(), (), ()> for Fixed {
fn handle(
&self,
_msg: &(),
_ctx: &mut Context<'_, (), ()>,
) -> impl Future<Output = HandlerOutcome> + Send {
let outcome = self.0;
async move { HandlerOutcome::from(outcome) }
}
}
async fn traced(layer: &TracingLayer, outcome: HandlerResult) -> HandlerResult {
let state = ();
let delivery = Delivery::empty();
let headers = HeaderMap::new();
let mut ctx = Context::new("orders", &headers, &state, (), &delivery);
layer
.layer(Fixed(outcome))
.handle(&(), &mut ctx)
.await
.outcome()
}
#[tokio::test]
async fn the_tracing_layer_logs_every_outcome_and_passes_it_through() {
let layer = TracingLayer::default();
assert_eq!(traced(&layer, HandlerResult::Ack).await, HandlerResult::Ack);
assert_eq!(
traced(&layer, HandlerResult::retry()).await,
HandlerResult::retry()
);
let delayed = HandlerResult::retry_after(Duration::from_secs(3));
assert_eq!(traced(&layer, delayed).await, delayed);
}
#[tokio::test]
async fn a_target_scoped_layer_wraps_the_same_way() {
let layer = TracingLayer::with_target("my_service::orders");
assert_eq!(traced(&layer, HandlerResult::Ack).await, HandlerResult::Ack);
}
}
}