# Typed Pipeline
The typed pipeline is rs-netty's central constraint mechanism. It puts stage order and message transitions into builder type parameters so many mistakes become compile-time errors.
## Shape
TCP:
```rust
pipeline()
.codec(...)
.inbound(...)*
.business(...)*
.handler(...)
.outbound(...)*
```
UDP:
```rust
datagram_pipeline()
.codec(...)
.inbound(...)*
.business(...)*
.handler(...)
.outbound(...)*
```
The two builders have the same stage shape, but different codec traits and final handler traits.
## Builder States
Shared state markers live in `pipeline/core/state.rs`:
- `Start`: initial state; only a codec can be added.
- `InboundPhase`: a codec exists; inbound, business, or final handler can be added.
- `BusinessPhase`: business processing has started; more business stages or the final handler can be added.
- `Ready`: the final handler exists; outbound stages can be added and the builder can become a runtime pipeline.
These states appear as the first type parameter of `PipelineBuilder<State, C, InP, BizP, H, OutP, CurrentIn, Write, CurrentOut>` and `DatagramPipelineBuilder<...>`. If a stage is illegal in the current state, the method is simply not implemented for that builder type.
## Message Type Chain
For TCP, the constraints are:
```text
C: Decoder<Item = A>
InboundPipe<A, Out = B>
BusinessPipe<B, Out = CIn>
H: Handler<CIn, Write = W>
OutboundPipe<W, Out = COut>
codec: Encoder<COut>
```
This means:
- the first inbound stage must accept the type decoded by the codec.
- each following inbound/business stage must accept the previous stage's `Out`.
- the final handler input must match the inbound/business chain output.
- the first outbound stage must accept `Handler::Write`.
- the final outbound output must be encodable by the stream codec.
UDP uses the same type chain, but swaps in `DatagramDecoder` / `DatagramEncoder` and `DatagramHandler`.
## Compile Failures Are Intentional API
The trybuild compile-fail tests document these constraints. This pipeline does not compile because `Parse` converts `String` into `Request`, but the final handler expects `String`:
```rust
let _ = pipeline()
.codec(LineCodec::new())
.inbound(Parse)
.handler(EchoString);
```
This one also fails because `LineCodec` can encode `String`, while the handler writes `Response` and no outbound stage converts it:
```rust
let _ = TcpServer::bind("127.0.0.1:0")
.pipeline(|| pipeline().codec(LineCodec::new()).handler(Router));
```
The fix is to add an outbound stage:
```rust
let _ = pipeline()
.codec(LineCodec::new())
.inbound(Parse)
.handler(Router)
.outbound(RenderResponse);
```
## Flow
`Inbound`, `Business`, and `Outbound` return `Result<Flow<T>>`:
```rust
pub enum Flow<T> {
Next(T),
Stop,
}
```
`Flow::Next` continues the pipeline. `Flow::Stop` consumes the current message and stops processing in that direction without error. Final `Handler` / `DatagramHandler` implementations do not return `Flow`; they return `Result<()>` because they are the end of the inbound side.