thruster 1.3.13

A middleware based http async web server.
Documentation
use bytes::{BufMut, BytesMut};
use std::collections::HashMap;

use crate::app::App;

use crate::core::context::Context;
use crate::core::request::decode;
use crate::core::request::Request;
use crate::core::response::{Response, StatusMessage};

pub async fn request<T: Context<Response = Response> + Clone + Send + Sync, S: 'static + Send>(
    app: &App<Request, T, S>,
    method: &str,
    route: &str,
    headers: &[(&str, &str)],
    body: &str,
) -> TestResponse {
    let headers_mapped: Vec<String> = headers
        .iter()
        .map(|val| format!("{}: {}", val.0, val.1))
        .collect();
    let headers = headers_mapped.join("\n");
    let body = format!(
        "{} {} HTTP/1.1\nHost: localhost:8080\n{}\n\n{}",
        method,
        route.to_owned(),
        headers,
        body
    );

    let mut bytes = BytesMut::with_capacity(body.len());
    bytes.put(body.as_bytes());

    let request = decode(&mut bytes).unwrap().unwrap();
    let matched_route = app.resolve_from_method_and_path("GET", route.to_owned());
    let response = app.resolve(request, matched_route).await.unwrap();

    TestResponse::new(response)
}

pub async fn get<T: Context<Response = Response> + Clone + Send + Sync, S: 'static + Send>(
    app: &App<Request, T, S>,
    route: &str,
) -> TestResponse {
    let body = format!(
        "GET {} HTTP/1.1\nHost: localhost:8080\n\n",
        route.to_owned()
    );

    let mut bytes = BytesMut::with_capacity(body.len());
    bytes.put(body.as_bytes());

    let request = decode(&mut bytes).unwrap().unwrap();
    let matched_route = app.resolve_from_method_and_path("GET", route.to_owned());
    let response = app.resolve(request, matched_route).await.unwrap();

    TestResponse::new(response)
}

pub async fn delete<T: Context<Response = Response> + Clone + Send + Sync, S: 'static + Send>(
    app: &App<Request, T, S>,
    route: &str,
) -> TestResponse {
    let body = format!(
        "DELETE {} HTTP/1.1\nHost: localhost:8080\n\n",
        route.to_owned()
    );

    let mut bytes = BytesMut::with_capacity(body.len());
    bytes.put(body.as_bytes());

    let request = decode(&mut bytes).unwrap().unwrap();
    let matched_route = app.resolve_from_method_and_path("DELETE", route.to_owned());
    let response = app.resolve(request, matched_route).await.unwrap();

    TestResponse::new(response)
}

pub async fn post<T: Context<Response = Response> + Clone + Send + Sync, S: 'static + Send>(
    app: &App<Request, T, S>,
    route: &str,
    content: &str,
) -> TestResponse {
    let body = format!(
        "POST {} HTTP/1.1\nHost: localhost:8080\nContent-Length: {}\n\n{}",
        route,
        content.len(),
        content
    );

    let mut bytes = BytesMut::with_capacity(body.len());
    bytes.put(body.as_bytes());

    let request = decode(&mut bytes).unwrap().unwrap();
    let matched_route = app.resolve_from_method_and_path("POST", route.to_owned());
    let response = app.resolve(request, matched_route).await.unwrap();

    TestResponse::new(response)
}

pub async fn put<T: Context<Response = Response> + Clone + Send + Sync, S: 'static + Send>(
    app: &App<Request, T, S>,
    route: &str,
    content: &str,
) -> TestResponse {
    let body = format!(
        "PUT {} HTTP/1.1\nHost: localhost:8080\nContent-Length: {}\n\n{}",
        route,
        content.len(),
        content
    );

    let mut bytes = BytesMut::with_capacity(body.len());
    bytes.put(body.as_bytes());

    let request = decode(&mut bytes).unwrap().unwrap();
    let matched_route = app.resolve_from_method_and_path("PUT", route.to_owned());
    let response = app.resolve(request, matched_route).await.unwrap();

    TestResponse::new(response)
}

pub async fn patch<T: Context<Response = Response> + Clone + Send + Sync, S: 'static + Send>(
    app: &App<Request, T, S>,
    route: &str,
    content: &str,
) -> TestResponse {
    let body = format!(
        "PATCH {} HTTP/1.1\nHost: localhost:8080\nContent-Length: {}\n\n{}",
        route,
        content.len(),
        content
    );

    let mut bytes = BytesMut::with_capacity(body.len());
    bytes.put(body.as_bytes());

    let request = decode(&mut bytes).unwrap().unwrap();
    let matched_route = app.resolve_from_method_and_path("PATCH", route.to_owned());
    let response = app.resolve(request, matched_route).await.unwrap();

    TestResponse::new(response)
}

#[derive(Debug)]
pub struct TestResponse {
    pub body: String,
    pub headers: HashMap<String, String>,
    pub status: (String, u32),
}

impl TestResponse {
    fn new(response: Response) -> TestResponse {
        let mut headers = HashMap::new();
        let header_string = String::from_utf8(response.header_raw.to_vec()).unwrap();

        for header_pair in header_string.split("\r\n") {
            if !header_pair.is_empty() {
                let mut split = header_pair.split(':');
                let key = split.next().unwrap().trim().to_owned();
                let value = split.next().unwrap().trim().to_owned();

                headers.insert(key, value);
            }
        }

        TestResponse {
            body: String::from_utf8(response.response).unwrap(),
            headers,
            status: match response.status_message {
                StatusMessage::Ok => ("Ok".to_owned(), 200),
                StatusMessage::Custom(code, message) => (message, code),
            },
        }
    }
}