saphir 3.1.1

Fully async-await http server framework
Documentation
use log::info;
use saphir::{file::middleware::FileMiddlewareBuilder, prelude::*};
use serde_derive::{Deserialize, Serialize};

struct PrintGuard {
    inner: String,
}

#[guard]
impl PrintGuard {
    pub fn new(inner: &str) -> Self {
        PrintGuard { inner: inner.to_string() }
    }

    async fn validate(&self, req: Request) -> Result<Request, u16> {
        info!("{}", self.inner);
        Ok(req)
    }
}

#[derive(Serialize, Deserialize, Clone)]
struct User {
    username: String,
    age: i64,
}

struct UserController {}

#[controller(name = "users", version = 1, prefix = "api")]
impl UserController {
    #[get("/<user_id>")]
    async fn get_user(&self, user_id: String, action: Option<u16>) -> (u16, String) {
        (200, format!("user_id: {}, action: {:?}", user_id, action))
    }

    #[post("/json")]
    #[validator(exclude("user"))]
    async fn post_user_json(&self, user: Json<User>) -> (u16, Json<User>) {
        (200, user)
    }

    #[get("/form")]
    #[post("/form")]
    #[validator(exclude("user"))]
    async fn user_form(&self, user: Form<User>) -> (u16, Form<User>) {
        (200, user)
    }

    #[cookies]
    #[post("/sync")]
    #[validator(exclude("req"))]
    fn get_user_sync(&self, mut req: Request<Json<User>>) -> (u16, Json<User>) {
        let u = req.body_mut();
        u.username = "Samuel".to_string();
        (200, Json(u.clone()))
    }

    #[get("/")]
    #[guard(PrintGuard, init_expr = "UserController::BASE_PATH")]
    async fn list_user(&self, _req: Request<Body>) -> (u16, String) {
        (200, "Yo".to_string())
    }

    #[post("/multi")]
    async fn multipart(&self, mul: Multipart) -> (u16, String) {
        let mut multipart_image_count = 0;
        while let Ok(Some(mut f)) = mul.next_field().await {
            if f.content_type() == &mime::IMAGE_PNG {
                let _ = f.save(format!("/tmp/{}.png", f.name())).await;
                multipart_image_count += 1;
            }
        }

        (200, format!("Multipart form data image saved on disk: {}", multipart_image_count))
    }

    #[get("/file")]
    async fn file(&self, _req: Request<Body<Vec<u8>>>) -> (u16, Option<File>) {
        match File::open("/path/to/file").await {
            Ok(file) => (200, Some(file)),
            Err(_) => (500, None),
        }
    }
}

struct ApiKeyMiddleware(String);

#[middleware]
impl ApiKeyMiddleware {
    pub fn new(api_key: &str) -> Self {
        ApiKeyMiddleware(api_key.to_string())
    }

    async fn next(&self, ctx: HttpContext, chain: &dyn MiddlewareChain) -> Result<HttpContext, SaphirError> {
        if let Some(Ok("Bearer secure-key")) = ctx
            .state
            .request_unchecked()
            .headers()
            .get(header::AUTHORIZATION)
            .map(|auth_value| auth_value.to_str())
        {
            info!("Authenticated");
        } else {
            info!("Not Authenticated");
        }

        info!("Handler {} will be used", ctx.metadata.name.unwrap_or("unknown"));
        chain.next(ctx).await
    }
}

#[tokio::main]
async fn main() -> Result<(), SaphirError> {
    env_logger::init();

    let file_middleware = FileMiddlewareBuilder::new("op", "./saphir/examples/files_to_serve").build()?;
    let server = Server::builder()
        .configure_listener(|l| l.interface("127.0.0.1:3000").server_name("MacroExample").request_timeout(None))
        .configure_middlewares(|m| {
            m.apply(ApiKeyMiddleware::new("secure-key"), vec!["/"], None)
                .apply(file_middleware, vec!["/op/"], None)
        })
        .configure_router(|r| r.controller(UserController {}))
        .build();

    server.run().await
}