4 releases (breaking)
| 0.4.0 | Dec 27, 2025 |
|---|---|
| 0.3.0 | Jul 24, 2024 |
| 0.2.0 | Jan 19, 2024 |
| 0.1.0 | Dec 29, 2023 |
#787 in HTTP server
734 downloads per month
Used in 5 crates
(2 directly)
140KB
3K
SLoC
A fork of matchit using small string type for params lifetime elision.
use xitca_router::Router;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut router = Router::new();
router.insert("/home", "Welcome!")?;
router.insert("/users/{id}", "A User")?;
let matched = router.at("/users/978")?;
assert_eq!(matched.params.get("id"), Some("978"));
assert_eq!(*matched.value, "A User");
Ok(())
}
Parameters
The router supports dynamic route segments. These can either be named or catch-all parameters.
Named parameters like /{id} match anything until the next static segment or the end of the path.
let mut router = Router::new();
router.insert("/users/{id}", 42)?;
let matched = router.at("/users/1")?;
assert_eq!(matched.params.get("id"), Some("1"));
let matched = router.at("/users/23")?;
assert_eq!(matched.params.get("id"), Some("23"));
assert!(router.at("/users").is_err());
Prefixes and suffixes within a segment are also supported. However, there may only be a single named parameter per route segment.
let mut router = Router::new();
router.insert("/images/img-{id}.png", true)?;
let matched = router.at("/images/img-1.png")?;
assert_eq!(matched.params.get("id"), Some("1"));
assert!(router.at("/images/img-1.jpg").is_err());
Catch-all parameters start with a * and match anything until the end of the path. They must always be at the end of the route.
let mut router = Router::new();
router.insert("/{*rest}", true)?;
let matched = router.at("/foo.html")?;
assert_eq!(matched.params.get("rest"), Some("foo.html"));
let matched = router.at("/static/bar.css")?;
assert_eq!(matched.params.get("rest"), Some("static/bar.css"));
// Note that this would lead to an empty parameter value.
assert!(router.at("/").is_err());
Relaxed Catch-all Parameters
Relaxed Catch-all parameters with a single * and match everything after the /(Including / itself). They must always be at the end of the route:
let mut m = Router::new();
m.insert("/{*}", true)?;
assert!(m.at("/")?.value);
assert!(m.at("/foo")?.value);
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 }}.
let mut router = Router::new();
router.insert("/{{hello}}", true)?;
router.insert("/{hello}", true)?;
// Match the static route.
let matched = router.at("/{hello}")?;
assert!(matched.params.is_empty());
// Match the dynamic route.
let matched = router.at("/hello")?;
assert_eq!(matched.params.get("hello"), Some("hello"));
Conflict Rules
Static and dynamic route segments are allowed to overlap. If they do, static segments will be given higher priority:
let mut router = Router::new();
router.insert("/", "Welcome!").unwrap(); // Priority: 1
router.insert("/about", "About Me").unwrap(); // Priority: 1
router.insert("/{*filepath}", "...").unwrap(); // Priority: 2
Formally, a route consists of a list of segments separated by /, with an optional leading and trailing slash: (/)<segment_1>/.../<segment_n>(/).
Given set of routes, their overlapping segments may include, in order of priority:
- Any number of static segments (
/a,/b, ...). - One of the following:
- Any number of route parameters with a suffix (
/{x}a,/{x}b, ...), prioritizing the longest suffix. - Any number of route parameters with a prefix (
/a{x},/b{x}, ...), prioritizing the longest prefix. - A single route parameter with both a prefix and a suffix (
/a{x}b). - One of the following;
- A single standalone parameter (
/{x}). - A single standalone catch-all parameter (
/{*rest}). Note this only applies to the final route segment.
Any other combination of route segments is considered ambiguous, and attempting to insert such a route will result in an error.
The one exception to the above set of rules is that catch-all parameters are always considered to conflict with suffixed route parameters, i.e. that /{*rest}
and /{x}suffix are overlapping. This is due to an implementation detail of the routing tree that may be relaxed in the future.
A fork of matchit
Compare to matchit
- Pros
- clean public types with no lifetime pollution. (easier to pass params around)
- 100% safe Rust. (unsafe code still used through dependencies)
- Cons
- immutable router value.
- potentially slower in micro benchmark.