ruststream 0.4.0

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

Two things can go wrong before your handler logic even gets a chance: the handler body can
**panic**, and an incoming payload can fail to **decode**. RustStream settles both through one
vocabulary, set per subscriber with the `on_failure(..)` clause, but with different defaults,
because the two failures mean different things.

## Defaults

With no clause, a subscriber uses the built-in defaults:

- **panic = `fail_fast`**: a panic is an internal bug. The runtime logs a loud error naming the
  subscription, then starts a graceful shutdown (it cancels the shutdown token and runs the
  shutdown hooks) and makes [`run`]../index.md return `Err` with a non-zero exit. An orchestrator
  restarts the service and the operator lands in the logs, instead of a subscriber that silently
  stopped consuming or an invisible redelivery loop.
- **decode = `drop`**: a decode failure is usually bad external input. Dropping the one bad message
  (a nack without requeue) keeps a single malformed payload from taking the consumer down, which on
  an untrusted topic would be a poison-message or denial-of-service footgun.

```rust
--8<-- "examples/failure_policy.rs:defaults"
```

## Setting a policy

`on_failure(panic = .., decode = ..)` overrides either key (both are optional; an omitted key keeps
its default):

```rust
--8<-- "examples/failure_policy.rs:tuned"
```

The policy values are:

| Value                 | Effect                                                                |
|-----------------------|-----------------------------------------------------------------------|
| `fail_fast`           | Log, start a graceful shutdown, and make `run` return `Err`.           |
| `drop`                | Drop the message (`nack` without requeue).                             |
| `retry`               | Requeue the message (`nack` with requeue).                            |
| `retry_after(<dur>)`  | Requeue after a delay (see the delayed-redelivery section in [Subscribers]subscribers.md). |
| `skip`                | Acknowledge the failed message to move past it. Not success: the message is gone, unprocessed. |

`skip` is the deliberate poison-message escape hatch: it advances past a message that cannot be
processed rather than dropping or retrying it.

```rust
--8<-- "examples/failure_policy.rs:skip"
```

## How it behaves

- A panic is caught (`catch_unwind`), so a panicking handler never kills the dispatch loop. Under
  `fail_fast` the message is left unsettled, so a broker with redelivery hands it back after the
  restart; under the other policies it is settled and the subscriber keeps consuming. Catching only
  applies under an unwinding panic profile; with `panic = "abort"` the process is already gone.
- A decode failure surfaces as a `Result`, so no unwinding is involved; the `decode` policy settles
  the message directly. The same `on_decode_failure` policy can also be set when building a handler
  by hand through the typed adapter (see [Codecs]codecs.md).
- On the batch path the policy applies per batch decode (each element decodes independently) and to
  a panic in the batch handler. Per-element panic handling is out of scope.

This is the full example: [`examples/failure_policy.rs`](https://github.com/powersemmi/ruststream/blob/main/examples/failure_policy.rs).