gotham/pipeline/
chain.rs

1//! Defines the types for connecting multiple pipeline handles into a "chain" when constructing the
2//! dispatcher for a route.
3
4use borrow_bag::{Handle, Lookup};
5use futures_util::future::{self, FutureExt};
6use log::trace;
7use std::panic::RefUnwindSafe;
8use std::pin::Pin;
9
10use crate::handler::HandlerFuture;
11use crate::middleware::chain::NewMiddlewareChain;
12use crate::pipeline::set::PipelineSet;
13use crate::pipeline::Pipeline;
14use crate::state::{request_id, State};
15
16/// A heterogeneous list of `Handle<P, _>` values, where `P` is a pipeline type. The pipelines are
17/// borrowed and invoked in order to serve a request.
18///
19/// Implemented using nested tuples, with `()` marking the end of the list. The list is in the
20/// reverse order of their invocation when a request is dispatched.
21///
22/// That is:
23///
24/// `(p3, (p2, (p1, ())))`
25///
26/// will be invoked as:
27///
28/// `(state, request)` &rarr; `p1` &rarr; `p2` &rarr; `p3` &rarr; `handler`
29pub trait PipelineHandleChain<P>: RefUnwindSafe {
30    /// Invokes this part of the `PipelineHandleChain`, with requests being passed through to `f`
31    /// once all `Middleware` in the `Pipeline` have passed the request through.
32    fn call<F>(&self, pipelines: &PipelineSet<P>, state: State, f: F) -> Pin<Box<HandlerFuture>>
33    where
34        F: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static;
35}
36
37/// Part of a `PipelineHandleChain` which references a `Pipeline` and continues with a tail element.
38impl<P, T, N, U> PipelineHandleChain<P> for (Handle<Pipeline<T>, N>, U)
39where
40    T: NewMiddlewareChain,
41    T::Instance: Send + 'static,
42    U: PipelineHandleChain<P>,
43    P: Lookup<Pipeline<T>, N>,
44    N: RefUnwindSafe,
45{
46    fn call<F>(&self, pipelines: &PipelineSet<P>, state: State, f: F) -> Pin<Box<HandlerFuture>>
47    where
48        F: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static,
49    {
50        let (handle, ref chain) = *self;
51        match pipelines.borrow(handle).construct() {
52            Ok(p) => chain.call(pipelines, state, move |state| p.call(state, f)),
53            Err(e) => {
54                trace!("[{}] error borrowing pipeline", request_id(&state));
55                future::err((state, e.into())).boxed()
56            }
57        }
58    }
59}
60
61/// The marker for the end of a `PipelineHandleChain`.
62impl<P> PipelineHandleChain<P> for () {
63    fn call<F>(&self, _: &PipelineSet<P>, state: State, f: F) -> Pin<Box<HandlerFuture>>
64    where
65        F: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static,
66    {
67        trace!("[{}] start pipeline", request_id(&state));
68        f(state)
69    }
70}