netter 0.2.1

Netter is a CLI tool for fast and easy server startup!
use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
use hyper::{body::{self, Bytes}, header::HeaderName, server::conn::http1, service::service_fn, Request, Response, StatusCode};
use serde::{Deserialize, Serialize};
use std::fs;
use crate::state;

#[derive(Debug)]
pub struct Routes {
    method: String,
    path: String,
    response: Resp,
}

#[derive(Debug)]
pub struct Resp {
    body: String,
    headers: Vec<(String, String)>,
    status: u16,
}

#[derive(Debug)]
pub struct Server {
    addr: Vec<u16>,
    port: u16,
    routes: Vec<Routes>,
}

pub trait HTTP {
    fn new(addr: Vec<u16>, port: u16, routes: Routes) -> Self;
    async fn start(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
    //async fn handler(req: Request<body::Incoming>)
                //-> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error>;
}

#[derive(Serialize, Deserialize)]
pub struct Config {
    addr: Vec<u16>,
    port: u16,
    routes: Vec<RouteConfig>,
}

#[derive(Serialize, Deserialize)]
pub struct RouteConfig {
    method: String,
    path: String,
    response: RespConfig,
}

#[derive(Serialize, Deserialize)]
pub struct RespConfig {
    body: String,
    headers: Vec<(String, String)>,
    status: u16,
}

impl Server {
    pub fn from_config_file(file_path: &str) -> Result<Self, Box<dyn std::error::Error>> {
        let config_data = fs::read_to_string(file_path)?;

        println!("Read config data");

        let config: Config = serde_json::from_str(&config_data)?;

        let routes = config.routes.into_iter().map(|route| Routes {
            method: route.method,
            path: route.path,
            response: Resp {
                body: route.response.body,
                headers: route.response.headers,
                status: route.response.status,
            },
        }).collect();

        Ok(Server {
            addr: config.addr,
            port: config.port,
            routes,
        })
    }
}

async fn handler(req: Request<body::Incoming>)
            -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
    let routes = Server::from_config_file("routes.json")
                .unwrap();
    println!("routes: {:?}", &routes);
    for route in routes.routes {
        if req.method().as_str() == route.method && req.uri().path() == route.path {
            let mut response = Response::new(full(route.response.body.clone()));
            *response.status_mut() = StatusCode::from_u16(route.response.status).unwrap();
            for (key, value) in &route.response.headers {
                response.headers_mut().insert(
                    key.parse::<HeaderName>().unwrap(),
                    value.parse().unwrap(),
                );
            }
            return Ok(response);
        }
    }
    // Если маршрут не найден, возвращаем 404
    let mut not_found = Response::new(empty());
    *not_found.status_mut() = StatusCode::NOT_FOUND;
    Ok(not_found)
}

impl HTTP for Server {
    
    fn new(addr: Vec<u16>, port: u16, routes: Routes) -> Self {
        Server {
            addr,
            port,
            routes: vec![routes],
        }
    }

    async fn start(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let addr = format!(
            "{}:{}",
            self.addr.iter().map(|n| n.to_string()).collect::<Vec<_>>().join("."),
            self.port
        );
        let listener = tokio::net::TcpListener::bind(&addr).await?;
        println!("Server started on {}", addr);

        state::save_state(String::from("HTTP"), self.addr.clone().iter().map(|n| n.to_string()).collect::<Vec<_>>().join("."), self.port.clone())
            .map_err(|_| "Failed to save state")?;

        loop {
            let (socket, _) = listener.accept().await?;
            let io = hyper_util::rt::TokioIo::new(socket);
            tokio::task::spawn(async move {
                if let Err(err) = http1::Builder::new()
                    .serve_connection(io, service_fn(handler))
                    .await
                {
                    println!("Error serving connection: {}", err);
                }
            });
        }
    }
}

fn empty() -> BoxBody<Bytes, hyper::Error> {
    Empty::<Bytes>::new()
        .map_err(|never| match never {})
        .boxed()
}
fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, hyper::Error> {
    Full::new(chunk.into())
        .map_err(|never| match never {})
        .boxed()
}