hyperbole 0.1.2

Reserved for now near-future use.
Documentation
use super::{
    combinators::{Add2, Base, Link},
    r,
    reply::Reply,
    tree::{Cluster, Params, Parser, PathSpec, Segment},
    Init, Response,
};
use frunk_core::hlist::HNil;
use futures::future::{ready, BoxFuture, FutureExt, TryFutureExt};
use hyper::{
    header::{CONTENT_TYPE, X_CONTENT_TYPE_OPTIONS},
    Body, Request, StatusCode,
};
use std::{net::SocketAddr, ops::Add};

pub trait Handler: Sync + Send {
    fn handle(&self, req: Request<Body>, addr: SocketAddr) -> BoxFuture<'static, Response>;
}

#[derive(Copy, Clone)]
pub struct NotFound;

impl Handler for NotFound {
    fn handle(&self, _: Request<Body>, _: SocketAddr) -> BoxFuture<'static, Response> {
        ready(
            hyper::Response::builder()
                .status(StatusCode::NOT_FOUND)
                .header(CONTENT_TYPE, "text/plain; charset=utf-8")
                .header(X_CONTENT_TYPE_OPTIONS, "nosniff")
                .body("404 page not found".into())
                .unwrap(),
        )
        .boxed()
    }
}

pub struct Chain<P, L> {
    path: PathSpec<P>,
    link: L,
}

impl<P, L: Clone> Clone for Chain<P, L> {
    fn clone(&self) -> Self {
        let path = self.path.clone();
        let link = self.link.clone();

        Self { path, link }
    }
}

impl<P: 'static, L: 'static> Handler for Chain<P, L>
where
    P: Parser<Cluster>,
    L: Sync + Send + Link<Init, P, Output = Response, Params = HNil>,
    <L as Link<Init, P>>::Error: Reply,
{
    fn handle(&self, req: Request<Body>, addr: SocketAddr) -> BoxFuture<'static, Response> {
        let (p, body) = req.into_parts();

        let params = match self.path.parse_params(p.uri.path(), p.uri.query()) {
            Err(e) => return ready(e.into_response()).boxed(),
            Ok(ps) => ps,
        };

        let state = r![
            body,
            p.method,
            p.uri,
            p.version,
            p.headers,
            p.extensions,
            addr,
        ];

        (self.link.handle_link(state, params))
            .map_ok(|(resp, _)| resp)
            .unwrap_or_else(Reply::into_response)
            .boxed()
    }
}

impl<P> Chain<P, Base> {
    pub const fn new(path: PathSpec<P>) -> Self {
        Self { path, link: Base }
    }
}

impl<P, L> Chain<P, L> {
    pub fn link_next<Ln, F: FnOnce(L) -> Ln>(self, wrap: F) -> Chain<P, Ln> {
        let path = self.path;
        let link = wrap(self.link);
        Chain { path, link }
    }

    pub fn add_path<_P>(self, spec: PathSpec<_P>) -> Chain<Add2<P, Params<_P>>, L>
    where
        P: Add<Params<_P>>,
        _P: Parser<Segment>,
    {
        let path = self.path.append(spec);
        let link = self.link;
        Chain { path, link }
    }

    pub const fn path(&self) -> &PathSpec<P> {
        &self.path
    }
}