# Handlers
rs-netty has five stage traits. They live in `src/traits.rs`, and `trait-variant` generates the public `Send` variants used on the main path.
## Inbound
`Inbound<I>` is an inbound transformation stage after decoding and before the final handler:
```rust
struct Trim;
impl Inbound<String> for Trim {
type Out = String;
async fn read(
&mut self,
_ctx: &mut rs_netty::InboundContext,
msg: String,
) -> rs_netty::Result<rs_netty::Flow<Self::Out>> {
Ok(rs_netty::Flow::Next(msg.trim().to_string()))
}
}
```
`InboundContext` exposes `id()`, `peer_addr()`, and `local_addr()`. It does not allow writes.
## Business
`Business<I>` is an application-level transformation stage between inbound stages and the final handler. It has the same type shape as `Inbound`, but the method is `handle` and the context is `BusinessContext`. Once the builder enters the business phase, it can add more business stages or the final handler, but cannot go back to inbound stages.
## Handler
`Handler<I>` is the end of the TCP inbound side:
```rust
impl Handler<Request> for Router {
type Write = Response;
async fn read(&mut self, ctx: &mut Context<Self::Write>, req: Request) -> Result<()> {
ctx.write_and_flush(Response { body: req.body }).await
}
}
```
`type Write` is the application type this handler can write to the outbound side. Outbound stages start from this type and eventually produce a value the codec can encode.
`Context<W>` provides:
- identity: `id`, `peer_addr`, `local_addr`
- `channel()`: a cloneable external channel
- `stats()`: `ConnectionStats` when stats are enabled
- `write`, `flush`, `write_and_flush`
- `close`
## DatagramHandler
`DatagramHandler<I>` is the end of the UDP inbound side. It uses `DatagramContext<W>`, which supports `write`, `write_to`, `flush`, `write_and_flush`, `write_to_and_flush`, and `close`.
```rust
impl DatagramHandler<String> for UdpEcho {
type Write = String;
async fn read(&mut self, ctx: &mut DatagramContext<Self::Write>, msg: String) -> Result<()> {
ctx.write_and_flush(format!("echo: {msg}")).await
}
}
```
## Outbound
`Outbound<I>` converts the application type written by a handler into the next outbound type. The final outbound type must be encodable by the codec.
```rust
struct RenderResponse;
impl Outbound<Response> for RenderResponse {
type Out = String;
async fn write(
&mut self,
_ctx: &mut rs_netty::OutboundContext,
msg: Response,
) -> rs_netty::Result<rs_netty::Flow<Self::Out>> {
Ok(rs_netty::Flow::Next(msg.body))
}
}
```
`OutboundContext` also exposes only identity information and does not allow direct writes.
## Macro Or Manual Impl
`#[handler]` is good for simple one-in/one-out final handlers and consume-only handlers. Write a manual impl when you need direct `Context` / `DatagramContext` access, explicit flush timing, multiple writes, connection close, or `channel()`.