blacktea/
router.rs

1// Copyright 2021 Black Tea Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::future::Future;
16use crate::response::HttpResponse;
17use async_trait::async_trait;
18use fnv::FnvHashMap;
19use hyper::{Method, Response, StatusCode};
20use route_recognizer::{Params, Router as InternalRouter};
21
22type HyperResponse = hyper::Response<hyper::Body>;
23
24pub struct Router {
25    method_map: FnvHashMap<Method, InternalRouter<Box<dyn Handler>>>,
26}
27
28#[async_trait]
29pub trait Handler: Send + Sync + 'static {
30    async fn invoke(&self) -> HyperResponse;
31}
32
33#[async_trait]
34impl<F: Send + Sync + 'static, Fut> Handler for F
35where
36    F: Fn() -> Fut,
37    Fut: Future + Send + 'static,
38    Fut::Output: IntoResponse,
39{
40    async fn invoke(&self) -> HyperResponse {
41        (self)().await.into_response()
42    }
43}
44
45pub trait IntoResponse: Send + Sized {
46    fn into_response(self) -> HyperResponse;
47}
48
49impl IntoResponse for HyperResponse {
50    fn into_response(self) -> HyperResponse {
51        self
52    }
53}
54
55impl IntoResponse for HttpResponse {
56    fn into_response(self) -> HyperResponse {
57        self.res()
58    }
59}
60
61pub struct RouterMatch<'a> {
62    pub handler: &'a dyn Handler,
63    pub params: Params,
64}
65
66impl Default for Router {
67    fn default() -> Self {
68        Self::new()
69    }
70}
71
72impl Router {
73    pub fn new() -> Self {
74        Self {
75            method_map: FnvHashMap::default(),
76        }
77    }
78
79    pub fn add(&mut self, path: &str, method: Method, handler: Box<dyn Handler>) {
80        self.method_map
81            .entry(method)
82            .or_insert_with(InternalRouter::new)
83            .add(path, handler);
84    }
85
86    pub fn route(&self, path: &str, method: &Method) -> RouterMatch {
87        if let Some(m) = self
88            .method_map
89            .get(method)
90            .and_then(|r| r.recognize(path).ok())
91        {
92            let mut params = Params::new();
93            params.clone_from(m.params());
94            RouterMatch {
95                handler: &***m.handler(),
96                params,
97            }
98        } else {
99            RouterMatch {
100                handler: &not_found_handler,
101                params: Params::new(),
102            }
103        }
104    }
105}
106
107async fn not_found_handler() -> HyperResponse {
108    Response::builder()
109        .status(StatusCode::NOT_FOUND)
110        .body("NOT FOUND".into())
111        .unwrap()
112}