pub struct RouterPipeline(/* private fields */);Expand description
Monadic router builder that propagates errors through the pipeline via Kleisli composition.
Wraps Result<Router<()>>. Each step is Result::and_then — any error
short-circuits the rest of the chain. Call build
at the end to surface the final Result<Router<()>>.
See module-level docs for the full operation table.
Implementations§
Source§impl RouterPipeline
impl RouterPipeline
Sourcepub fn mount<C: Controller>(self, state: Arc<C::State>) -> Self
pub fn mount<C: Controller>(self, state: Arc<C::State>) -> Self
Kleisli bind: thread the router through a Controller’s Kleisli
arrow.
Calls C::mount(state) to obtain the arrow, then threads it via
and_then. The controller’s routes are merged into the pipeline’s
router. Short-circuits if any previous step failed.
The controller has no knowledge of routing infrastructure — it only provides the Kleisli arrow. The pipeline is the sole compositor.
Sourcepub fn map<F>(self, f: F) -> Self
pub fn map<F>(self, f: F) -> Self
Functor map (fmap): apply an infallible Router -> Router transform.
The most common use is adding a middleware layer:
pipeline.map(|r| r.layer(TraceLayer::new_for_http()))Sourcepub fn and_then<F>(self, f: F) -> Self
pub fn and_then<F>(self, f: F) -> Self
Monad bind (>>=): apply a fallible Router -> Result<Router>
transform.
Short-circuits on any previous error. Use for transforms that can fail.
Sourcepub fn route<H, T>(
self,
route_info: (&'static str, &'static str),
handler: H,
) -> Self
pub fn route<H, T>( self, route_info: (&'static str, &'static str), handler: H, ) -> Self
Register a stateless route (no service state) using a route info tuple.
The route_info tuple is produced by a route macro annotation:
__root_route is ("/", "GET") when annotated #[get("/")].
The HTTP verb is enforced by ApiRoute::api_route.
Sourcepub fn build(self) -> Result<Router<()>>
pub fn build(self) -> Result<Router<()>>
Terminate the pipeline and return the built Router<()>.
Use ? at the call site to propagate any error that occurred during
pipeline construction:
let app = RouterPipeline::new()
.mount::<HealthController>(Arc::new(HealthService::new()))
.build()?;Sourcepub fn mount_if<C: Controller>(
self,
condition: bool,
state: Arc<C::State>,
) -> Self
pub fn mount_if<C: Controller>( self, condition: bool, state: Arc<C::State>, ) -> Self
Conditional mount: mount a Controller only when condition is
true.
When false, the pipeline passes through unchanged — no error produced.
The state value is moved into the mount call when condition is
true, or dropped when false.
RouterPipeline::new()
.mount::<HealthController>(health_svc)
.mount_if::<MetricsController>(config.enable_metrics, metrics_svc)
.mount_if::<AdminController>(env.is_dev(), admin_svc)
.build()?Sourcepub fn mount_guarded<C: Controller, G>(
self,
state: Arc<C::State>,
guard: G,
) -> Self
pub fn mount_guarded<C: Controller, G>( self, state: Arc<C::State>, guard: G, ) -> Self
Guarded mount: mount a Controller only when guard() returns
Ok(()).
The guard is a fallible predicate evaluated before the controller’s
Kleisli arrow runs. A guard error short-circuits the pipeline the same
way as a failed mount.
Use this for runtime checks (required config, capability flags, etc.):
RouterPipeline::new()
.mount_guarded::<AdminController>(admin_svc, || {
if config.admin_secret.is_empty() {
Err(Error::other("admin_secret must be set"))
} else {
Ok(())
}
})
.build()?Sourcepub fn fold<I, F>(self, steps: I) -> Self
pub fn fold<I, F>(self, steps: I) -> Self
Catamorphism (fold): apply a dynamic, ordered collection of fallible
Router -> Result<Router> steps, left-to-right.
Short-circuits on the first error. Replaces imperative for loops
when the set of pipeline steps is known only at runtime.
let steps: Vec<Box<dyn FnOnce(Router<()>) -> Result<Router<()>>>> = vec![
Box::new(HealthController::mount(health_svc)),
Box::new(EchoController::mount(echo_svc)),
];
RouterPipeline::new().fold(steps).build()?Sourcepub fn layer_all(
self,
transforms: impl IntoIterator<Item = RouterTransform>,
) -> Self
pub fn layer_all( self, transforms: impl IntoIterator<Item = RouterTransform>, ) -> Self
Apply a dynamic collection of infallible Router -> Router transforms,
left-to-right (fold over map).
Each item is a RouterTransform (Box<dyn FnOnce(Router<()>) -> Router<()>>) so heterogeneous transforms (different layer types)
can coexist in one collection. For a small, static set of layers,
chaining .map() is cleaner.
let transforms: Vec<RouterTransform> = vec![
Box::new(|r| r.layer(TraceLayer::new_for_http())),
Box::new(|r| r.layer(CorsLayer::permissive())),
];
RouterPipeline::new()
.mount::<HealthController>(svc)
.layer_all(transforms)
.build()?Sourcepub fn group<F>(self, prefix: &str, f: F) -> Self
pub fn group<F>(self, prefix: &str, f: F) -> Self
Run a sub-pipeline and nest all of its routes under prefix.
All controllers and routes registered inside the closure f will have
prefix prepended to their paths before being merged into the outer
router. This is the scoped functor: mapping a prefix transformation
over an enclosed group of routes.
RouterPipeline::new()
.group("/api/v1", |g| g
.mount::<HealthController>(health_svc)
.mount::<EchoController>(echo_svc)
)
.group("/internal", |g| g
.mount_if::<MetricsController>(config.enable_metrics, metrics_svc)
)
.build()?