http_server_tiny 0.1.2

HTTP SERVER
Documentation
use std::{collections::HashMap, fs::File, net::ToSocketAddrs};
pub use tiny_http::Method;
use tiny_http::{Header, Request, Response, Server};

pub fn handle_file<'a>(file_name: &'a str, ct: &'a str, sc: u32) -> Response<File> {
    Response::from_file(File::open(file_name).unwrap())
        .with_header(Header::from_bytes("Content-Type", ct).unwrap())
        .with_status_code(sc)
}

pub struct RequestInside<'a> {
    pub header: &'a [Header],
    pub method: &'a Method,
    pub content: String,
    pub url: &'a str,
}

pub enum Res<'a> {
    Json(String),
    File { name: &'a str, ct: &'a str, sc: u32 },
    Empty,
}

impl<'a> RequestInside<'a> {
    fn new(req: &'a mut Request) -> Self {
        let mut content = String::new();
        req.as_reader().read_to_string(&mut content).unwrap();
        Self {
            header: req.headers(),
            content,
            method: req.method(),
            url: req.url(),
        }
    }
}

pub struct HttpServer<'a> {
    server: Server,
    error_path: &'a str,
    pub routes: HashMap<(&'a Method, &'a str), Box<dyn Fn(RequestInside) -> Res<'a>>>,
}

impl<'a> HttpServer<'a> {
    pub fn new<S>(addr: S, error_path: &'a str) -> Self
    where
        S: ToSocketAddrs,
    {
        Self {
            server: Server::http(addr).unwrap(),
            error_path,
            routes: HashMap::new(),
        }
    }
    pub fn add_route(
        &mut self,
        method: &'a Method,
        url: &'a str,
        f: Box<dyn Fn(RequestInside) -> Res<'a>>,
    ) {
        self.routes.insert((method, url), f);
    }

    pub fn handle_requests(&self) -> Result<(), Box<dyn std::error::Error>> {
        for mut request in self.server.incoming_requests() {
            let req = RequestInside::new(&mut request);
            let result = self.routes.get(&(req.method, req.url));

            if let Some(func) = result {
                let res = func(req);
                let _ = match res {
                    Res::Json(content) => request.respond(
                        Response::from_string(content)
                            .with_status_code(200)
                            .with_header(
                                Header::from_bytes("Content-Type", "application/json").unwrap(),
                            ),
                    ),
                    Res::File { name, ct, sc } => request.respond(handle_file(name, ct, sc)),
                    Res::Empty => request.respond(Response::empty(200)),
                };
            } else {
                request.respond(handle_file(
                    self.error_path,
                    "text/html; charset=utf-8",
                    404,
                ))?;
            }
        }

        Ok(())
    }
}