ruststream 0.2.4

Async messaging framework for Rust: broker-agnostic traits, router, codecs, and a conformance harness for broker authors.
Documentation
# Middleware

Middleware wraps handlers with cross-cutting logic: tracing, metrics, auth, retries. RustStream has
two middleware levels, both built on the same `Layer` machinery, applied at different points in the
dispatch path.

## Global middleware

Add a layer to the whole application with `layer`, before `with_broker`. Every handler registered on
a broker scope is wrapped with it.

```rust
use ruststream::runtime::layers::TracingLayer;

let app = RustStream::new(info)
    .layer(TracingLayer::default())
    .with_broker(broker, |b| b.include(handle));
```

The first layer added is the outermost. The global stack is static: it has zero runtime dispatch
cost, and its type grows as you call `layer`.

!!! note "Routers opt out"
    The global stack does not wrap handlers brought in via `include_router`, because a router is
    finalized independently. Wrap those handlers inside the router if you need the same behaviour.

## Writing a layer

A layer transforms one handler into another. Implement `Layer<H>`:

```rust
use ruststream::runtime::{Context, Handler, HandlerResult, Layer};

struct LogLayer;

struct Logged<H>(H);

impl<H> Layer<H> for LogLayer {
    type Handler = Logged<H>;
    fn layer(&self, inner: H) -> Logged<H> {
        Logged(inner)
    }
}

impl<M, H: Handler<M>> Handler<M> for Logged<H> {
    async fn handle(&self, msg: &M, ctx: &mut Context<'_>) -> HandlerResult {
        // pre
        let result = self.0.handle(msg, ctx).await;
        // post
        result
    }
}
```

`Identity` is the no-op layer (the default global stack), and `Stack<Inner, Outer>` composes two.

## Per-handler middleware

Wrap a single handler with `HandlerExt::with` instead of the whole application:

```rust
use ruststream::runtime::HandlerExt;

let handler = base_handler.with(LogLayer);
```

This is the right tool when only some handlers need a layer. It composes with the global stack.

## Dynamic middleware

The static layers above are resolved at compile time. When you need a runtime-built chain (a list of
layers decided from config, or layers stored behind `dyn`), use the dynamic stack: `DynStack`,
`DynMiddleware`, and `Next`. A `DynMiddleware` has an around/next signature, so it can short-circuit
or wrap the call:

```rust
use ruststream::runtime::{DynMiddleware, Next};
```

Each dynamic layer costs one boxed future per call, against zero for the static layers, so prefer
static layers unless you genuinely need runtime composition.

## Publish-side middleware

The middleware above runs on the consume path (incoming messages). The publish path has its own
pipeline; see [Publishing and replies](publishing.md#the-publish-pipeline).

## Built-in layers

- `layers::TracingLayer` emits a tracing span per message.
- The `metrics` feature ships a layer that records Prometheus counters and a duration histogram; see
  [Metrics]metrics.md.