path-tree

Path-based routing tree.

A compressing dynamic trie ([radix tree]) structure is used for efficient matching.

Build Status Latest version Documentation License

Features

Usage

```rust use path_tree::PathTree;

let mut tree = PathTree::::new();

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, []); ```

Examples

```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; use std::sync::Arc;

type Params<'a> = Vec<(&'a str, &'a str)>;

type Handler = fn(Request, Params) -> Response;

fn index(_: Request, _: Params) -> Response { Response::new(Body::from("Hello, Web!")) }

fn helloworld(: Request, params: Params) -> 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 hellouser(: Request, params: Params) -> Response { let mut s = String::new(); s.pushstr("Hello, "); for (k, v) in params { s.pushstr(&format!("{} = {}", k, v)); } s.push_str("!"); Response::new(Body::from(s)) }

fn hellorust(: Request, _: Params) -> Response { Response::new(Body::from("Hello, Rust!")) }

fn login(_req: Request, _: Params) -> Response { Response::new(Body::from("I'm logined!")) }

fn main() { let addr = ([127, 0, 0, 1], 3000).into();

let mut tree = PathTree::<Handler>::new();
tree.insert("/GET/", index);
tree.insert("/GET/*", hello_world);
tree.insert("/GET/hello/:name", hello_user);
tree.insert("/GET/rust", hello_rust);
tree.insert("/POST/login", login);

let tree = Arc::new(tree);

let routing = move || {
    let router = Arc::clone(&tree);

    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);

} ```

Acknowledgements

It is inspired by the:

License

This project is licensed under either of