matchit/lib.rs
1//! # `matchit`
2//!
3//! [](https://docs.rs/matchit)
4//! [](https://crates.io/crates/matchit)
5//! [](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
104mod error;
105mod params;
106mod router;
107mod tree;
108
109pub use error::{InsertError, MatchError};
110pub use params::{Params, ParamsIter};
111pub use router::{Match, Router};
112
113#[cfg(doctest)]
114mod 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}