athene_macro 0.1.0

Macro generation for athene
Documentation
athene_macro-0.1.0 has been yanked.

Useage

use askama::Template;
use athene::prelude::*;
use athene::{html, json, status};
use serde::{Deserialize, Serialize};
use tracing::info;

// use validator::{Validate, ValidationError};
// #[derive(Serialize, Deserialize, Clone, Validate)]
#[derive(Serialize, Deserialize, Clone)]
pub struct User {
    // #[validate(email)]
    pub username: String,
    // #[validate(range(min = 18, max = 20))]
    pub age: u16,
}

// ================================ Controller ===================================
pub struct UserController {}

// http://127.0.01:7878/api/v1/user
#[controller(prefix = "api", version = 1, name = "user")]
impl UserController {

    #[delete("/<username>/<age>")]
    pub async fn delete_by_param(&self, username: String, age: Option<u16>) -> (u16, String) {
        (
            200,
            format!("username is : {}, and age is : {:?}", username, age),
        )
    }

    #[get("/get_query_1")]
    pub async fn get_query_1(&self, username: String, age: u16) -> (u16, Json<User>) {
        (200, Json(User { username, age }))
    }

    #[get("/get_query_2")]
    pub async fn get_query_2(&self, request: Request) -> Result<(u16, Json<User>)> {
        let user = request.query::<User>()?;
        Ok((200, Json(user)))
    }

    // Context-Type : application/json
    #[post("/post_json")]
    // #[validator(exclude("user"))]
    pub async fn post_json(&self, user: Json<User>) -> impl Responder {
        (200, user)
    }

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

    // Context-Type : application/json  Or application/x-www-form-urlencoded
    // Context-Type : application/msgpack Or application/cbor
    #[post("/parse_body")]
    async fn parse_body(&self, mut req: Request) -> Result<(u16, String)> {
        let user = req.parse::<User>().await?;
        Ok((
            200,
            format!("username = {} and age = {}", user.username, user.age),
        ))
    }

    // Context-Type : application/multipart-formdata
    #[post("/files")]
    async fn files(&self, mut req: Request) -> Result<(u16, String)> {
        let files = req.files("files").await?;
        let fist_file_name = files[0].name().unwrap();
        let second_file_name = files[1].name().unwrap();
        let third_file_name = files[2].name().unwrap();
        Ok((
            200,
            format!("fist {}, second {}, third {}", fist_file_name, second_file_name,third_file_name),
        ))
    }

    // Context-Type : application/multipart-formdata
    #[post("/upload")]
    async fn upload(&self, mut req: Request) -> impl Responder {
        if let Ok(res) = req.upload("file", "temp").await {
            (200, res)
        } else {
            (200, String::from("Bad Request"))
        }
    }
}

// ================================ Middleware  ======================
struct ApiKeyMiddleware(String);

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

    async fn next(&self, ctx: Context, chain: &dyn Next) -> Result<Context, Error> {
        if let Some(Ok("Bearer secure-key")) = ctx
            .state
            .request_unchecked()
            .headers()
            .get("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
    }
}

async fn log_middleware(ctx: Context, next: &'static dyn Next) -> Result<Context, Error> {
    info!(
        "new request on path: {}",
        ctx.state.request_unchecked().uri().path()
    );

    let ctx = next.next(ctx).await?;

    info!(
        "new response with status: {}",
        ctx.state.response_unchecked().status()
    );
    Ok(ctx)
}

// ================================  Handler ===================================

pub async fn hello(_req: Request) -> (u16, &'static str) {
    (200, "Hello, World!")
}

pub async fn user_params(mut req: Request) -> impl Responder {
    let username = req.param("username");
    let age = req.param("age");
    let age = age.parse::<u16>().unwrap();
    (200, Json(User { username, age }))
}

pub async fn login(mut req: Request) -> impl Responder {
    if let Ok(user) = req.body_mut().parse::<Json<User>>().await {
        (
            200,
            format!("username: {} , age: {} ", user.username, user.age),
        )
    } else {
        (400, String::from("Bad user format"))
    }
}

pub async fn sign_up(mut req: Request) -> Result<(u16, Json<User>)> {
    let user = req.body_mut().parse::<Form<User>>().await?;
    Ok((200, Json(user)))
}

pub async fn read_body(mut req: Request) -> Result<(u16, String)> {
    let body = req.body_mut().parse::<String>().await?;
    Ok((200, body))
}

// ================================  Router ===================================
pub fn user_router(r: Router) -> Router {
    r.get("/hello", hello)
        .get("/{username}/{age}", user_params)
        .post("/login", login)
        .post("/sign_up", sign_up)
        .put("/read_body", read_body)
}

#[tokio::main]
pub async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt().compact().init();

    let app = athene::new()
        .router(|r| {

            // ====================== Closure ==========================
            r.get("/", |_req: Request| async {
                "Welcome to the athene web framework "
            })

            // Content-Disposition: application/octet-stream
            .get("/download", |_req: Request| async {
                let mut res = status!(hyper::StatusCode::OK);
                res.write_file("templates/author.txt", DispositionType::Attachment)?;
                Ok::<_, Error>(res)
            })

            .delete("/delete", |req: Request| async move {
                let path = req.uri().path().to_string();
                (200, path)
            })

            // use json!()
            .patch("/patch", |_req: Request| async {
                let user = User {
                    username: "Rustacean".to_string(),
                    age: 18,
                };
                json!(&user)
            })

            // Template rendering
            .get("/html", |_req: Request| async {
                #[derive(Serialize, Template)]
                #[template(path = "favorite.html")]
                pub struct Favorite<'a> {
                    title: &'a str,
                    time: i32,
                }

                let love = Favorite {
                    title: "rust_lang",
                    time: 1314,
                };
                html!(love.render().unwrap())
            })
        })
        .router(user_router)
        .router(|r| r.controller(UserController {}))
        .middleware(|m| {
            m.apply(ApiKeyMiddleware::new("secure-key"), vec!["/"], vec!["/api"])
                .apply(log_middleware, vec!["/api"], None)
        })
        .build();

    app.listen("127.0.0.1:7878").await
}