Skip to content

Commit 2ca3611

Browse files
committed
update documentation
1 parent 5c498aa commit 2ca3611

3 files changed

Lines changed: 124 additions & 141 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ assert_eq!(m.at("/foo.js")?.params.get("p"), Some("foo.js"));
4848
assert_eq!(m.at("/c/bar.css")?.params.get("p"), Some("c/bar.css"));
4949
5050
// note that this will not match
51-
assert_eq!(m.at("/").is_err());
51+
assert!(m.at("/").is_err());
5252
```
5353

5454
The literal characters `{` and `}` may be included in a static route by escaping them with the same character. For example, the `{` character is escaped with `{{` and the `}` character is escaped with `}}`.

src/lib.rs

Lines changed: 117 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,116 @@
1-
//! # `matchit`
2-
//!
3-
//! [![Documentation](https://img.shields.io/badge/docs-0.8.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-
//! Note that named parameters must be followed by a `/` or the end of the route. Dynamic suffixes are not currently supported.
47-
//!
48-
//! ### Catch-all Parameters
49-
//!
50-
//! Catch-all parameters start with `*` and match anything until the end of the path.
51-
//! They must always be at the **end** of the route.
52-
//!
53-
//! ```rust
54-
//! # use matchit::Router;
55-
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
56-
//! let mut m = Router::new();
57-
//! m.insert("/{*p}", true)?;
58-
//!
59-
//! assert_eq!(m.at("/foo.js")?.params.get("p"), Some("foo.js"));
60-
//! assert_eq!(m.at("/c/bar.css")?.params.get("p"), Some("c/bar.css"));
61-
//!
62-
//! // note that this will not match
63-
//! assert!(m.at("/").is_err());
64-
//!
65-
//! # Ok(())
66-
//! # }
67-
//! ```
68-
//!
69-
//! ### Escaping Parameters
70-
//!
71-
//! The literal characters `{` and `}` may be included in a static route by escaping them with the same character.
72-
//! For example, the `{` character is escaped with `{{` and the `}` character is escaped with `}}`.
73-
//!
74-
//! ```rust
75-
//! # use matchit::Router;
76-
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
77-
//! let mut m = Router::new();
78-
//! m.insert("/{{hello}}", true)?;
79-
//! m.insert("/{hello}", true)?;
80-
//!
81-
//! // match the static route
82-
//! assert!(m.at("/{hello}")?.value);
83-
//!
84-
//! // match the dynamic route
85-
//! assert_eq!(m.at("/hello")?.params.get("hello"), Some("hello"));
86-
//! # Ok(())
87-
//! # }
88-
//! ```
89-
//!
90-
//! ## Routing Priority
91-
//!
92-
//! Static and dynamic route segments are allowed to overlap. If they do, static segments will be given higher priority:
93-
//!
94-
//! ```rust
95-
//! # use matchit::Router;
96-
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
97-
//! let mut m = Router::new();
98-
//! m.insert("/", "Welcome!").unwrap() ; // priority: 1
99-
//! m.insert("/about", "About Me").unwrap(); // priority: 1
100-
//! m.insert("/{*filepath}", "...").unwrap(); // priority: 2
101-
//!
102-
//! # Ok(())
103-
//! # }
104-
//! ```
105-
//!
106-
//! ## How does it work?
107-
//!
108-
//! 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:
109-
//!
110-
//! ```text
111-
//! Priority Path Value
112-
//! 9 \ 1
113-
//! 3 ├s None
114-
//! 2 |├earch\ 2
115-
//! 1 |└upport\ 3
116-
//! 2 ├blog\ 4
117-
//! 1 | └{post} None
118-
//! 1 | └\ 5
119-
//! 2 ├about-us\ 6
120-
//! 1 | └team\ 7
121-
//! 1 └contact\ 8
122-
//! ```
123-
//!
124-
//! 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
125-
//! by the number of children with registered values, increasing the chance of choosing the correct branch of the first try.
1+
/*!
2+
A high performance, zero-copy URL router.
3+
4+
```rust
5+
use matchit::Router;
6+
7+
fn main() -> Result<(), Box<dyn std::error::Error>> {
8+
let mut router = Router::new();
9+
router.insert("/home", "Welcome!")?;
10+
router.insert("/users/{id}", "A User")?;
11+
12+
let matched = router.at("/users/978")?;
13+
assert_eq!(matched.params.get("id"), Some("978"));
14+
assert_eq!(*matched.value, "A User");
15+
16+
Ok(())
17+
}
18+
```
19+
20+
# Parameters
21+
22+
The router supports dynamic route segments. These can either be named or catch-all parameters.
23+
24+
Named parameters like `/{id}` match anything until the next `/` or the end of the path. Note that named parameters must be followed
25+
by a `/` or the end of the route. Dynamic suffixes are not currently supported.
26+
27+
```rust
28+
# use matchit::Router;
29+
# fn main() -> Result<(), Box<dyn std::error::Error>> {
30+
let mut m = Router::new();
31+
m.insert("/users/{id}", true)?;
32+
33+
assert_eq!(m.at("/users/1")?.params.get("id"), Some("1"));
34+
assert_eq!(m.at("/users/23")?.params.get("id"), Some("23"));
35+
assert!(m.at("/users").is_err());
36+
# Ok(())
37+
# }
38+
```
39+
40+
Catch-all parameters start with `*` and match anything until the end of the path. They must always be at the **end** of the route.
41+
42+
```rust
43+
# use matchit::Router;
44+
# fn main() -> Result<(), Box<dyn std::error::Error>> {
45+
let mut m = Router::new();
46+
m.insert("/{*p}", true)?;
47+
48+
assert_eq!(m.at("/foo.js")?.params.get("p"), Some("foo.js"));
49+
assert_eq!(m.at("/c/bar.css")?.params.get("p"), Some("c/bar.css"));
50+
51+
// note that this will not match
52+
assert!(m.at("/").is_err());
53+
# Ok(())
54+
# }
55+
```
56+
57+
The literal characters `{` and `}` may be included in a static route by escaping them with the same character. For example, the `{` character is escaped with `{{` and the `}` character is escaped with `}}`.
58+
59+
```rust
60+
# use matchit::Router;
61+
# fn main() -> Result<(), Box<dyn std::error::Error>> {
62+
let mut m = Router::new();
63+
m.insert("/{{hello}}", true)?;
64+
m.insert("/{hello}", true)?;
65+
66+
// match the static route
67+
assert!(m.at("/{hello}")?.value);
68+
69+
// match the dynamic route
70+
assert_eq!(m.at("/hello")?.params.get("hello"), Some("hello"));
71+
# Ok(())
72+
# }
73+
```
74+
75+
# Routing Priority
76+
77+
Static and dynamic route segments are allowed to overlap. If they do, static segments will be given higher priority:
78+
79+
```rust
80+
# use matchit::Router;
81+
# fn main() -> Result<(), Box<dyn std::error::Error>> {
82+
let mut m = Router::new();
83+
m.insert("/", "Welcome!").unwrap(); // priority: 1
84+
m.insert("/about", "About Me").unwrap(); // priority: 1
85+
m.insert("/{*filepath}", "...").unwrap(); // priority: 2
86+
# Ok(())
87+
# }
88+
```
89+
90+
# How does it work?
91+
92+
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.
93+
94+
```text
95+
Priority Path Value
96+
9 \ 1
97+
3 ├s None
98+
2 |├earch\ 2
99+
1 |└upport\ 3
100+
2 ├blog\ 4
101+
1 | └{post} None
102+
1 | └\ 5
103+
2 ├about-us\ 6
104+
1 | └team\ 7
105+
1 └contact\ 8
106+
```
107+
108+
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
109+
by the number of children with registered values, increasing the chance of choosing the correct branch of the first try.
110+
111+
As it turns out, this method of routing is extremely fast. See the [benchmark results](https://github.com/ibraheemdev/matchit?tab=readme-ov-file#benchmarks) for details.
112+
*/
113+
126114
#![deny(rust_2018_idioms, clippy::all)]
127115

128116
mod error;
@@ -136,13 +124,8 @@ pub use params::{Params, ParamsIter};
136124
pub use router::{Match, Router};
137125

138126
#[cfg(doctest)]
139-
mod test_readme {
140-
macro_rules! doc_comment {
141-
($x:expr) => {
142-
#[doc = $x]
143-
extern "C" {}
144-
};
145-
}
146-
147-
doc_comment!(include_str!("../README.md"));
127+
mod readme {
128+
#[allow(dead_code)]
129+
#[doc = include_str!("../README.md")]
130+
struct Readme;
148131
}

src/router.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::tree::Node;
22
use crate::{InsertError, MatchError, Params};
33

4-
/// A URL router.
4+
/// A zero-copy URL router.
55
///
66
/// See [the crate documentation](crate) for details.
77
#[derive(Clone, Debug)]
@@ -55,7 +55,7 @@ impl<T> Router<T> {
5555
/// # Ok(())
5656
/// # }
5757
/// ```
58-
pub fn at<'m, 'p>(&'m self, path: &'p str) -> Result<Match<'m, 'p, &'m T>, MatchError> {
58+
pub fn at<'path>(&self, path: &'path str) -> Result<Match<'_, 'path, &T>, MatchError> {
5959
match self.root.at(path.as_bytes()) {
6060
Ok((value, params)) => Ok(Match {
6161
// Safety: We only expose `&mut T` through `&mut self`
@@ -82,10 +82,10 @@ impl<T> Router<T> {
8282
/// # Ok(())
8383
/// # }
8484
/// ```
85-
pub fn at_mut<'m, 'p>(
86-
&'m mut self,
87-
path: &'p str,
88-
) -> Result<Match<'m, 'p, &'m mut T>, MatchError> {
85+
pub fn at_mut<'path>(
86+
&mut self,
87+
path: &'path str,
88+
) -> Result<Match<'_, 'path, &mut T>, MatchError> {
8989
match self.root.at(path.as_bytes()) {
9090
Ok((value, params)) => Ok(Match {
9191
// Safety: We have `&mut self`

0 commit comments

Comments
 (0)