serv4rs 0.1.7

serv4rs is a powerful, pragmatic, and extremely fast web framework for Rust
Documentation
use crate::prelude::*;
use crate::mandelbrot;
use actix_web::{Responder};

static TEMP_UPLOAD_FOLDER: &str = "/tmp/temp_serv4rs";

#[derive(Debug, validator::Validate, serde::Serialize, serde::Deserialize)]
pub struct QueryStringParams {
    #[validate(length(min = 3))]
    projects: Vec<i32>,
    #[validate(length(min = 2))]
    name: String,
    #[validate(range(min = 18, max = 20, message = "Age must be between 18 to 20"))]
    age: usize,
}

#[derive(Debug, validator::Validate, serde::Serialize, serde::Deserialize)]
pub struct ProjectId {
    #[validate(range(min = 1, max = 100, message = "project_id must be between 1 to 100"))]
    project_id: usize,
}

#[derive(Debug, validator::Validate, actix_easy_multipart::MultipartForm)]
pub struct MyUploadForm {
    #[multipart(rename = "file")]
    files: Vec<actix_easy_multipart::tempfile::Tempfile>,
    names: Vec<actix_easy_multipart::text::Text<String>>,
}

// info
pub async fn info2() -> crate::Result<&'static str> {
    Ok("Hello, server is alive and kicking.")
}

// http://localhost:9101/5/fftt?projects[]=66&projects[]=66&projects[]=66&name=keesh
// PathVariable and QueryString
pub async fn simple1(
    name: actix_web::web::Path<String>,
    query: actix_web_validator::QsQuery<QueryStringParams>,
) -> impl Responder {
    log::info!("simple1: name={}", name);
    HttpResponse::Ok().json(R::ok(query.into_inner()))
}

// PathVariable and JsonBody
pub async fn simple2(
    project: actix_web_validator::Path<ProjectId>,
    params: actix_web_validator::Json<QueryStringParams>,
) -> crate::Result<HttpResponse> {
    log::info!("simple2: project_id={}", project.into_inner().project_id);
    Ok(HttpResponse::Ok().json(R::ok(params.into_inner())))
}

// PathVariable and JsonBody
pub async fn simple3(
    project: actix_web_validator::Path<ProjectId>,
    params: actix_web_validator::Json<QueryStringParams>,
) -> impl Responder {
    log::info!("simple3: project_id={}", project.into_inner().project_id);
    HttpResponse::Ok().json(R::ok(params.into_inner()))
}

// JsonBody validation
pub async fn simple4(
    project: actix_web_validator::Json<ProjectId>,
) -> crate::Result<HttpResponse> {
    let p = project.into_inner();
    log::info!("simple4: project_id={}", p.project_id);
    Ok(HttpResponse::Ok().json(R::ok(p.project_id)))
}

// curl -X POST http://localhost:9101/index5/10  -H 'Content-Type: application/x-www-form-urlencoded' -d 'firstName=Jane&lastName=Doe&projects[2]=1&projects[1]=2&projects[0]=3'
// FormData validation, BAD!!! (cound not parse array form data elements)
pub async fn simple5(
    project: actix_web_validator::Path<ProjectId>,
    params: actix_web_validator::Form<QueryStringParams>,
) -> crate::Result<HttpResponse> {
    let p = project.into_inner();
    log::info!("simple5: project_id={}", p.project_id);
    Ok(HttpResponse::Ok().json(R::ok(params.into_inner())))
}

// simple redirect
// -L follow redirect
// curl -vL http://localhost:9101/simple6
pub async fn simple6(request: HttpRequest) -> crate::Result<HttpResponse> {
    request.redirect("info")
}

// 上传文件
// curl -v -F 'file=@\"C:/myfile.txt\"' 'http://localhost:9101/upload_file'
pub async fn save_files(
    actix_easy_multipart::MultipartForm(form): actix_easy_multipart::MultipartForm<MyUploadForm>,
) -> crate::Result<impl Responder> {
    for f in form.files {
        if let Some(file_name) = f.file_name {
            if file_name.trim() != "" {
                let path = format!("{}/{}", TEMP_UPLOAD_FOLDER, file_name);
                log::info!("save_files: saving to {path}");
                f.file.persist(path).unwrap();
            }
        }
    }

    for name in form.names {
        log::info!("save_files: names {}", name.into_inner());
    }

    Ok(HttpResponse::Ok().json(R::ok(true)))
}

// Assert Validation
pub async fn panic1(project: actix_web_validator::Path<ProjectId>) -> impl Responder {
    let i = project.into_inner();
    // assert!(i.project_id == 1, "project id must be equals 1");
    // assert_err!(i == 1, Err(Error::UrlParsingFailed(_)));
    HttpResponse::Ok().json(R::ok(i.project_id))
}

// 蒙德布洛德集合绘图
pub async fn mandelbrot() -> std::io::Result<actix_files::NamedFile> {
    let file_name = "mandel.png";
    let current_file = std::path::Path::new(&crate::commons::temp_dir()).join(file_name);

    log::info!("mandelbrot_png: {}", current_file.display().to_string());

    let args = vec![
        current_file.display().to_string(),
        String::from("4000x3000"),
        String::from("-1.20,0.35"),
        String::from("-1,0.20"),
    ];

    mandelbrot::mandelbrot_png::write1(&args);

    actix_files::NamedFile::open(current_file.display().to_string())
}

// web pages
pub async fn default_page(request: HttpRequest) -> impl Responder {
    let mut ctx = tera::Context::new();
    ctx.insert("name", "啦啦发啦");
    request.render(200, "index.html", ctx)
}

// graphiql handler
pub async fn graphiql(request: HttpRequest) -> impl Responder {
    let mut ctx = tera::Context::new();
    ctx.insert("title", "QraphiQl");
    request.render(200, "graphiql.html", ctx)
}