1use std::future::Future;
2
3use tower_service::Service;
4
5use crate::error::Error;
6use crate::func::Func;
7use crate::mount::Mount;
8use crate::or::Or;
9use crate::param::Param;
10use crate::request::PathReq;
11use crate::request::RemovePrefix;
12use crate::route::Route;
13use crate::void::Void;
14use crate::with::With;
15
16#[derive(Clone, Copy, Debug)]
20pub struct Router<R> {
21 inner: R,
22}
23
24impl<R, T> Service<T> for Router<R>
25where
26 R: Service<T>,
27{
28 type Response = R::Response;
29
30 type Error = R::Error;
31
32 type Future = R::Future;
33
34 fn poll_ready(
35 &mut self,
36 _: &mut std::task::Context<'_>,
37 ) -> std::task::Poll<Result<(), Self::Error>> {
38 std::task::Poll::Ready(Ok(()))
39 }
40
41 fn call(&mut self, req: T) -> Self::Future {
42 self.inner.call(req)
43 }
44}
45
46impl<T, U> Router<Void<T, U>> {
47 pub const fn void() -> Router<Void<T, U>> {
49 Router { inner: Void::new() }
50 }
51}
52
53impl<F, P> Router<Func<F, P>> {
54 #[inline]
56 pub fn new<T, U, E, Fut>(route: F) -> Router<Func<F, P>>
57 where
58 F: FnMut(T, P) -> Fut,
59 P: Param<T>,
60 E: From<Error>,
61 Fut: Future<Output = Result<U, E>>,
62 {
63 Router {
64 inner: Func::new(route),
65 }
66 }
67}
68
69impl<R> Router<R> {
70 #[inline]
72 pub fn route<F, T, P, U, E, Fut>(self, route: F) -> Router<Or<R, Func<F, P>>>
73 where
74 R: Route<T, Response = U, Error = E>,
75 F: FnMut(T, P) -> Fut,
76 P: Param<T>,
77 E: From<Error>,
78 Fut: Future<Output = Result<U, E>>,
79 {
80 Router {
81 inner: Or::new(self.inner, Func::new(route)),
82 }
83 }
84
85 #[inline]
102 pub fn mount<S, T, U, E>(self, prefix: &'static str, service: S) -> Router<Or<R, Mount<S>>>
103 where
104 R: Route<T, Response = U, Error = E>,
105 S: Service<T, Response = U, Error = E>,
106 T: PathReq + RemovePrefix,
107 E: From<Error>,
108 {
109 let prefix = prefix.trim_end_matches('/');
110 assert!(!prefix.contains('?'), "Prefix cannot contains '?'");
111 assert!(!prefix.contains('#'), "Prefix cannot contains '#'");
112 assert!(!prefix.is_empty(), "Prefix cannot be empty");
113 assert!(prefix != "/", "Prefix cannot be '/'");
114 assert!(prefix.starts_with('/'), "Prefix must be starts with '/'");
115 Router {
116 inner: Or::new(self.inner, Mount::new(service, prefix)),
117 }
118 }
119
120 #[inline]
122 pub fn with<F, T, Fut>(self, func: F) -> Router<With<R, F>>
123 where
124 R: Clone + Service<T>,
125 F: FnMut(T) -> Fut,
126 Fut: Future<Output = Result<T, R::Error>>,
127 {
128 Router {
129 inner: With::new(self.inner, func),
130 }
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use std::convert::Infallible;
137 use std::net::SocketAddr;
138
139 use http::Request;
140 use http::Response;
141 use hyper::service::make_service_fn;
142 use hyper::Body;
143
144 use crate::error::Error;
145 use crate::param::Param;
146 use crate::Router;
147
148 #[derive(Clone, Copy, Debug)]
149 struct Home;
150
151 impl Param<Request<Body>> for Home {
152 fn from_request(req: &Request<Body>) -> Result<Self, Error> {
153 match req.uri().path() {
154 "/" => Ok(Home),
155 _ => Err(Error::Path),
156 }
157 }
158 }
159
160 async fn home(req: Request<Body>, param: Home) -> Result<Response<Body>, Error> {
161 Ok(Response::new(Body::from(format!(
162 "{:?} @ {}",
163 param,
164 req.uri().path()
165 ))))
166 }
167
168 #[derive(Clone, Copy, Debug)]
169 struct About;
170
171 impl Param<Request<Body>> for About {
172 fn from_request(req: &Request<Body>) -> Result<Self, Error> {
173 match req.uri().path() {
174 "/about" => Ok(About),
175 _ => Err(Error::Path),
176 }
177 }
178 }
179
180 async fn about(req: Request<Body>, param: About) -> Result<Response<Body>, Error> {
181 Ok(Response::new(Body::from(format!(
182 "{:?} @ {}",
183 param,
184 req.uri().path()
185 ))))
186 }
187
188 #[test]
189 #[allow(dead_code)]
190 fn compile() {
191 let subrouter = Router::new(home).route(about);
192 let router = Router::new(home).route(about).mount("/sub", subrouter);
193
194 let _ = |addr: SocketAddr| async move {
195 let _ = hyper::server::Server::bind(&addr)
196 .serve(make_service_fn(
197 |_| async move { Ok::<_, Infallible>(router) },
198 ))
199 .await;
200 };
201 }
202}