1pub mod filter;
2
3use crate::{handler_fn, FnTrait, PathParams};
4use crate::handler::RequestHandler;
5
6use crate::handler::handler_decorator::HandlerDecorator;
7use crate::handler::handler_decorator_factory::{
8 HandlerDecoratorFactory, HandlerDecoratorFactoryComposer, HandlerDecoratorFactoryExt, IdentityHandlerDecoratorFactory,
9};
10use filter::{AllFilter, Filter};
11use std::collections::HashMap;
12use tracing::error;
13use crate::extract::FromRequest;
14use crate::responder::Responder;
15
16type RouterFilter = dyn Filter + Send + Sync + 'static;
17type InnerRouter<T> = matchit::Router<T>;
18
19#[derive(Debug)]
21pub struct Router {
22 inner_router: InnerRouter<Vec<RouterItem>>,
23}
24
25#[derive(Debug)]
27pub struct RouterItem {
28 filter: Box<RouterFilter>,
29 handler: Box<dyn RequestHandler>,
30}
31
32#[derive(Debug)]
34pub struct RouteResult<'router, 'req> {
35 router_items: &'router [RouterItem],
36 params: PathParams<'router, 'req>,
37}
38
39impl Router {
40 pub fn builder() -> RouterBuilder<IdentityHandlerDecoratorFactory> {
42 RouterBuilder::new()
43 }
44
45 pub fn at<'router, 'req>(&'router self, path: &'req str) -> RouteResult<'router, 'req> {
52 self.inner_router
53 .at(path)
54 .map(|matched| RouteResult { router_items: matched.value.as_slice(), params: matched.params.into() })
55 .map_err(|e| error!("match '{}' error: {}", path, e))
56 .unwrap_or(RouteResult::empty())
57 }
58}
59
60impl RouterItem {
61 pub fn filter(&self) -> &RouterFilter {
63 self.filter.as_ref()
64 }
65
66 pub fn handler(&self) -> &dyn RequestHandler {
68 self.handler.as_ref()
69 }
70}
71
72impl<'router, 'req> RouteResult<'router, 'req> {
73 fn empty() -> Self {
74 Self { router_items: &[], params: PathParams::empty() }
75 }
76
77 #[inline]
79 pub fn is_empty(&self) -> bool {
80 self.router_items.is_empty()
81 }
82
83 pub fn params(&self) -> &PathParams<'router, 'req> {
85 &self.params
86 }
87
88 pub fn router_items(&self) -> &'router [RouterItem] {
90 self.router_items
91 }
92}
93
94#[derive(Debug)]
95pub struct RouterBuilder<DF> {
96 data: HashMap<String, Vec<RouterItemBuilder>>,
97 decorator_factory: DF,
98}
99
100impl RouterBuilder<IdentityHandlerDecoratorFactory> {
101 fn new() -> Self {
102 Self { data: HashMap::new(), decorator_factory: IdentityHandlerDecoratorFactory }
103 }
104}
105impl<DF> RouterBuilder<DF> {
106 pub fn route(mut self, route: impl Into<String>, item_builder: RouterItemBuilder) -> Self {
107 let vec = self.data.entry(route.into()).or_default();
108 vec.push(item_builder);
109 self
110 }
111
112 pub fn with_global_decorator<DF2>(self, factory: DF2) -> RouterBuilder<HandlerDecoratorFactoryComposer<DF, DF2>>
113 where
114 DF: HandlerDecoratorFactory,
115 DF2: HandlerDecoratorFactory,
116 {
117 RouterBuilder { data: self.data, decorator_factory: self.decorator_factory.and_then(factory) }
118 }
119
120 pub fn build(self) -> Router
122 where
123 DF: HandlerDecoratorFactory,
124 {
125 let mut inner_router = InnerRouter::new();
126
127 for (path, items) in self.data.into_iter() {
128 let router_items = items
129 .into_iter()
130 .map(|item_builder| item_builder.build())
131 .map(|item| {
132 let decorator = self.decorator_factory.create_decorator();
133 let handler = decorator.decorate(item.handler);
134 RouterItem { handler: Box::new(handler), ..item }
135 })
136 .collect::<Vec<_>>();
137
138 inner_router.insert(path, router_items).unwrap();
139 }
140
141 Router { inner_router }
142 }
143}
144
145macro_rules! inner_method_router_filter {
146 ($method:ident, $method_name:ident) => {
147 #[inline]
148 pub fn $method<H: RequestHandler + 'static>(handler: H) -> RouterItemBuilder {
149 let mut filters = filter::all_filter();
150 filters.and(filter::$method_name());
151 RouterItemBuilder { filters, handler: Box::new(handler) }
152 }
153 };
154}
155
156macro_rules! method_router_filter {
157 ($method:ident, $inner_method:ident) => {
158 pub fn $method<F, Args>(f: F) -> RouterItemBuilder
159 where
160 for<'r> F: FnTrait<Args> + 'r,
161 for<'r> Args: FromRequest + 'r,
162 for<'r> F: FnTrait<Args::Output<'r>>,
163 for<'r> <F as FnTrait<Args::Output<'r>>>::Output: Responder,
164 {
165 let handler = handler_fn(f);
166 $inner_method(handler)
167 }
168 };
169}
170
171inner_method_router_filter!(inner_get, get_method);
172inner_method_router_filter!(inner_post, post_method);
173inner_method_router_filter!(inner_put, put_method);
174inner_method_router_filter!(inner_delete, delete_method);
175inner_method_router_filter!(inner_head, head_method);
176inner_method_router_filter!(inner_options, options_method);
177inner_method_router_filter!(inner_connect, connect_method);
178inner_method_router_filter!(inner_patch, patch_method);
179inner_method_router_filter!(inner_trace, trace_method);
180
181method_router_filter!(get, inner_get);
182method_router_filter!(post, inner_post);
183method_router_filter!(put, inner_put);
184method_router_filter!(delete, inner_delete);
185method_router_filter!(head, inner_head);
186method_router_filter!(options, inner_options);
187method_router_filter!(connect, inner_connect);
188method_router_filter!(patch, inner_patch);
189method_router_filter!(trace, inner_trace);
190
191
192#[derive(Debug)]
193pub struct RouterItemBuilder {
194 filters: AllFilter,
195 handler: Box<dyn RequestHandler>,
196}
197
198impl RouterItemBuilder {
199 pub fn with<F: Filter + Send + Sync + 'static>(mut self, filter: F) -> Self {
200 self.filters.and(filter);
201 self
202 }
203
204 fn build(self) -> RouterItem {
205 RouterItem { filter: Box::new(self.filters), handler: self.handler }
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::filter::header;
213 use super::{Router, get, post};
214 use crate::{PathParams, RequestContext};
215 use http::{HeaderValue, Method, Request};
216 use micro_http::protocol::RequestHeader;
217
218 async fn simple_get_1(_method: &Method) -> String {
219 "hello world".into()
220 }
221
222 async fn simple_get_2(_method: &Method) -> String {
223 "hello world".into()
224 }
225
226 fn router() -> Router {
227 Router::builder()
228 .route("/", get(simple_get_1))
229 .route(
230 "/",
231 post(simple_get_1).with(header(
232 http::header::CONTENT_TYPE,
233 HeaderValue::from_str(mime::APPLICATION_WWW_FORM_URLENCODED.as_ref()).unwrap(),
234 )),
235 )
236 .route("/", post(simple_get_1))
237 .route("/2", get(simple_get_2))
238 .build()
239 }
240
241 #[test]
242 fn test_route_get() {
243 let router = router();
244 let route_result = router.at("/");
245
246 assert_eq!(route_result.params.len(), 0);
247
248 let items = route_result.router_items;
249 assert_eq!(items.len(), 3);
250
251 let header: RequestHeader = Request::builder().method(Method::GET).body(()).unwrap().into_parts().0.into();
252 let params = PathParams::empty();
253 let req_ctx = RequestContext::new(&header, ¶ms);
254
255 assert!(items[0].filter.matches(&req_ctx));
256 assert!(!items[1].filter.matches(&req_ctx));
257 assert!(!items[2].filter.matches(&req_ctx));
258 }
259
260 #[test]
261 fn test_route_post() {
262 let router = router();
263 let route_result = router.at("/");
264
265 assert_eq!(route_result.params.len(), 0);
266
267 let items = route_result.router_items;
268 assert_eq!(items.len(), 3);
269
270 let header: RequestHeader = Request::builder().method(Method::POST).body(()).unwrap().into_parts().0.into();
271 let params = PathParams::empty();
272 let req_ctx = RequestContext::new(&header, ¶ms);
273
274 assert!(!items[0].filter.matches(&req_ctx));
275 assert!(!items[1].filter.matches(&req_ctx));
276 assert!(items[2].filter.matches(&req_ctx));
277 }
278
279 #[test]
280 fn test_route_post_with_content_type() {
281 let router = router();
282 let route_result = router.at("/");
283
284 assert_eq!(route_result.params.len(), 0);
285
286 let items = route_result.router_items;
287 assert_eq!(items.len(), 3);
288
289 let header: RequestHeader = Request::builder()
290 .method(Method::POST)
291 .header(http::header::CONTENT_TYPE, "application/x-www-form-urlencoded")
292 .body(())
293 .unwrap()
294 .into_parts()
295 .0
296 .into();
297 let params = PathParams::empty();
298 let req_ctx = RequestContext::new(&header, ¶ms);
299
300 assert!(!items[0].filter.matches(&req_ctx));
301 assert!(items[1].filter.matches(&req_ctx));
302 assert!(items[2].filter.matches(&req_ctx));
303 }
304}