lest 0.2.1

A modular approach to a web server. Based on actix-web.
Documentation
use std::collections::HashMap;
use std::path;

use actix_web::http::header::ContentType;
use actix_web::web::Payload;
use actix_web::{http::StatusCode, HttpRequest, HttpResponse};
use serde_json::Value;

use crate::models::app::ResponseTemplate;
use crate::util::req::extract_port;
use crate::{models::app::App, util::req::extract_host};
use crate::models::request::Request;
use crate::models::response::Response;
use crate::models::route::Route;
use crate::types::method::Method;
use crate::util::qs::parse_query;
use crate::util::ua::parse_user_agent;
use crate::models::guard::Guard::{Host, Port};
use libcoerced::{generic::text_verify, json::json_verify};

use super::pathfinder::process_path;

fn root_parse_headers(req: &HttpRequest) -> Vec<(String, String)> {
    req.headers().iter().map(|(key, value)| {
        (key.to_string(), value.to_str().unwrap().to_string())
    }).collect()
}

fn root_create_request(req: HttpRequest, stream: Payload, method: Method, root_url: &str) -> Result<Request, String> {
    let user_agent = parse_user_agent(req.headers().get("User-Agent").unwrap().to_str().unwrap().to_string());
    let headers = root_parse_headers(&req);
    let body = String::new();
    let path = req.uri().path().to_string();
    let (valid, params) = process_path(root_url, &path).unwrap_or((false, HashMap::new()));
    if !valid {
        return Err("Invalid path.".to_string());
    }
    let host = extract_host(&req);
    let port = extract_port(&req);
    Ok(Request {
        host: host.unwrap_or("".to_string()),
        port: port.unwrap_or(0),
        method,
        user_agent,
        headers,
        body,
        query: parse_query(req.query_string().to_string()),
        path: req.path().to_string(),
        actix: req,
        stream,
        params,
    })
}

fn root_verify_request(data: &Request, route: &Route) -> (bool, String) {
    match route.guard {
        Host(_) => {
            if data.host.is_empty() {
                return (false, "Host is empty.".to_string());
            }
        },
        Port(_) => {
            if data.port == 0 {
                return (false, "Port is 0 or invalid.".to_string());
            }
        },
        _ => {}
    }

    let guard = (&route.guard).verify(data);
    if guard.is_err() {
        return (false, guard.unwrap_err().to_string());
    }

    if data.method != route.method {
        return (false, "Method does not match.".to_string());
    }

    let request_verify = route.request_verify.as_ref();
    if let Some(left) = request_verify.left() {
        let s = text_verify(left, data.body.as_str());
        if !s {
            return (false, "Text verification failed.".to_string());
        }
        return (true, "".to_string());
    }
    if let Some(right) = request_verify.right() {
        if let Ok(json_body) = serde_json::from_str::<Value>(&data.body) {
            let s = json_verify(Box::new(right.clone()), json_body);
            if !s {
                return (false, "JSON verification failed.".to_string());
            }
            return (true, "".to_string());
        }
    }
    (true, "".to_string())
}

fn root_verify_response(data: &Response, route: &Route) -> bool {
    for (status, verify) in route.response_verify.iter() {
        let response_fn = verify.as_ref();
        if status.into_u16() == data.status {
            if let Some(left) = response_fn.left() {
                if let Some(body) = data.body.clone() {
                    return text_verify(left, body.as_str());
                }
            }
            if let Some(right) = response_fn.right() {
                if let Some(body) = data.body.clone() {
                    if let Ok(json_body) = serde_json::from_str::<Value>(&body) {
                        return json_verify(Box::new(right.clone()), json_body);
                    }
                }
            }
        }
    }
    true
}

fn root_build_response(response: Response) -> actix_web::HttpResponse {
    let mut builder = actix_web::HttpResponse::build(StatusCode::from_u16(response.status).unwrap());
    if response.redirect.is_some() {
        return builder.insert_header(("Location", response.redirect.unwrap())).finish();
    }
    for (key, value) in response.headers {
        builder.insert_header((key, value));
    }
    builder.insert_header(("Set-Cookie", response.cookie));
    builder.content_type(response.content_type);
    builder.body(response.body.unwrap_or("".to_string()))
}

pub async fn root_handle_request(req: HttpRequest, stream: Payload, method: Method, obj: &App<'_>, response_templates: &Vec<ResponseTemplate>) -> HttpResponse {
    let root = obj.root.1.as_ref().unwrap();
    if root.method != method {
        return actix_web::HttpResponse::MethodNotAllowed().body("Method not allowed.");
    }
    let request = root_create_request(req, stream, method, obj.root.0.as_str());
    if request.is_err() {
        for template in response_templates {
            match template {
                ResponseTemplate::InvalidRequest(path) => {
                    let extension = path::Path::new(path).extension().unwrap();
                    let mut body = std::fs::read(path).unwrap();
                    if body.is_empty() {
                        body = b"Invalid request\n*DEVELOPER* - Your template file is empty!".to_vec();
                        return actix_web::HttpResponse::BadRequest().body(body);
                    }
                    if extension == "html" {
                        return actix_web::HttpResponse::BadRequest().content_type(ContentType::html()).body(body);
                    }
                    return actix_web::HttpResponse::BadRequest().body(body);
                },
                _ => {}
            }
        }
        return actix_web::HttpResponse::BadRequest().body("Invalid request");
    }
    let request = request.unwrap();
    let rvf = root_verify_request(&request, root);
    if rvf.0 == false {
        for template in response_templates {
            match template {
                ResponseTemplate::RequestVerificationFailed(path) => {
                    let extension = path::Path::new(path).extension().unwrap();
                    let mut body = std::fs::read(path).unwrap();
                    let body_str = String::from_utf8(body.clone()).unwrap();
                    body = if extension == "html" {
                        str::replace(&body_str, "{{error}}", ("<div>".to_string() + &str::replace(&rvf.1, "\n", "<br>") + "</div>").as_str()).into_bytes()
                    } else {
                        str::replace(&body_str, "{{error}}", &rvf.1).into_bytes()
                    };
                    if body.is_empty() {
                        body = ("Request verification failed:\n".to_string() + &rvf.1 + "\n*DEVELOPER* - Your template file is empty!").into_bytes();
                        return actix_web::HttpResponse::BadRequest().body(body);
                    }
                    if extension == "html" {
                        return actix_web::HttpResponse::BadRequest().content_type(ContentType::html()).body(body);
                    }
                    return actix_web::HttpResponse::BadRequest().body(body);
                },
                _ => {}
            }
        }
        return actix_web::HttpResponse::BadRequest().body("Request verification failed:\n".to_string() + &rvf.1);
    }
    let response = (root.function)(request).await;
    if response.is_err() {
        return actix_web::HttpResponse::InternalServerError().body(response.unwrap_err().to_string());
    }
    let response = response.unwrap();
    if !root_verify_response(&response, root) {
        for template in response_templates {
            match template {
                ResponseTemplate::ResponseVerificationFailed(path) => {
                    let extension = path::Path::new(path).extension().unwrap();
                    let mut body = std::fs::read(path).unwrap();
                    if body.is_empty() {
                        body = b"Response verification failed\n*DEVELOPER* - Your template file is empty!".to_vec();
                        return actix_web::HttpResponse::InternalServerError().body(body);
                    }
                    if extension == "html" {
                        return actix_web::HttpResponse::InternalServerError().content_type(ContentType::html()).body(body);
                    }
                    return actix_web::HttpResponse::InternalServerError().body(body);
                },
                _ => {}
            }
        }
        return actix_web::HttpResponse::InternalServerError().body("Response verification failed.");
    }
    root_build_response(response)
}