lest 0.2.1

A modular approach to a web server. Based on actix-web.
Documentation
use std::{fs, time::Duration};

use super::{cookie::CookieBuilder, form::FormResponse};

#[derive(Debug, Clone, Copy)]
/// Abstraction over actix_web's StatusCode which is arguably more user-friendly.
pub struct Status {
    pub code: u16
}

impl Status {
    pub fn from_u16(code: u16) -> Self {
        Status {
            code
        }
    }

    pub fn from_u32(code: u32) -> Self {
        Status {
            code: code as u16
        }
    }

    pub fn from_u8(code: u8) -> Self {
        Status {
            code: code as u16
        }
    }

    pub fn into_u16(self) -> u16 {
        self.code
    }
}

impl PartialEq for Status {
    fn eq(&self, other: &Self) -> bool {
        self.code == other.code
    }
}

impl Eq for Status {}

impl std::hash::Hash for Status {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.code.hash(state);
    }
}

#[derive(Debug)]
/// This is passed down to response functions to determine the response.
pub struct Response {
    pub status: u16,
    pub headers: Vec<(String, String)>,
    pub cookie: String,
    pub content_type: String,
    pub body: Option<String>,
    pub redirect: Option<String>
}

#[allow(dead_code)]
impl Response {
    /// Create a new response with a status code.
    pub fn new(status: Status) -> Response {
        Response {
            status: status.into_u16(),
            headers: Vec::new(),
            cookie: String::new(),
            content_type: "text/plain".to_string(),
            body: None,
            redirect: None
        }
    }

    /// Turn this response into a redirect response.
    pub fn redirect(&mut self, location: String) {
        self.status = 302;
        self.add_header("Location".to_string(), location);
    }

    /// Add a header to the response.
    pub fn add_header(&mut self, key: String, value: String) {
        self.headers.push((key, value));
    }

    /// Set the cache duration of the response.
    pub fn set_cache(&mut self, duration: Duration) {
        self.add_header("Cache-Control".to_string(), format!("max-age={}", duration.as_secs()));
    }

    /// Set the status code of the response.
    pub fn set_status(&mut self, status: u16) {
        self.status = status;
    }

    /// Set the cookie header of the response.
    pub fn set_cookie(&mut self, cookie: String) {
        self.cookie = cookie;
    }

    /// Using a `CookieBuilder`, set the cookie header of the response.
    pub fn set_cookie_from_builder(&mut self, cookie_builder: CookieBuilder) {
        self.cookie = cookie_builder.build();
    }
    
    /// Set the content type of the response.
    pub fn set_content_type(&mut self, content_type: String) {
        self.content_type = content_type;
    }

    /// Using a `serde_json::Value`, set the JSON body of the response.
    pub fn body_json(&mut self, json: serde_json::Value) {
        self.set_content_type("application/json".to_string());
        self.body = Some(json.to_string());
    }

    /// Set the content_type to HTML and the body to the given HTML.
    pub fn body_html(&mut self, html: String) {
        self.set_content_type("text/html".to_string());
        self.body = Some(html);
    }

    // Set the content_type to plain text and the body to the given text.
    pub fn body_text(&mut self, text: String) {
        self.set_content_type("text/plain".to_string());
        self.body = Some(text);
    }

    /// Set the content_type to the MIME type of the file and the body to the file's contents.
    pub fn body_file(&mut self, file: String) {
        self.set_content_type(mime_guess::from_path(&file).first_or_octet_stream().to_string());
        self.body = Some(fs::read_to_string(file).unwrap());
    }

    /// Using a built form builder, set the form body of the response.
    pub fn body_form(&mut self, form: FormResponse) {
        self.set_content_type(form.content_type);
        self.body = Some(form.body);
    }

    /// Return the raw HTTP response.
    pub fn http_raw(&self) -> String {
        let mut response = String::new();
        response.push_str(&format!("HTTP/1.1 {}\n", self.status));
        for (key, value) in &self.headers {
            response.push_str(&format!("{}: {}\n", key, value));
        }
        response.push_str(&format!("Set-Cookie: {}\n", self.cookie));
        response.push_str(&self.body.clone().unwrap_or("".to_string()));
        response
    }

    /// Return the raw HTTP/2 response.
    pub fn http2_raw(&self) -> String {
        let mut response = String::new();
        response.push_str(&format!("HTTP/2 {}\n", self.status));
        for (key, value) in &self.headers {
            response.push_str(&format!("{}: {}\n", key, value));
        }
        response.push_str(&format!("Set-Cookie: {}\n", self.cookie));
        response.push_str(&self.body.clone().unwrap_or("".to_string()));
        response
    }
}