Skip to main content

RouterPipeline

Struct RouterPipeline 

Source
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

Source

pub fn new() -> Self

Start a new pipeline with an empty Router<()>.

Source

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.

Source

pub fn map<F>(self, f: F) -> Self
where F: FnOnce(Router<()>) -> Router<()>,

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()))
Source

pub fn and_then<F>(self, f: F) -> Self
where F: FnOnce(Router<()>) -> Result<Router<()>>,

Monad bind (>>=): apply a fallible Router -> Result<Router> transform.

Short-circuits on any previous error. Use for transforms that can fail.

Source

pub fn route<H, T>( self, route_info: (&'static str, &'static str), handler: H, ) -> Self
where H: Handler<T, ()>, T: 'static,

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.

Source

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()?;
Source

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()?
Source

pub fn mount_guarded<C: Controller, G>( self, state: Arc<C::State>, guard: G, ) -> Self
where G: FnOnce() -> Result<()>,

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()?
Source

pub fn fold<I, F>(self, steps: I) -> Self
where I: IntoIterator<Item = F>, F: FnOnce(Router<()>) -> Result<Router<()>>,

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()?
Source

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()?
Source

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()?

Trait Implementations§

Source§

impl Default for RouterPipeline

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,