1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
use std::{convert::Infallible, ops::Index};

use futures_util::future::BoxFuture;
pub use hyper::http::request::Parts;
use hyper::{Request, Response};
use tracing::{instrument, Instrument};

use crate::{extension::Matched, SgBody};

pub trait Router: Clone {
    type Index: Clone;
    fn route(&self, req: &mut Request<SgBody>) -> Option<Self::Index>;
}

#[derive(Debug, Clone)]
pub struct RouterService<S, R, F>
where
    R: Router,
{
    services: S,
    fallback: F,
    router: R,
}

impl<S, R, F> RouterService<S, R, F>
where
    R: Router,
    S: Index<R::Index>,
{
    pub fn new(services: S, router: R, fallback: F) -> Self {
        Self { services, router, fallback }
    }
}

impl<S, R, F> hyper::service::Service<Request<SgBody>> for RouterService<S, R, F>
where
    R: Router + Send + Sync + 'static,
    R::Index: Send + Sync + 'static + Clone,
    S: Index<R::Index>,
    S::Output: hyper::service::Service<Request<SgBody>, Response = Response<SgBody>, Error = Infallible> + Send + 'static,
    F: hyper::service::Service<Request<SgBody>, Response = Response<SgBody>, Error = Infallible> + Send + 'static,
    <F as hyper::service::Service<hyper::Request<SgBody>>>::Future: std::marker::Send,
    <S::Output as hyper::service::Service<hyper::Request<SgBody>>>::Future: std::marker::Send,
{
    type Error = Infallible;
    type Response = Response<SgBody>;
    type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
    #[instrument("router", skip_all, fields(http.uri =? req.uri(), http.method =? req.method()))]
    fn call(&self, mut req: Request<SgBody>) -> Self::Future {
        let fut: Self::Future = if let Some(index) = self.router.route(&mut req) {
            req.extensions_mut().insert(Matched {
                index: index.clone(),
                router: self.router.clone(),
            });
            let fut = self.services.index(index).call(req);
            Box::pin(fut.in_current_span())
        } else {
            let fut = self.fallback.call(req);
            Box::pin(fut.in_current_span())
        };
        fut
    }
}