1 //! # `matchit`
2 //!
3 //! [![Documentation](https://img.shields.io/badge/docs-0.7.3-4d76ae?style=for-the-badge)](https://docs.rs/matchit)
4 //! [![Version](https://img.shields.io/crates/v/matchit?style=for-the-badge)](https://crates.io/crates/matchit)
5 //! [![License](https://img.shields.io/crates/l/matchit?style=for-the-badge)](https://crates.io/crates/matchit)
6 //!
7 //! A blazing fast URL router.
8 //!
9 //! ```rust
10 //! use matchit::Router;
11 //!
12 //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
13 //! let mut router = Router::new();
14 //! router.insert("/home", "Welcome!")?;
15 //! router.insert("/users/:id", "A User")?;
16 //!
17 //! let matched = router.at("/users/978")?;
18 //! assert_eq!(matched.params.get("id"), Some("978"));
19 //! assert_eq!(*matched.value, "A User");
20 //! # Ok(())
21 //! # }
22 //! ```
23 //!
24 //! ## Parameters
25 //!
26 //! Along with static routes, the router also supports dynamic route segments. These can either be named or catch-all parameters:
27 //!
28 //! ### Named Parameters
29 //!
30 //! Named parameters like `/:id` match anything until the next `/` or the end of the path:
31 //!
32 //! ```rust
33 //! # use matchit::Router;
34 //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
35 //! let mut m = Router::new();
36 //! m.insert("/users/:id", true)?;
37 //!
38 //! assert_eq!(m.at("/users/1")?.params.get("id"), Some("1"));
39 //! assert_eq!(m.at("/users/23")?.params.get("id"), Some("23"));
40 //! assert!(m.at("/users").is_err());
41 //!
42 //! # Ok(())
43 //! # }
44 //! ```
45 //!
46 //! ### Catch-all Parameters
47 //!
48 //! Catch-all parameters start with `*` and match everything after the `/`. They must always be at the **end** of the route:
49 //!
50 //! ```rust
51 //! # use matchit::Router;
52 //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
53 //! let mut m = Router::new();
54 //! m.insert("/*p", true)?;
55 //!
56 //! assert_eq!(m.at("/foo.js")?.params.get("p"), Some("foo.js"));
57 //! assert_eq!(m.at("/c/bar.css")?.params.get("p"), Some("c/bar.css"));
58 //!
59 //! // note that this would not match
60 //! assert!(m.at("/").is_err());
61 //!
62 //! # Ok(())
63 //! # }
64 //! ```
65 //!
66 //! ## Routing Priority
67 //!
68 //! Static and dynamic route segments are allowed to overlap. If they do, static segments will be given higher priority:
69 //!
70 //! ```rust
71 //! # use matchit::Router;
72 //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
73 //! let mut m = Router::new();
74 //! m.insert("/", "Welcome!").unwrap()    ;  // priority: 1
75 //! m.insert("/about", "About Me").unwrap(); // priority: 1
76 //! m.insert("/*filepath", "...").unwrap();  // priority: 2
77 //!
78 //! # Ok(())
79 //! # }
80 //! ```
81 //!
82 //! ## How does it work?
83 //!
84 //! The router takes advantage of the fact that URL routes generally follow a hierarchical structure. Routes are stored them in a radix trie that makes heavy use of common prefixes:
85 //!
86 //! ```text
87 //! Priority   Path             Value
88 //! 9          \                1
89 //! 3          ├s               None
90 //! 2          |├earch\         2
91 //! 1          |└upport\        3
92 //! 2          ├blog\           4
93 //! 1          |    └:post      None
94 //! 1          |         └\     5
95 //! 2          ├about-us\       6
96 //! 1          |        └team\  7
97 //! 1          └contact\        8
98 //! ```
99 //!
100 //! This allows us to reduce the route search to a small number of branches. Child nodes on the same level of the tree are also prioritized
101 //! by the number of children with registered values, increasing the chance of choosing the correct branch of the first try.
102 #![deny(rust_2018_idioms, clippy::all)]
103 
104 mod error;
105 mod params;
106 mod router;
107 mod tree;
108 
109 pub use error::{InsertError, MatchError};
110 pub use params::{Params, ParamsIter};
111 pub use router::{Match, Router};
112 
113 #[cfg(doctest)]
114 mod test_readme {
115     macro_rules! doc_comment {
116         ($x:expr) => {
117             #[doc = $x]
118             extern "C" {}
119         };
120     }
121 
122     doc_comment!(include_str!("../README.md"));
123 }
124