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