Path-based routing tree.
A compressing dynamic trie ([radix tree]) structure is used for efficient matching.
Fast!
Flexible!
Named parameters. e.g. :name
.
Catch-All parameters. e.g. *any
.
Supports multiple naming for the same path segment. e.g. /users/:id
and /users/:user_id/repos
.
Don't care about routes orders, we will automatically find them.
```rust use path_tree::PathTree;
let mut tree = PathTree::
tree.insert("/", 0); tree.insert("/users", 1); tree.insert("/users/:id", 2); tree.insert("/users/:id/:org", 3); tree.insert("/users/:userid/repos", 4); tree.insert("/users/:userid/repos/:id", 5); tree.insert("/users/:user_id/repos/:id/any", 6); tree.insert("/:username", 7); tree.insert("/any", 8); tree.insert("/about", 9); tree.insert("/about/", 10); tree.insert("/about/us", 11); tree.insert("/users/repos/*any", 12);
// Matched "/" let node = tree.find("/"); asserteq!(node.issome(), true); let res = node.unwrap(); asserteq!(*res.0, 0); asserteq!(res.1, []); // Params
// Matched "/:username" let node = tree.find("/username"); asserteq!(node.issome(), true); let res = node.unwrap(); asserteq!(*res.0, 1); asserteq!(res.1, [("username", "username")]); // Params
// Matched "/any" let node = tree.find("/user/s"); let res = node.unwrap(); assert_eq!(res.0, 8); assert_eq!(res.1, [("any", "user/s")]);
// Matched "/users/:id" let node = tree.find("/users/fundon"); let res = node.unwrap(); asserteq!(*res.0, 2); asserteq!(res.1, [("id", "fundon")]); // Params
// Matched "/users/:userid/repos/:id" let node = tree.find("/users/fundon/repos/trek-rs"); let res = node.unwrap(); asserteq!(*res.0, 5); asserteq!(res.1, [("userid", "fundon"), ("id", "trek-rs")]); // Params
// Matched "/users/:userid/repos/:id/*any" let node = tree.find("/users/fundon/repos/trek-rs/noder/issues"); let res = node.unwrap(); asserteq!(*res.0, 6); asserteq!( res.1, [ ("userid", "fundon"), ("id", "trek-rs"), ("any", "noder/issues"), ] ); // Params
// Matched "/users/repos/any" let node = tree.find("/users/repos/"); let res = node.unwrap(); assert_eq!(res.0, 12); assert_eq!(res.1, []); ```
```rust extern crate futures; extern crate hyper; extern crate path_tree;
use futures::Future; use hyper::server::Server; use hyper::service::servicefnok; use hyper::{Body, Request, Response, StatusCode}; use path_tree::PathTree;
pub type Handler = fn(Request
, Vec<(&str, &str)>) -> Response;fn index(_: Request
, _: Vec<(&str, &str)>) -> Response { Response::new(Body::from("Hello, Web!")) }fn helloworld(: Request
, params: Vec<(&str, &str)>) -> Response { let mut s = String::new(); s.pushstr("Hello, World!\n"); for (, v) in params { s.push_str(&format!("param = {}", v)); } Response::new(Body::from(s)) }fn hellorust(: Request
, _: Vec<(&str, &str)>) -> Response { Response::new(Body::from("Hello, Rust!")) }fn login(_req: Request
, _: Vec<(&str, &str)>) -> Response { Response::new(Body::from("I'm logined!")) }fn main() { let addr = ([127, 0, 0, 1], 3000).into();
let mut tree: PathTree<Handler> = PathTree::new();
tree.insert("/GET/", index);
tree.insert("/GET/*", hello_world);
tree.insert("/GET/rust", hello_rust);
tree.insert("/POST/login", login);
let routing = move || {
let router = tree.clone();
service_fn_ok(move |req| {
let path = "/".to_owned() + req.method().as_str() + req.uri().path();
dbg!(&path);
match router.find(&path) {
Some((handler, params)) => handler(req, params),
None => Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Body::from("Not Found"))
.unwrap(),
}
})
};
let server = Server::bind(&addr)
.serve(routing)
.map_err(|e| eprintln!("server error: {}", e));
hyper::rt::run(server);
} ```
It is inspired by the:
This project is licensed under either of