1use std::fmt::{self, Debug, Formatter};
2use std::sync::Arc;
3use std::sync::atomic::{AtomicUsize, Ordering};
4
5use super::filters::{self, FnFilter, PathFilter};
6use super::{DetectMatched, Filter, PathState};
7use crate::handler::{Handler, WhenHoop};
8use crate::http::uri::Scheme;
9use crate::{Depot, Request};
10
11#[non_exhaustive]
15pub struct Router {
16 #[doc(hidden)]
17 pub id: usize,
18 pub routers: Vec<Self>,
20 pub filters: Vec<Box<dyn Filter>>,
22 pub hoops: Vec<Arc<dyn Handler>>,
24 pub goal: Option<Arc<dyn Handler>>,
26}
27
28impl Default for Router {
29 #[inline]
30 fn default() -> Self {
31 Self::new()
32 }
33}
34static NEXT_ROUTER_ID: AtomicUsize = AtomicUsize::new(1);
35
36impl Router {
37 #[inline]
39 pub fn new() -> Self {
40 Self {
41 id: NEXT_ROUTER_ID.fetch_add(1, Ordering::Relaxed),
42 routers: Vec::new(),
43 filters: Vec::new(),
44 hoops: Vec::new(),
45 goal: None,
46 }
47 }
48
49 #[inline]
51 #[must_use]
52 pub fn routers(&self) -> &Vec<Self> {
53 &self.routers
54 }
55 #[inline]
57 pub fn routers_mut(&mut self) -> &mut Vec<Self> {
58 &mut self.routers
59 }
60
61 #[inline]
63 #[must_use]
64 pub fn hoops(&self) -> &Vec<Arc<dyn Handler>> {
65 &self.hoops
66 }
67 #[inline]
69 pub fn hoops_mut(&mut self) -> &mut Vec<Arc<dyn Handler>> {
70 &mut self.hoops
71 }
72
73 #[inline]
75 #[must_use]
76 pub fn filters(&self) -> &Vec<Box<dyn Filter>> {
77 &self.filters
78 }
79 #[inline]
81 pub fn filters_mut(&mut self) -> &mut Vec<Box<dyn Filter>> {
82 &mut self.filters
83 }
84
85 pub async fn detect(
87 &self,
88 req: &mut Request,
89 path_state: &mut PathState,
90 ) -> Option<DetectMatched> {
91 Box::pin(async move {
92 for filter in &self.filters {
93 if !filter.filter(req, path_state).await {
94 return None;
95 }
96 }
97 if !self.routers.is_empty() {
98 let original_cursor = path_state.cursor;
99 #[cfg(feature = "matched-path")]
100 let original_matched_parts_len = path_state.matched_parts.len();
101 for child in &self.routers {
102 if let Some(dm) = child.detect(req, path_state).await {
103 return Some(DetectMatched {
104 hoops: [&self.hoops[..], &dm.hoops[..]].concat(),
105 goal: dm.goal.clone(),
106 });
107 } else {
108 #[cfg(feature = "matched-path")]
109 path_state
110 .matched_parts
111 .truncate(original_matched_parts_len);
112 path_state.cursor = original_cursor;
113 }
114 }
115 }
116 if path_state.is_ended() {
117 path_state.once_ended = true;
118 if let Some(goal) = &self.goal {
119 return Some(DetectMatched {
120 hoops: self.hoops.clone(),
121 goal: goal.clone(),
122 });
123 }
124 }
125 None
126 })
127 .await
128 }
129
130 #[inline]
132 #[must_use]
133 pub fn unshift(mut self, router: Self) -> Self {
134 self.routers.insert(0, router);
135 self
136 }
137 #[inline]
139 #[must_use]
140 pub fn insert(mut self, index: usize, router: Self) -> Self {
141 self.routers.insert(index, router);
142 self
143 }
144
145 #[inline]
147 #[must_use]
148 pub fn push(mut self, router: Self) -> Self {
149 self.routers.push(router);
150 self
151 }
152 #[inline]
154 #[must_use]
155 pub fn append(mut self, others: &mut Vec<Self>) -> Self {
156 self.routers.append(others);
157 self
158 }
159
160 #[inline]
163 #[must_use]
164 pub fn with_hoop<H: Handler>(hoop: H) -> Self {
165 Self::new().hoop(hoop)
166 }
167
168 #[inline]
171 #[must_use]
172 pub fn with_hoop_when<H, F>(hoop: H, filter: F) -> Self
173 where
174 H: Handler,
175 F: Fn(&Request, &Depot) -> bool + Send + Sync + 'static,
176 {
177 Self::new().hoop_when(hoop, filter)
178 }
179
180 #[inline]
183 #[must_use]
184 pub fn hoop<H: Handler>(mut self, hoop: H) -> Self {
185 self.hoops.push(Arc::new(hoop));
186 self
187 }
188
189 #[inline]
192 #[must_use]
193 pub fn hoop_when<H, F>(mut self, hoop: H, filter: F) -> Self
194 where
195 H: Handler,
196 F: Fn(&Request, &Depot) -> bool + Send + Sync + 'static,
197 {
198 self.hoops.push(Arc::new(WhenHoop {
199 inner: hoop,
200 filter,
201 }));
202 self
203 }
204
205 #[inline]
211 #[must_use]
212 pub fn with_path(path: impl Into<String>) -> Self {
213 Self::with_filter(PathFilter::new(path))
214 }
215
216 #[inline]
222 #[must_use]
223 pub fn path(self, path: impl Into<String>) -> Self {
224 self.filter(PathFilter::new(path))
225 }
226
227 #[inline]
229 #[must_use]
230 pub fn with_filter(filter: impl Filter + Sized) -> Self {
231 Self::new().filter(filter)
232 }
233 #[inline]
235 #[must_use]
236 pub fn filter(mut self, filter: impl Filter + Sized) -> Self {
237 self.filters.push(Box::new(filter));
238 self
239 }
240
241 #[inline]
243 #[must_use]
244 pub fn with_filter_fn<T>(func: T) -> Self
245 where
246 T: Fn(&mut Request, &mut PathState) -> bool + Send + Sync + 'static,
247 {
248 Self::with_filter(FnFilter(func))
249 }
250 #[inline]
252 #[must_use]
253 pub fn filter_fn<T>(self, func: T) -> Self
254 where
255 T: Fn(&mut Request, &mut PathState) -> bool + Send + Sync + 'static,
256 {
257 self.filter(FnFilter(func))
258 }
259
260 #[inline]
262 #[must_use]
263 pub fn goal<H: Handler>(mut self, goal: H) -> Self {
264 self.goal = Some(Arc::new(goal));
265 self
266 }
267
268 #[inline]
271 #[must_use]
272 pub fn then<F>(self, func: F) -> Self
273 where
274 F: FnOnce(Self) -> Self,
275 {
276 func(self)
277 }
278
279 #[inline]
283 #[must_use]
284 pub fn scheme(self, scheme: Scheme) -> Self {
285 self.filter(filters::scheme(scheme))
286 }
287
288 #[inline]
292 #[must_use]
293 pub fn host(self, host: impl Into<String>) -> Self {
294 self.filter(filters::host(host))
295 }
296
297 #[inline]
301 #[must_use]
302 pub fn with_host(host: impl Into<String>) -> Self {
303 Self::with_filter(filters::host(host))
304 }
305
306 #[inline]
310 #[must_use]
311 pub fn port(self, port: u16) -> Self {
312 self.filter(filters::port(port))
313 }
314
315 #[inline]
319 #[must_use]
320 pub fn with_port(port: u16) -> Self {
321 Self::with_filter(filters::port(port))
322 }
323
324 #[inline]
328 #[must_use]
329 pub fn get<H: Handler>(self, goal: H) -> Self {
330 self.push(Self::with_filter(filters::get()).goal(goal))
331 }
332
333 #[inline]
337 #[must_use]
338 pub fn post<H: Handler>(self, goal: H) -> Self {
339 self.push(Self::with_filter(filters::post()).goal(goal))
340 }
341
342 #[inline]
346 #[must_use]
347 pub fn put<H: Handler>(self, goal: H) -> Self {
348 self.push(Self::with_filter(filters::put()).goal(goal))
349 }
350
351 #[inline]
355 #[must_use]
356 pub fn delete<H: Handler>(self, goal: H) -> Self {
357 self.push(Self::with_filter(filters::delete()).goal(goal))
358 }
359
360 #[inline]
364 #[must_use]
365 pub fn patch<H: Handler>(self, goal: H) -> Self {
366 self.push(Self::with_filter(filters::patch()).goal(goal))
367 }
368
369 #[inline]
373 #[must_use]
374 pub fn head<H: Handler>(self, goal: H) -> Self {
375 self.push(Self::with_filter(filters::head()).goal(goal))
376 }
377
378 #[inline]
382 #[must_use]
383 pub fn options<H: Handler>(self, goal: H) -> Self {
384 self.push(Self::with_filter(filters::options()).goal(goal))
385 }
386}
387
388const SYMBOL_DOWN: &str = "│";
389const SYMBOL_TEE: &str = "├";
390const SYMBOL_ELL: &str = "└";
391const SYMBOL_RIGHT: &str = "─";
392impl Debug for Router {
393 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
394 fn print(f: &mut Formatter, prefix: &str, last: bool, router: &Router) -> fmt::Result {
395 let mut path = "".to_owned();
396 let mut others = Vec::with_capacity(router.filters.len());
397 if router.filters.is_empty() {
398 "!NULL!".clone_into(&mut path);
399 } else {
400 for filter in &router.filters {
401 let info = format!("{filter:?}");
402 if info.starts_with("path:") {
403 info.split_once(':')
404 .expect("`split_once` get `None`")
405 .1
406 .clone_into(&mut path)
407 } else {
408 let mut parts = info.splitn(2, ':').collect::<Vec<_>>();
409 if !parts.is_empty() {
410 others.push(parts.pop().expect("part should exists.").to_owned());
411 }
412 }
413 }
414 }
415 let cp = if last {
416 format!("{prefix}{SYMBOL_ELL}{SYMBOL_RIGHT}{SYMBOL_RIGHT}")
417 } else {
418 format!("{prefix}{SYMBOL_TEE}{SYMBOL_RIGHT}{SYMBOL_RIGHT}")
419 };
420 let hd = router
421 .goal
422 .as_ref()
423 .map(|goal| format!(" -> {}", goal.type_name()))
424 .unwrap_or_default();
425 if !others.is_empty() {
426 writeln!(f, "{cp}{path}[{}]{hd}", others.join(","))?;
427 } else {
428 writeln!(f, "{cp}{path}{hd}")?;
429 }
430 let routers = router.routers();
431 if !routers.is_empty() {
432 let np = if last {
433 format!("{prefix} ")
434 } else {
435 format!("{prefix}{SYMBOL_DOWN} ")
436 };
437 for (i, router) in routers.iter().enumerate() {
438 print(f, &np, i == routers.len() - 1, router)?;
439 }
440 }
441 Ok(())
442 }
443 print(f, "", true, self)
444 }
445}
446
447#[cfg(test)]
448mod tests {
449 use super::{PathState, Router};
450 use crate::Response;
451 use crate::handler;
452 use crate::test::TestClient;
453
454 #[handler]
455 async fn fake_handler(_res: &mut Response) {}
456 #[test]
457 fn test_router_debug() {
458 let router = Router::default()
459 .push(
460 Router::with_path("users")
461 .push(
462 Router::with_path("{id}")
463 .push(Router::with_path("emails").get(fake_handler)),
464 )
465 .push(
466 Router::with_path("{id}/articles/{aid}")
467 .get(fake_handler)
468 .delete(fake_handler),
469 ),
470 )
471 .push(
472 Router::with_path("articles")
473 .push(
474 Router::with_path("{id}/authors/{aid}")
475 .get(fake_handler)
476 .delete(fake_handler),
477 )
478 .push(
479 Router::with_path("{id}")
480 .get(fake_handler)
481 .delete(fake_handler),
482 ),
483 );
484 assert_eq!(
485 format!("{router:?}"),
486 r#"└──!NULL!
487 ├──users
488 │ ├──{id}
489 │ │ └──emails
490 │ │ └──[GET] -> salvo_core::routing::router::tests::fake_handler
491 │ └──{id}/articles/{aid}
492 │ ├──[GET] -> salvo_core::routing::router::tests::fake_handler
493 │ └──[DELETE] -> salvo_core::routing::router::tests::fake_handler
494 └──articles
495 ├──{id}/authors/{aid}
496 │ ├──[GET] -> salvo_core::routing::router::tests::fake_handler
497 │ └──[DELETE] -> salvo_core::routing::router::tests::fake_handler
498 └──{id}
499 ├──[GET] -> salvo_core::routing::router::tests::fake_handler
500 └──[DELETE] -> salvo_core::routing::router::tests::fake_handler
501"#
502 );
503 }
504 #[tokio::test]
505 async fn test_router_detect1() {
506 let router =
507 Router::default().push(Router::with_path("users").push(
508 Router::with_path("{id}").push(Router::with_path("emails").get(fake_handler)),
509 ));
510 let mut req = TestClient::get("http://local.host/users/12/emails").build();
511 let mut path_state = PathState::new(req.uri().path());
512 let matched = router.detect(&mut req, &mut path_state).await;
513 assert!(matched.is_some());
514 }
515 #[tokio::test]
516 async fn test_router_detect2() {
517 let router = Router::new()
518 .push(Router::with_path("users").push(Router::with_path("{id}").get(fake_handler)))
519 .push(Router::with_path("users").push(
520 Router::with_path("{id}").push(Router::with_path("emails").get(fake_handler)),
521 ));
522 let mut req = TestClient::get("http://local.host/users/12/emails").build();
523 let mut path_state = PathState::new(req.uri().path());
524 let matched = router.detect(&mut req, &mut path_state).await;
525 assert!(matched.is_some());
526 }
527 #[tokio::test]
528 async fn test_router_detect3() {
529 let router = Router::new().push(
530 Router::with_path("users").push(
531 Router::with_path(r"{id|\d+}").push(
532 Router::new()
533 .push(Router::with_path("facebook/insights/{**rest}").goal(fake_handler)),
534 ),
535 ),
536 );
537 let mut req = TestClient::get("http://local.host/users/12/facebook/insights").build();
538 let mut path_state = PathState::new(req.uri().path());
539 let matched = router.detect(&mut req, &mut path_state).await;
540 assert!(matched.is_some());
541
542 let mut req = TestClient::get("http://local.host/users/12/facebook/insights/23").build();
543 let mut path_state = PathState::new(req.uri().path());
544 let matched = router.detect(&mut req, &mut path_state).await;
545 assert!(matched.is_some());
547 }
548 #[tokio::test]
549 async fn test_router_detect4() {
550 let router = Router::new().push(
551 Router::with_path("users").push(
552 Router::with_path(r"{id|\d+}").push(
553 Router::new()
554 .push(Router::with_path("facebook/insights/{*+rest}").goal(fake_handler)),
555 ),
556 ),
557 );
558 let mut req = TestClient::get("http://local.host/users/12/facebook/insights").build();
559 let mut path_state = PathState::new(req.uri().path());
560 let matched = router.detect(&mut req, &mut path_state).await;
561 assert!(matched.is_none());
563
564 let mut req = TestClient::get("http://local.host/users/12/facebook/insights/23").build();
565 let mut path_state = PathState::new(req.uri().path());
566 let matched = router.detect(&mut req, &mut path_state).await;
567 assert!(matched.is_some());
568 }
569 #[tokio::test]
570 async fn test_router_detect5() {
571 let router = Router::new().push(
572 Router::with_path("users").push(
573 Router::with_path(r"{id|\d+}").push(
574 Router::new().push(
575 Router::with_path("facebook/insights")
576 .push(Router::with_path("{**rest}").goal(fake_handler)),
577 ),
578 ),
579 ),
580 );
581 let mut req = TestClient::get("http://local.host/users/12/facebook/insights").build();
582 let mut path_state = PathState::new(req.uri().path());
583 let matched = router.detect(&mut req, &mut path_state).await;
584 assert!(matched.is_some());
585
586 let mut req = TestClient::get("http://local.host/users/12/facebook/insights/23").build();
587 let mut path_state = PathState::new(req.uri().path());
588 let matched = router.detect(&mut req, &mut path_state).await;
589 assert!(matched.is_some());
590 assert_eq!(path_state.params["id"], "12");
591 }
592 #[tokio::test]
593 async fn test_router_detect6() {
594 let router = Router::new().push(
595 Router::with_path("users").push(
596 Router::with_path(r"{id|\d+}").push(
597 Router::new().push(
598 Router::with_path("facebook/insights")
599 .push(Router::new().path("{*+rest}").goal(fake_handler)),
600 ),
601 ),
602 ),
603 );
604 let mut req = TestClient::get("http://local.host/users/12/facebook/insights").build();
605 let mut path_state = PathState::new(req.uri().path());
606 let matched = router.detect(&mut req, &mut path_state).await;
607 assert!(matched.is_none());
608
609 let mut req = TestClient::get("http://local.host/users/12/facebook/insights/23").build();
610 let mut path_state = PathState::new(req.uri().path());
611 let matched = router.detect(&mut req, &mut path_state).await;
612 assert!(matched.is_some());
613 }
614 #[tokio::test]
615 async fn test_router_detect_utf8() {
616 let router = Router::new().push(
617 Router::with_path("用户").push(
618 Router::with_path(r"{id|\d+}").push(
619 Router::new().push(
620 Router::with_path("facebook/insights")
621 .push(Router::with_path("{*+rest}").goal(fake_handler)),
622 ),
623 ),
624 ),
625 );
626 let mut req =
627 TestClient::get("http://local.host/%E7%94%A8%E6%88%B7/12/facebook/insights").build();
628 let mut path_state = PathState::new(req.uri().path());
629 let matched = router.detect(&mut req, &mut path_state).await;
630 assert!(matched.is_none());
631
632 let mut req =
633 TestClient::get("http://local.host/%E7%94%A8%E6%88%B7/12/facebook/insights/23").build();
634 let mut path_state = PathState::new(req.uri().path());
635 let matched = router.detect(&mut req, &mut path_state).await;
636 assert!(matched.is_some());
637 }
638 #[tokio::test]
639 async fn test_router_detect9() {
640 let router = Router::new()
641 .push(Router::with_path("users/{sub|(images|css)}/{filename}").goal(fake_handler));
642 let mut req = TestClient::get("http://local.host/users/12/m.jpg").build();
643 let mut path_state = PathState::new(req.uri().path());
644 let matched = router.detect(&mut req, &mut path_state).await;
645 assert!(matched.is_none());
646
647 let mut req = TestClient::get("http://local.host/users/css/m.jpg").build();
648 let mut path_state = PathState::new(req.uri().path());
649 let matched = router.detect(&mut req, &mut path_state).await;
650 assert!(matched.is_some());
651 }
652 #[tokio::test]
653 async fn test_router_detect10() {
654 let router = Router::new()
655 .push(Router::with_path(r"users/{*sub|(images|css)/.+}").goal(fake_handler));
656 let mut req = TestClient::get("http://local.host/users/12/m.jpg").build();
657 let mut path_state = PathState::new(req.uri().path());
658 let matched = router.detect(&mut req, &mut path_state).await;
659 assert!(matched.is_none());
660
661 let mut req = TestClient::get("http://local.host/users/css/abc/m.jpg").build();
662 let mut path_state = PathState::new(req.uri().path());
663 let matched = router.detect(&mut req, &mut path_state).await;
664 assert!(matched.is_some());
665 }
666 #[tokio::test]
667 async fn test_router_detect11() {
668 let router = Router::new()
669 .push(Router::with_path(r"avatars/{width|\d+}x{height|\d+}.{ext}").goal(fake_handler));
670 let mut req = TestClient::get("http://local.host/avatars/321x641f.webp").build();
671 let mut path_state = PathState::new(req.uri().path());
672 let matched = router.detect(&mut req, &mut path_state).await;
673 assert!(matched.is_none());
674
675 let mut req = TestClient::get("http://local.host/avatars/320x640.webp").build();
676 let mut path_state = PathState::new(req.uri().path());
677 let matched = router.detect(&mut req, &mut path_state).await;
678 assert!(matched.is_some());
679 }
680 #[tokio::test]
681 async fn test_router_detect12() {
682 let router = Router::new()
683 .push(Router::with_path("/.well-known/acme-challenge/{token}").goal(fake_handler));
684
685 let mut req =
686 TestClient::get("http://local.host/.well-known/acme-challenge/q1XXrxIx79uXNl3I")
687 .build();
688 let mut path_state = PathState::new(req.uri().path());
689 let matched = router.detect(&mut req, &mut path_state).await;
690 assert!(matched.is_some());
691 }
692
693 #[tokio::test]
694 async fn test_router_detect13() {
695 let router = Router::new()
696 .path("user/{id|[0-9a-z]{8}(-[0-9a-z]{4}){3}-[0-9a-z]{12}}")
697 .get(fake_handler);
698 let mut req =
699 TestClient::get("http://local.host/user/726d694c-7af0-4bb0-9d22-706f7e38641e").build();
700 let mut path_state = PathState::new(req.uri().path());
701 let matched = router.detect(&mut req, &mut path_state).await;
702 assert!(matched.is_some());
703 let mut req =
704 TestClient::get("http://local.host/user/726d694c-7af0-4bb0-9d22-706f7e386e").build();
705 let mut path_state = PathState::new(req.uri().path());
706 let matched = router.detect(&mut req, &mut path_state).await;
707 assert!(matched.is_none());
708 }
709
710 #[tokio::test]
711 async fn test_router_detect_path_encoded() {
712 let router = Router::new().path("api/{p}").get(fake_handler);
713 let mut req = TestClient::get("http://127.0.0.1:6060/api/a%2fb%2fc").build();
714 let mut path_state = PathState::new(req.uri().path());
715 let matched = router.detect(&mut req, &mut path_state).await;
716 assert!(matched.is_some());
717 assert_eq!(path_state.params["p"], "a/b/c");
718 }
719}