velen 0.1.10

A simple package for starting a HTTP server
Documentation
use std::{
    collections::HashMap,
    io::{self, Read, Write},
    net::{TcpListener, TcpStream},
    sync::Arc,
    thread,
};

use crate::{
    models::server_models::{Request, Response, Server},
    utils::server_utils::{get_method_path_query, parse_body, parse_headers},
};

impl Server {
    pub fn listen(self, host: &str, port: i32, handler: fn(Result<i32, io::Error>) -> ()) {
        let addr = format!("{host}:{port}");
        let listener = TcpListener::bind(addr);

        match listener {
            Ok(connection) => {
                let shared_server = Arc::new(self);
                handler(Ok(port));
                for connection in connection.incoming() {
                    match connection {
                        Ok(stream) => {
                            let shared_server_clone = Arc::clone(&shared_server);
                            thread::spawn(move || {
                                println!(
                                    "New connection is being handled by thread: {:?}",
                                    thread::current().id()
                                );

                                shared_server_clone.handle_connection(stream);
                            });
                        }
                        Err(err) => {
                            println!("Error while handing connection {:?}", err);
                        }
                    }
                }
            }
            Err(err) => handler(Err(err)),
        }
    }
    pub fn get(&mut self, path: &str, handler: fn(Request, Response) -> ()) {
        self.request_table.insert(format!("GET:{path}"), handler);
    }
    pub fn post(&mut self, path: &str, handler: fn(Request, Response) -> ()) {
        self.request_table.insert(format!("POST:{path}"), handler);
    }
    pub fn put(&mut self, path: &str, handler: fn(Request, Response) -> ()) {
        self.request_table.insert(format!("PUT:{path}"), handler);
    }
    pub fn delete(&mut self, path: &str, handler: fn(Request, Response) -> ()) {
        self.request_table.insert(format!("DELETE:{path}"), handler);
    }
    pub fn head(&mut self, path: &str, handler: fn(Request, Response) -> ()) {
        self.request_table.insert(format!("HEAD:{path}"), handler);
    }
    pub fn patch(&mut self, path: &str, handler: fn(Request, Response) -> ()) {
        self.request_table.insert(format!("PATCH:{path}"), handler);
    }
    fn handle_connection(&self, mut stream: TcpStream) {
        let mut req_body = [0; 1024];
        stream.read(&mut req_body).unwrap();

        let request_body = &String::from_utf8(req_body.to_vec()).unwrap();
        let (method, path, query_map) = get_method_path_query(request_body);
        let request_payload = parse_body(request_body);
        let request_headers = parse_headers(request_body);
        let handler = self.request_table.get(&format!("{method}:{path}"));

        match handler {
            Some(api_handler) => {
                let req = Server::build_request(
                    method,
                    path,
                    request_payload,
                    query_map,
                    request_headers,
                );
                let res = Server::build_response(stream);

                api_handler(req, res);
            }
            None => {
                /* Handle 404 */
                stream
                    .write(
                        format!(
                            "{}\r\n{}\r\n\r\n{}",
                            "HTTP/1.1 404 NOT FOUND".to_string(),
                            "Content-Type: application/json".to_string(),
                            "{\"message\":\"Not found\"}".to_string()
                        )
                        .as_bytes(),
                    )
                    .unwrap();

                stream.flush().unwrap();
            }
        }
    }
    fn build_request(
        method: String,
        path: String,
        body: String,
        query_map: HashMap<String, String>,
        request_headers: HashMap<String, String>,
    ) -> Request {
        return Request {
            method,
            path,
            body,
            query_params: query_map,
            headers: request_headers,
        };
    }
    fn build_response(stream: TcpStream) -> Response {
        return Response { stream, code: 200 };
    }
}

impl Response {
    pub fn set_status_code(&mut self, code: i32) {
        self.stream
            .write(format!("HTTP/1.1 {}", code).to_string().as_bytes())
            .unwrap();
    }
    pub fn set_header(&mut self, name: &str, value: &str) {
        self.stream
            .write(format!("\r\n{}: {}", name, value).to_string().as_bytes())
            .unwrap();
    }
    pub fn send(&mut self, body: String) {
        self.stream
            .write(format!("\r\n\r\n{}", body).to_string().as_bytes())
            .unwrap();
        self.stream.flush().unwrap();
    }
}