# `rou3-rs`
**A flexible and fast HTTP router for Rust, inspired by [rou3-js](https://github.com/h3js/rou3).**
[](https://crates.io/crates/rou3)
[](https://docs.rs/rou3)
[](https://github.com/MuntasirSZN/rou3-rs/actions)
[](https://opensource.org/licenses/MIT)
`rou3-rs` provides a high-performance routing solution for Rust applications, drawing inspiration from the design principles of `rou3-js`. It uses a trie-based structure with efficient hash map lookups for dynamic path segments and an optimized map for purely static routes.
## Features
- **Versatile Route Matching:**
- **Static routes:** e.g., `/home`, `/about/contact`
- **Parameterized routes:** e.g., `/users/:id`, `/posts/:category/:slug`
- **Wildcard routes:**
- Single segment wildcard: `/files/*` (captures one segment)
- Multi-segment (catch-all) wildcard: `/assets/**:filepath` (must be at the end)
- **Optional parameters:** e.g., `/search/:query?` (matches `/search/` and `/search/term`)
- **Method-based Routing:** Supports standard HTTP methods (GET, POST, PUT, DELETE, etc.) and an "ANY" method (empty string `""`) to match any HTTP method.
- **Efficient:**
- Trie structure with `AHashMap` for fast dynamic dispatch.
- Dedicated `static_map` for instant lookups of purely static paths.
- **Dynamic Modification:** Add and remove routes at runtime.
- **`find_all_routes`:** Retrieve all routes that match a given path, useful for middleware or complex dispatch logic.
- **Thread-Safe:** Core router operations are thread-safe using `parking_lot::RwLock`.
- **Clear Error Handling:** Provides a `RouterError` enum for robust error management.
## Installation
Add `rou3-rs` to your `Cargo.toml`:
```toml
[dependencies]
rou3 = "0.1.0"
```
## Usage
Here's a quick overview of how to use `rou3-rs`:
```rust
use rou3::{Router, add_route, find_route, find_all_routes, MatchedRoute, RouterError};
use std::collections::HashMap; // For easily checking params
fn main() -> Result<(), RouterError> {
// Create a new router. Let's say it stores string slices as data.
let router: Router<&'static str> = Router::new();
// 1. Add a static route
add_route(&router, "GET", "/home", "Welcome Home!")?;
// 2. Add a parameterized route
add_route(&router, "GET", "/users/:userId", "User Profile")?;
add_route(&router, "POST", "/users/:userId/message", "Send Message to User")?;
// 3. Add a wildcard route
add_route(&router, "GET", "/files/*", "Single File Wildcard")?; // Matches /files/report.pdf
add_route(&router, "GET", "/assets/**:filepath", "Serve Asset")?; // Matches /assets/css/style.css
// 4. Add a route with an optional parameter
add_route(&router, "GET", "/search/:query?", "Search Page")?;
// 5. Add a route for ANY HTTP method
add_route(&router, "", "/any/path", "Matches any method")?;
// --- Finding routes ---
// Find the static route
let home_route = find_route(&router, "GET", "/home", false)?; // capture_params = false
assert_eq!(home_route.data, "Welcome Home!");
assert!(home_route.params.is_none());
// Find a parameterized route and capture parameters
let user_route = find_route(&router, "GET", "/users/123", true)?; // capture_params = true
assert_eq!(user_route.data, "User Profile");
let expected_params = HashMap::from([("userId".to_string(), "123".to_string())]);
assert_eq!(user_route.params.map(|p| p.into_iter().collect::<HashMap<_,_>>()), Some(expected_params));
// Find a route matching the optional parameter (with value)
let search_with_query = find_route(&router, "GET", "/search/rust-router", true)?;
assert_eq!(search_with_query.data, "Search Page");
assert_eq!(
search_with_query.params.unwrap().get("query"),
Some(&"rust-router".to_string())
);
// Find a route matching the optional parameter (without value)
let search_without_query = find_route(&router, "GET", "/search/", true)?; // or /search
assert_eq!(search_without_query.data, "Search Page");
assert!(search_without_query.params.as_ref().map_or(true, |p| p.get("query").is_none() && p.is_empty()));
// Find a route using the ANY method
let any_method_route_get = find_route(&router, "GET", "/any/path", false)?;
assert_eq!(any_method_route_get.data, "Matches any method");
let any_method_route_post = find_route(&router, "POST", "/any/path", false)?;
assert_eq!(any_method_route_post.data, "Matches any method");
// --- Finding all matching routes ---
add_route(&router, "GET", "/config", "Config Base")?;
add_route(&router, "GET", "/config/:key", "Config Key Specific")?;
add_route(&router, "GET", "/config/**:path", "Config Wildcard")?;
let all_matches = find_all_routes(&router, "GET", "/config/timeout", true);
println!("Found {} matches for /config/timeout:", all_matches.len());
for m in all_matches {
println!(" - Data: {}, Params: {:?}", m.data, m.params);
}
// Expected output would show 3 matches: Config Key Specific, Config Wildcard, and potentially a root wildcard if one was added.
Ok(())
}
```
## Benchmarks
`rou3-rs` is designed with performance in mind. It includes benchmarks comparing it against other popular Rust routers. For detailed results, please see the benchmark files in the `benches` directory and run `cargo bench`.
## Contributing
Contributions are welcome! Please feel free to submit issues, fork the repository, and create pull requests.
Ways to contribute:
- Reporting bugs
- Suggesting new features or enhancements
- Improving documentation
- Adding more test cases
- Optimizing performance
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Acknowledgements
- The design and API are heavily inspired by [h3js/rou3](https://github.com/h3js/rou3).