athene 1.0.3

A simple and lightweight rust web framework based on hyper
Documentation

Useage

use askama::Template;
use serde::{Serialize,Deserialize};
use athene::{Error, Middleware, Next, Request, Response, Result, StatusCode};
use tera::{Tera,Context};
use std::pin::Pin;

#[derive(Serialize,Deserialize)]
pub struct User {
    name: String,
    age: i32,
}

pub async fn hello(req: Request) -> Result{
    let name: String = req.param("name").unwrap();
    let mut res = Response::new();
    res.text(name)?;
    Ok(res)
}

pub async fn world(_req: Request) -> Result{
    let mut res = Response::new();
    res.text("world")?;
    Ok(res)
}

pub async fn welcome(req: Request) -> Result{
    let user = req.query::<User>().unwrap();
    let mut res = Response::new();
    res.json(&user)?;
    Ok(res)
}

pub async fn redirect(_req: Request) -> Result{
    println!("redirect handler");
    Response::redirect(StatusCode::MOVED_PERMANENTLY, "/api/rust")
}

pub async fn code(_req: Request) -> Result{
    let mut res = Response::new();
    res.text("code handler")?;
    Ok(res)
}

pub async fn rust_lang(_req: Request) -> Result{
    let mut res = Response::new();
    res.text("welcome to rust_lang world")?;
    Ok(res)
}

pub async fn permanent(_req: Request) -> Result{
    Response::redirect(StatusCode::PERMANENT_REDIRECT, "/api/code")
}

pub async fn temporary(_req: Request) -> Result{
    Response::redirect(StatusCode::TEMPORARY_REDIRECT, "/api/world")
}

pub async fn location(_req: Request) -> Result{
    Response::location("/")
}

pub async fn login(req: Request) -> Result{
    let mut req = req;
    let user = req.parse::<User>().await?;
    let mut res = Response::new();
    res.json(&user)?;
    Ok(res)
}

pub async fn upload(req: Request) -> Result{
    let mut req = req;
    let file = req.file("file").await?;
    std::fs::create_dir_all("temp").unwrap();
    let dest = format!("temp/{}", file.name().unwrap_or("file"));
    let info = if let Err(e) = std::fs::copy(file.path(), std::path::Path::new(&dest)) {
        format!("file not found in request: {e}")
    } else {
        format!("File uploaded to {dest}")
    };
    let mut res = Response::new();
    res.text(info)?;
    Ok(res)
}

pub async fn uploads(req: Request) -> Result{
    let mut req = req;
    let mut res = Response::new();
    let files = req.files("files").await?;
    std::fs::create_dir_all("temp").unwrap();
    let mut msgs = Vec::with_capacity(files.len());
    for file in files {
        let dest = format!("temp/{}", file.name().unwrap_or("file"));
        if let Err(e) = std::fs::copy(file.path(), std::path::Path::new(&dest)) {
            res.text(format!("file not found in request: {e}"))?;
        } else {
            msgs.push(dest);
        }
    }
    res.text(format!("Files uploaded:\n\n{}", msgs.join("\n")))?;
    Ok(res)
}

pub async fn auth_index(_: Request) -> Result{
    let mut res = Response::new();
    res.text("The current handler auth_index has been not authenticated ")?;
    Ok(res)
}

#[derive(serde::Serialize, Hash, Eq, PartialEq, Clone, Template)]
#[template(path = "favorite.html")]
pub struct Favorite<'a> {
    title: &'a str,
    time: i32,
}

#[allow(dead_code)]
const LOVE: Favorite = Favorite {
    title: "rust_lang",
    time: 1314,
};

pub async fn askama_template(_: Request) -> Result{
    let mut res = Response::new();
    let love = LOVE.render()?;
    res.html(love)?;
    Ok(res)
}

#[derive(Serialize)]
struct Awesome<'a> {
    url: &'a str,
    name: &'a str,
}

pub async fn tera_template(_: Request) -> Result{
    let tera = Tera::new("templates/*")?;
    let mut ctx = Context::new();
    ctx.insert("title", "Welcome to rust-lang world!");
    ctx.insert(
        "product",
        &vec![
            Awesome {
                url: "https://github.com/rust-lang",
                name: "rust-lang",
            },
            Awesome {
                url: "https://github.com/lapce/lapce",
                name: "lapce",
            },
        ],
    );
    let mut res = Response::new();
    let love = tera.render("index.html",&ctx)?;
    res.html(love)?;
    Ok(res)
}

#[derive(Debug)]
pub struct Logger;

#[async_trait::async_trait]
impl Middleware for Logger {
    async fn handle(self: Pin<&Self>, request: Request, next: Next<'_>) -> Result{
        // let mut res = Response::new();
        // res.text("logger middleware is used")?;
        // Ok(res)
        let res = next.next(request).await?;
        Ok(res)
    }
}

#[tokio::main]
pub async fn main() -> Result<(), Error> {
    let mut router = athene::new();
    router.get("/", redirect);
    router.get("/permanent", permanent);
    router.get("/temporary", temporary);
    router.get("/base", welcome);
    router.post("/upload", upload);
    router.post("/uploads", uploads);
    router.get("/location", location);
    router.get("/askama_template", askama_template);
    router.get("/tera_template", tera_template);

    router.group("/closure", |base| {
        base.get("/str", |_req| async { "closure str" });
        base.get("/string", |_| async { String::from("closure string") });
        base.get("/vec/", |_| async { [1, 3, 5, 7, 9].to_vec() });
        base.get("/status", |_| async { StatusCode::NO_CONTENT });
        base.get("/empty", |_| async {});
        base.get("/empty/bracket", |_| async { () });
        base.get("/remote_addr", |req: Request| async move {
            let addr = req.remote_addr().unwrap();
            let addr = addr.to_string();
            addr
        });
        base.get("/remote_ip", |req: Request| async move {
            let ip = req.ip().unwrap();
            let ip = ip.to_string();
            ip
        });
        base.get("/status/value", |_| async {
            (StatusCode::NOT_FOUND, "welcome to my world")
        });
    });

    router.group("/api", |base| {
        base.get("/hello/{name}", hello);
        base.get("/world", world);
        base.get("/code", code);
        base.get("/rust", rust_lang);
        base.group("/user", |user| {
            user.post("/", login);
        });
    });

    router.middleware(Logger).group("/auth", |base| {
        base.get("/index", auth_index);
    });

    router.listen("127.0.0.1:8081").await
}

templates/index.html

<title>{{title}}</title>
<ul>
  {% for one in product -%}
  <li><a href="{{ one.url | safe }}">{{ one.name }}</a></li>
  {%- endfor %}
</ul>

templates/favorite.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Rust is one of my favorite programming languages</title>
</head>

<body>
    <h4>{{title}}</h4>
    <p>{{time}}</p>
</body>

</html>