darpi 0.1.0

A web framework with type safety and speed in mind
Documentation
### darpi

A web api framework with speed and safety in mind.
One of the big goals is to catch all errors at `compile time`, if possible.
The framework uses [hyper](https://github.com/hyperium/hyper) and ideally, the performance should be as if you were using `hyper` yourself.
The framework also uses [shaku](https://github.com/Mcat12/shaku) for `compile time` verifiable dependency injection.

The framework is in early development.
All feedback is appreciated.

An example of a simple app

`Cargo.toml`
```
[dependencies]
darpi = {git = "https://github.com/petar-dambovaliev/darpi.git", branch = "master"}
serde = { version = "1.0", features = ["derive"] }
tokio = {version = "0.2.11", features = ["full"]}
shaku = {version = "0.5.0", features = ["thread_safe"]}
```
`main.rs`
```rust
use async_trait::async_trait;
use darpi::{
    app, handler, middleware, middleware::Expect, path_type, query_type, request::ExtractBody,
    response::ResponderError, Body, Json, Method, Path, Query, RequestParts,
};
use derive_more::{Display, From};
use serde::{Deserialize, Serialize};
use shaku::{module, Component, Interface};
use std::sync::Arc;
use UserRole::Admin;
 
///////////// setup dependencies with shaku ///////////
#[derive(Component)]
#[shaku(interface = UserExtractor)]
struct UserExtractorImpl;

#[async_trait]
impl UserExtractor for UserExtractorImpl {
    async fn extract(&self, p: &RequestParts) -> Result<UserRole, Error> {
        Ok(UserRole::Admin)
    }
}

#[async_trait]
trait UserExtractor: Interface {
    async fn extract(&self, p: &RequestParts) -> Result<UserRole, Error>;
}

module! {
    Container {
        components = [UserExtractorImpl, MyLogger],
        providers = [],
    }
}

fn make_container() -> Container {
    Container::builder().build()
}

////////////////////////

#[derive(Debug, Display, From)]
enum Error {
    #[display(fmt = "no auth header")]
    NoAuthHeaderError,
    #[display(fmt = "Access denied")]
    AccessDenied,
}

impl ResponderError for Error {}

#[derive(Eq, PartialEq, Ord, PartialOrd)]
enum UserRole {
    Regular,
    Admin,
}

// there are 2 types of middleware `Request` and `Response`
// the constant argument that needs to be present is &RequestParts
// everything else is up to the user
// Arc<dyn UserExtractor> types are injected from the shaku container
// Expect<UserRole> is a special type that is provided by the user when
// the middleware is linked to a handler. This allows the expected value
// to be different per handler + middleware
// middlewares are obgligated to return Result<(), impl ResponderErr>
// if a middleware returns an Err(e) all work is aborted and the coresponding
// response is sent to the user
#[middleware(Request)]
async fn access_control(
    user_role_extractor: Arc<dyn UserExtractor>,
    p: &RequestParts,
    expected_role: Expect<UserRole>,
) -> Result<(), Error> {
    let actual_role = user_role_extractor.extract(p).await?;

    if expected_role > actual_role {
        return Err(Error::AccessDenied);
    }
    Ok(())
}

#[path_type]
#[query_type]
#[derive(Deserialize, Serialize, Debug)]
pub struct Name {
    name: String,
}

// Path<Name> is extracted from the registered path "/hello_world/{name}"
// and it is always mandatory. A request without "{name}" will result
// in the request path not matching the handler. It will either match another
// handler or result in an 404
// Option<Query<Name>> is extracted from the url query "?name=jason"
// it is optional, as the type suggests. To make it mandatory, simply
// remove the Option type. If there is a Query<T> in the handler and
// an incoming request url does not contain the query parameters, it will
// result in an error response
#[handler]
async fn hello_world(p: Path<Name>, q: Option<Query<Name>>) -> String {
    let other = q.map_or("nobody".to_owned(), |n| n.0.name);
    format!("{} sends hello to {}", p.name, other)
}

// the handler macro has 2 optional arguments
// the shaku container type and a collection of middlewares
// the enum variant `Admin` is corresponding to the middlewre `access_control`'s Expect<UserRole>
// ExtractBody<T<Name>> is extracted from the request body
// where T is the format and it has to implement the trait FromRequestBody
// Json, Yaml and Xml are supported out of the box
// failure to extract will result in an error response
#[handler(Container, [access_control(Admin)])]
async fn do_something(p: Path<Name>, payload: ExtractBody<Json<Name>>) -> String {
    format!("{} sends hello to {}", p.name, payload.name)
}
 
 #[tokio::main]
 async fn main() -> Result<(), darpi::Error> {
    // the `app` macro creates a server and allows the user to call
    // the method `run` and await on that future
     app!({
        // if the address is a string literal, it is verified at compile time
        // if it is a string variable, obviously, it won't
         address: "127.0.0.1:3000",
         // via the container we inject our dependencies
         // in this case, MyLogger type
         // any handler that has the trait Logger as an argument
         // will be given MyLogger
         module: make_container => Container,
         bind: [
             {
                 // When a path argument is defined in the route,
                 // the handler is required to have Path<T> as an argument
                 // if not present, it will result in a compilation error
                 route: "/hello_world/{name}",
                 method: Method::GET,
                 // handlers bound with a GET method are not allowed
                 // to request a body(payload) from the request.
                 // Json<T> argument would result in a compilation error
                 handler: hello_world
             },
             {
                 route: "/hello_world/{name}",
                 method: Method::POST,
                 // the POST method allows this handler to have
                 // Json<Name> as an argument
                 handler: do_something
             },
         ],
     })
    .run()
    .await
 }
```