Crates.io | wayfind |
lib.rs | wayfind |
version | 0.9.0 |
created_at | 2024-07-31 13:50:27.370374+00 |
updated_at | 2025-08-27 17:38:48.223661+00 |
description | A speedy, flexible router. |
homepage | |
repository | https://github.com/DuskSystems/wayfind |
max_upload_size | |
id | 1320945 |
size | 147,060 |
wayfind
A speedy, flexible router for Rust.
wayfind
attempts to bridge the gap between existing Rust router options:
Real-world projects often need fancy routing capabilities, such as projects ported from frameworks like Ruby on Rails, or those adhering to specifications like the Open Container Initiative (OCI) Distribution Specification.
The goal of wayfind
is to remain competitive with the fastest libraries, while offering advanced routing features when needed. Unused features shouldn't impact performance - you only pay for what you use.
[dependencies]
wayfind = "0.9"
use std::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)?;
router.insert("/images/<name>.<ext>", 6)?;
{
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/photo.jpg").unwrap();
assert_eq!(search.data, &6);
assert_eq!(search.template, "/images/<name>.<ext>");
assert_eq!(search.parameters[0], ("name", "photo"));
assert_eq!(search.parameters[1], ("ext", "jpg"));
let search = router.search("/images/.png");
assert_eq!(search, None);
}
// Wildcard
router.insert("/files/<*path>", 7)?;
router.insert("/files/<*path>/delete", 8)?;
{
let search = router.search("/files/documents").unwrap();
assert_eq!(search.data, &7);
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, &8);
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", 9)?;
router.insert("/backups/<*path>.<ext>", 10)?;
{
let search = router.search("/backups/production/database.tar.gz").unwrap();
assert_eq!(search.data, &9);
assert_eq!(search.template, "/backups/<*path>.tar.gz");
assert_eq!(search.parameters[0], ("path", "production/database"));
let search = router.search("/backups/dev/application.log.bak").unwrap();
assert_eq!(search.data, &10);
assert_eq!(search.template, "/backups/<*path>.<ext>");
assert_eq!(search.parameters[0], ("path", "dev/application.log"));
assert_eq!(search.parameters[1], ("ext", "bak"));
let search = router.search("/backups/.bak");
assert_eq!(search, None);
}
println!("{router}");
Ok(())
}
/
├─ backups/
│ ╰─ <*path>
│ ╰─ .
│ ├─ tar.gz
│ ╰─ <ext>
├─ files/
│ ├─ <*path>
│ │ ╰─ /delete
│ ╰─ <*path>
├─ health
├─ images/
│ ╰─ <name>
│ ╰─ .
│ ├─ png
│ ╰─ <ext>
╰─ users/
╰─ <id>
╰─ /message
wayfind
uses a compressed radix trie for its data storage.
This is the common backbone of almost all routers implemented in Rust.
What sets wayfind
apart is its search strategy.
Most routers either use "first match wins" or "best match wins" (via backtracking), wayfind
uses a hybrid approach:
You only pay the cost of backtracking if you make use of inline parameters, and only for that given segment.
This can result in some matches which may be unexpected, but in practice it works well for real-world usage.
wayfind
is fast, and appears to be competitive against other top performers in all benchmarks we currently run.
See BENCHMARKING.md for the results.
wayfind
is licensed under the terms of both the MIT License and the Apache License (Version 2.0).