A speedy, flexible router for Rust.
Real-world projects often need advanced routing: inline parameters, mid-route wildcards, or compatibility with frameworks like Ruby on Rails and specifications like the OCI Distribution Specification.
wayfind aims to be competitive with the fastest routers while supporting these features. Unused features don't impact performance.
[dependencies]
wayfind = "0.9"use core::error::Error;
use wayfind::Router;
fn main() -> Result<(), Box<dyn Error>> {
let mut router = Router::new();
// Static
router.insert("/", 1)?;
router.insert("/health", 2)?;
{
let search = router.search("/").unwrap();
assert_eq!(search.data, &1);
assert_eq!(search.template, "/");
let search = router.search("/health").unwrap();
assert_eq!(search.data, &2);
assert_eq!(search.template, "/health");
let search = router.search("/heal");
assert_eq!(search, None);
}
// Dynamic
router.insert("/users/<id>", 3)?;
router.insert("/users/<id>/message", 4)?;
{
let search = router.search("/users/123").unwrap();
assert_eq!(search.data, &3);
assert_eq!(search.template, "/users/<id>");
assert_eq!(search.parameters[0], ("id", "123"));
let search = router.search("/users/123/message").unwrap();
assert_eq!(search.data, &4);
assert_eq!(search.template, "/users/<id>/message");
assert_eq!(search.parameters[0], ("id", "123"));
let search = router.search("/users/");
assert_eq!(search, None);
}
// Dynamic Inline
router.insert("/images/<name>.png", 5)?;
{
let search = router.search("/images/avatar.final.png").unwrap();
assert_eq!(search.data, &5);
assert_eq!(search.template, "/images/<name>.png");
assert_eq!(search.parameters[0], ("name", "avatar.final"));
let search = router.search("/images/.png");
assert_eq!(search, None);
}
// Wildcard
router.insert("/files/<*path>", 6)?;
router.insert("/files/<*path>/delete", 7)?;
{
let search = router.search("/files/documents").unwrap();
assert_eq!(search.data, &6);
assert_eq!(search.template, "/files/<*path>");
assert_eq!(search.parameters[0], ("path", "documents"));
let search = router.search("/files/documents/my-project/delete").unwrap();
assert_eq!(search.data, &7);
assert_eq!(search.template, "/files/<*path>/delete");
assert_eq!(search.parameters[0], ("path", "documents/my-project"));
let search = router.search("/files");
assert_eq!(search, None);
}
// Wildcard Inline
router.insert("/backups/<*path>.tar.gz", 8)?;
{
let search = router.search("/backups/production/database.tar.gz").unwrap();
assert_eq!(search.data, &8);
assert_eq!(search.template, "/backups/<*path>.tar.gz");
assert_eq!(search.parameters[0], ("path", "production/database"));
let search = router.search("/backups/.tar.gz");
assert_eq!(search, None);
}
println!("{router}");
Ok(())
}/
├─ backups/
│ ╰─ <*path>
│ ╰─ .tar.gz
├─ files/
│ ├─ <*path>
│ │ ╰─ /delete
│ ╰─ <*path>
├─ health
├─ images/
│ ╰─ <name>
│ ╰─ .png
╰─ users/
╰─ <id>
╰─ /message
wayfind stores routes in a compressed radix trie.
When searching, each node tries its children in priority order:
- static
- dynamic
- wildcard
All parameters are greedy, consuming as much of the path as possible.
There is no backtracking across priority levels. This can result in some matches which may be unexpected.
In the following router:
/api/
├─ <version>
│ ╰─ /
│ ╰─ <*rest>
╰─ <*path>
╰─ /help
The path /api/docs/help would match the first route, not the second.
Even though the second is arguably more specific.
wayfind is competitive with the fastest Rust routers across all benchmarks we run.
For all benchmarks, we convert any extracted parameters to strings.
All routers provide a way to return parameters as strings, but some delay the actual UTF-8 decoding until post-search.
| Library | Percent Decoding | String Parameters |
|---|---|---|
| wayfind | no | yes |
| actix-router | partial | yes |
| matchit | no | delayed |
| ntex-router | partial | yes |
| path-tree | no | delayed |
| route-recognizer | no | yes |
| xitca-router | no | yes |
As such, we provide 2 sets of results per benchmark:
- one with the default behaviour of the router.
- one with the parameters extracted to
Vec<(&str, &str)>.
See the results at: https://codspeed.io/DuskSystems/wayfind/benchmarks
wayfind is licensed under the terms of both the MIT License and the Apache License (Version 2.0).