wbsl 0.1.1

Wizards Basic Server Library
Documentation
use std::collections::HashMap;
use std::fs::read_to_string;
use std::io::{BufRead, BufReader};
use std::net::TcpStream;
use std::str::Split;

use aul::level::Level;
use aul::log;
use aul::sensitive::Sens;
use wbdl::Date;
use whdp::resp_presets::ok;
use whdp::{Request, Response};
use wjp::{map, Serialize, Values};

use crate::error::WBSLError;
use crate::middleware::Middleware;

pub type HTTPFunction = fn(Request) -> Response;
pub type RawFunction = fn(String) -> String;
pub type SerializableFunction<I, O> = fn(I) -> O;

pub(crate) fn parse_stream(stream: &TcpStream) -> Result<String, WBSLError> {
    let mut reader = BufReader::new(stream);
    let received: Vec<u8> = reader.fill_buf().map_err(|_err| WBSLError)?.to_vec();
    reader.consume(received.len());
    String::from_utf8(received).map_err(|_e| WBSLError)
}

#[derive(PartialOrd, PartialEq, Ord, Eq, Copy, Clone, Default, Hash, Debug)]
pub(crate) struct Logger(Level);

impl From<Level> for Logger {
    fn from(value: Level) -> Self {
        Self(value)
    }
}

unsafe impl Middleware for Logger {
    fn on_request(&self, req: Request) -> Request {
        log!(self.0, "Request: \n{}", Sens(&req));
        req
    }
    fn on_response(&self, resp: Response) -> Response {
        log!(self.0, "Response: \n{}", Sens(&resp));
        resp
    }
}

pub(crate) fn health(_: Request) -> Response {
    ok(&Health::default().json())
}

#[derive(Clone, PartialOrd, PartialEq, Ord, Eq, Debug, Hash)]
pub struct Health {
    active: bool,
    time: String,
    ram: Option<Ram>,
}

impl Serialize for Health {
    fn serialize(&self) -> Values {
        Values::Struct(map!(
            ("active", &self.active),
            ("time", &self.time),
            ("ram", &self.ram)
        ))
    }
}
#[derive(PartialOrd, PartialEq, Ord, Eq, Copy, Clone, Hash, Debug, Default)]
pub struct Ram {
    total: u64,
    free: u64,
}

impl Serialize for Ram {
    fn serialize(&self) -> Values {
        let total = (self.total as f64 / 1_000_000_f64).to_string() + " GB";
        let free = (self.free as f64 / 1_000_000_f64).to_string() + " GB";
        Values::Struct(map!(("total", &total), ("free", &free)))
    }
}

impl Default for Health {
    fn default() -> Self {
        Self {
            active: true,
            time: get_current_time(),
            ram: get_mem(),
        }
    }
}

fn get_mem() -> Option<Ram> {
    let s = read_to_string("/proc/meminfo").ok()?;
    let mut meminfo_hashmap = HashMap::new();
    for line in s.lines() {
        let mut split_line = line.split_whitespace();
        let label = split_line.next();
        let value = split_line.next();
        if value.is_some() && label.is_some() {
            let label = label.unwrap().split(':').next()?;
            let value = value.unwrap().parse::<u64>().ok()?;
            meminfo_hashmap.insert(label, value);
        }
    }
    let total = meminfo_hashmap.remove("MemTotal")?;
    let free = meminfo_hashmap.remove("MemFree")?;
    Some(Ram { free, total })
}

pub(crate) struct AdditionalHeaders(String, String);

impl From<(String, String)> for AdditionalHeaders {
    fn from(value: (String, String)) -> Self {
        Self(value.0, value.1)
    }
}

unsafe impl Middleware for AdditionalHeaders {
    fn on_request(&self, req: Request) -> Request {
        req
    }
    fn on_response(&self, mut resp: Response) -> Response {
        resp.try_insert((String::from("Server"), self.0.clone()));
        resp.try_insert((String::from("Content-Type"), self.1.clone()));
        resp.try_insert((String::from("Date"), get_current_time()));

        resp
    }
}

pub(crate) fn get_current_time() -> String {
    Date::now()
        .map(|s| s.to_string())
        .expect("Time went backwards")
}

pub fn query(str: &str) -> HashMap<&str, &str> {
    let split = str.split('?');
    let wtf = split.last().map(|func| func.split('&'));
    if let Some(w) = wtf {
        let mut map = HashMap::new();
        for e in w {
            let mut e = e.split('=');
            let key = e.next();
            let val = e.next();
            if let Some(key) = key {
                if let Some(val) = val {
                    map.insert(key, val);
                }
            }
        }
        return map;
    }
    HashMap::new()
}

pub(crate) fn remainder(mut split: Split<char>) -> Option<String> {
    let mut res = String::new();
    let mut next = split.next();
    while next.is_some() {
        res += next.unwrap();
        next = split.next()
    }
    Some(res)
}