pub mod chain;
pub mod set;
pub mod single;
use log::trace;
use std::pin::Pin;
use crate::handler::HandlerFuture;
use crate::middleware::chain::{MiddlewareChain, NewMiddlewareChain};
use crate::middleware::NewMiddleware;
use crate::state::{request_id, State};
pub struct Pipeline<T>
where
T: NewMiddlewareChain,
{
chain: T,
}
struct PipelineInstance<T>
where
T: MiddlewareChain,
{
chain: T,
}
impl<T> Pipeline<T>
where
T: NewMiddlewareChain,
{
fn construct(&self) -> anyhow::Result<PipelineInstance<T::Instance>> {
Ok(PipelineInstance {
chain: self.chain.construct()?,
})
}
}
impl<T> PipelineInstance<T>
where
T: MiddlewareChain,
{
fn call<F>(self, state: State, f: F) -> Pin<Box<HandlerFuture>>
where
F: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static,
{
trace!("[{}] calling middleware", request_id(&state));
self.chain.call(state, f)
}
}
pub fn new_pipeline() -> PipelineBuilder<()> {
trace!(" starting pipeline construction");
PipelineBuilder { t: () }
}
pub fn single_middleware<M>(m: M) -> Pipeline<(M, ())>
where
M: NewMiddleware,
M::Instance: Send + 'static,
{
new_pipeline().add(m).build()
}
pub struct PipelineBuilder<T>
where
T: NewMiddlewareChain,
{
t: T,
}
impl<T> PipelineBuilder<T>
where
T: NewMiddlewareChain,
{
pub fn build(self) -> Pipeline<T>
where
T: NewMiddlewareChain,
{
Pipeline { chain: self.t }
}
pub fn add<M>(self, m: M) -> PipelineBuilder<(M, T)>
where
M: NewMiddleware,
M::Instance: Send + 'static,
Self: Sized,
{
trace!(" adding middleware to pipeline");
PipelineBuilder { t: (m, self.t) }
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::prelude::*;
use hyper::{Body, Response, StatusCode};
use crate::handler::Handler;
use crate::middleware::Middleware;
use crate::state::StateData;
use crate::test::TestServer;
fn handler(state: State) -> (State, Response<Body>) {
let number = state.borrow::<Number>().value;
(
state,
Response::builder()
.status(StatusCode::OK)
.body(format!("{}", number).into())
.unwrap(),
)
}
#[derive(Clone)]
struct Number {
value: i32,
}
impl NewMiddleware for Number {
type Instance = Number;
fn new_middleware(&self) -> anyhow::Result<Number> {
Ok(self.clone())
}
}
impl Middleware for Number {
fn call<Chain>(self, mut state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
where
Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static,
Self: Sized,
{
state.put(self.clone());
chain(state)
}
}
impl StateData for Number {}
struct Addition {
value: i32,
}
impl NewMiddleware for Addition {
type Instance = Addition;
fn new_middleware(&self) -> anyhow::Result<Addition> {
Ok(Addition { ..*self })
}
}
impl Middleware for Addition {
fn call<Chain>(self, mut state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
where
Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static,
Self: Sized,
{
state.borrow_mut::<Number>().value += self.value;
chain(state)
}
}
struct Multiplication {
value: i32,
}
impl NewMiddleware for Multiplication {
type Instance = Multiplication;
fn new_middleware(&self) -> anyhow::Result<Multiplication> {
Ok(Multiplication { ..*self })
}
}
impl Middleware for Multiplication {
fn call<Chain>(self, mut state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
where
Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + 'static,
Self: Sized,
{
state.borrow_mut::<Number>().value *= self.value;
chain(state)
}
}
#[test]
fn pipeline_ordering_test() {
let test_server = TestServer::new(|| {
let pipeline = new_pipeline()
.add(Number { value: 0 }) .add(Addition { value: 1 }) .add(Multiplication { value: 2 }) .add(Addition { value: 1 }) .add(Multiplication { value: 2 }) .add(Addition { value: 2 }) .add(Multiplication { value: 3 }) .build();
Ok(move |state| match pipeline.construct() {
Ok(p) => p.call(state, |state| handler.handle(state)),
Err(e) => future::err((state, e.into())).boxed(),
})
})
.unwrap();
let response = test_server
.client()
.get("http://localhost/")
.perform()
.unwrap();
let buf = response.read_body().unwrap();
assert_eq!(buf.as_slice(), b"24");
}
}