usher 0.2.0

Parameterized routing for generic resources in Rust
Documentation
use hyper::rt::{self, Future};
use hyper::service::service_fn_ok;
use hyper::{Body, Request, Response, Server, StatusCode};
use usher::capture::find_capture;
use usher::http::HttpRouter;
use usher::prelude::*;

use std::sync::Arc;

/// Represents a boxed function which receives a request/params and returns a response.
type Callee =
    Box<dyn Fn(Request<Body>, Vec<(&str, (usize, usize))>) -> Response<Body> + Send + Sync>;

fn main() {
    // Create our address to bind to, localhost:3000
    let addr = ([127, 0, 0, 1], 3000).into();

    // Just like in a normal Router, we provide our parsers at startup.
    let mut router: HttpRouter<Callee> =
        HttpRouter::new(vec![Box::new(DynamicParser), Box::new(StaticParser)]);

    // This will echo the provided name back to the caller.
    router.get(
        "/:name",
        Box::new(|req, params| {
            let path = req.uri().path();
            let name = find_capture(&path, &params, "name").unwrap();

            Response::new(format!("Hello, {}!\n", name).into())
        }),
    );

    // Wrap inside an Arc to avoid large clones.
    let router = Arc::new(router);

    // Construct our Hyper server.
    let server = Server::bind(&addr)
        .serve(move || {
            // We need a "clone" of the router.
            let router = router.clone();

            // Construct a Hyper service from a function which turns a request
            // into an asynchronous response (which comes from echo()).
            service_fn_ok(move |req: Request<Body>| {
                // First we need to extract the method and path to use for the
                // actual handler lookup as it uses a combination of both values.
                let method = req.method();
                let path = req.uri().path();

                // Then we delegate to a hander when possible.
                match router.handler(method, path) {
                    // In this case, invoke the handler and pass through the
                    // request instance and the captures associated with it.
                    //
                    // In this case we pass through the captures as they're
                    // generated by default, but this might be where you wish
                    // to turn them into something like a `HashMap` for access.
                    Some((handler, captures)) => handler(req, captures),

                    // If no handler matches, we generate a 404 response to
                    // state so back to the caller. This happens when either
                    // the HTTP method or the path is wrong during matching.
                    None => {
                        let mut response = Response::new(Body::empty());
                        *response.status_mut() = StatusCode::NOT_FOUND;
                        response
                    }
                }
            })
        })
        .map_err(|e| eprintln!("server error: {}", e));

    // Log the port we're listening on so we don't forget!
    println!("Listening on http://{}", addr);

    // Initialze the actual service.
    rt::run(server);
}