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