pathmaker/
hyper.rs

1use failure::{Compat, Error};
2use hyper::service::Service;
3use hyper::{Body, Method, Request, Response, StatusCode};
4use std::borrow::Cow;
5use futures::prelude::*;
6
7type HandlerFuture = Box<dyn Future<Item = Response<Body>, Error = failure::Error> + Send + 'static>;
8
9/// The handler that's stored as a part of every route in the router.  Since
10/// we're dealing with Hyper, it must return a future; we use the `Box<Fn>`
11/// type in order to keep flexibility.
12///
13/// The [`Route`] and [`Build`] types automatically box the closure as a part
14/// of its shortcut methods.
15pub type Handler = Box<dyn Fn(Request<Body>, Vec<String>) -> HandlerFuture + Send + 'static>;
16
17/// A single route, tied to Hyper's types, and our [`Handler`].  We add some
18/// shortcut methods onto this type in order to make building routes for hyper
19/// easier.
20pub type Route = super::router::Route<Method, Handler>;
21
22/// The router type, tied to Hyper's types, and our [`Handler`].  This
23/// implements [`hyper::serivce::Service`] by default, and if no default handler
24/// is given, it returns an empty 404 response.
25pub type Router = super::router::Router<Method, Handler>;
26
27/// A builder for building routes, tied to Hyper's types and our [`Handler`].
28/// We add some shortcut methods onto this type in order to make building
29/// routes for hyper easier.
30pub type Build = super::router::Build<Method, Handler>;
31
32macro_rules! route {
33    (
34        $(#$meta:tt)*
35        $name:ident => $method:expr
36    ) => {
37        $(#$meta)*
38        pub fn $name<P, F>(path: P, handler: F) -> Self
39        where
40            P: Into<Cow<'static, str>>,
41            F: Fn(Request<Body>, Vec<String>) -> HandlerFuture + Send + 'static
42        {
43            Self::new(path, $method, Box::new(handler))
44        }
45    };
46}
47
48impl Route {
49    route!(options => Method::OPTIONS);
50    route!(get => Method::GET);
51    route!(post => Method::POST);
52    route!(put => Method::PUT);
53    route!(delete => Method::DELETE);
54    route!(head => Method::HEAD);
55    route!(trace => Method::TRACE);
56    route!(connect => Method::CONNECT);
57    route!(patch => Method::PATCH);
58}
59
60macro_rules! build {
61    (
62        $(#$meta:tt)*
63        $name:ident
64    ) => {
65        $(#$meta)*
66        pub fn $name<P, F>(&mut self, path: P, handler: F) -> &mut Self
67        where
68            P: Into<Cow<'static, str>>,
69            F: Fn(Request<Body>, Vec<String>) -> HandlerFuture + Send + 'static
70        {
71            self.add(Route::$name(path, handler))
72        }
73    }
74}
75
76impl Build {
77    build!(options);
78    build!(get);
79    build!(post);
80    build!(put);
81    build!(delete);
82    build!(head);
83    build!(trace);
84    build!(connect);
85    build!(patch);
86
87    pub fn default_fn<F>(&mut self, default: F) -> &mut Self
88        where F: Fn(Request<Body>, Vec<String>) -> HandlerFuture + Send + 'static
89    {
90        self.with_default(Box::new(default))
91    }
92}
93
94impl Service for Router {
95    type ReqBody = Body;
96    type ResBody = Body;
97    type Error = Compat<Error>;
98    type Future = Box<dyn Future<Item = Response<Body>, Error = Compat<Error>> + Send + 'static>;
99
100    fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
101        let path = req.uri().path();
102        if let Some((handler, params)) = self.lookup(req.method(), path) {
103            let params = params.into_iter().map(str::to_string).collect();
104            Box::new(handler(req, params).map_err(Error::compat))
105        } else {
106            let response = Response::builder()
107                .status(StatusCode::NOT_FOUND)
108                .body(Body::empty())
109                .map_err(Error::from)
110                .map_err(Error::compat);
111            Box::new(futures::future::result(response))
112        }
113    }
114}