use std::collections::HashMap; use std::fmt; use std::str::{FromStr, Lines}; use crate::errors::RequestError; const HTTP_VERSION: &'static str = "HTTP/1.1"; #[derive(Debug)] pub struct Request { pub method: String, pub uri: String, pub headers: Vec<(String, String)>, pub body: String, } #[derive(Debug)] pub struct Response { pub status: Status, pub headers: Vec<(String, String)>, pub body: String, } #[derive(Debug, Clone, Copy)] pub enum Status { Ok = 200, NotFound = 404, BadRequest = 400, Unauthorized = 401, InternalServerError = 500, } impl Status { pub fn message(&self) -> &'static str { match self { Self::Ok => "OK", Self::NotFound => "NOT FOUND", Self::BadRequest => "BAD REQUEST", Self::Unauthorized => "UNAUTHORIZED", Self::InternalServerError => "INTERNAL SERVER ERROR", } } } impl Request { pub fn form(&self) -> Result, RequestError> { let mut hashmap = HashMap::new(); for mut x in self.body.split("&").map(|x| x.split("=")) { hashmap.insert( x.next().ok_or(RequestError::NotAForm)?, x.next().ok_or(RequestError::NotAForm)? ); } Ok(hashmap) } } impl FromStr for Request { type Err = RequestError; fn from_str(s: &str) -> Result { //dbg!(&s); let mut iter = s.lines(); let mut first = iter.next().ok_or(RequestError::BadRequest)? .split_whitespace(); let method = first.next().ok_or(RequestError::BadRequest)?; let uri = first.next().ok_or(RequestError::BadRequest)?; let mut headers: Vec<(String, String)> = vec![]; for line in &mut iter { if line.is_empty() { break; } let line: Vec<&str> = line.split(":").take(2).collect(); headers.push((line[0].into(), line[1].into())); } let body: String = iter.fold(String::new(), |a, b| format!("{}{}", a, b)); Ok(Self { method: method.to_string(), uri: uri.to_string(), headers: headers, body: body.to_string(), }) } } impl Response { pub fn new(status: Status, headers: Vec<(String, String)>, body: String) -> Self { Self { status, headers, body, } } } impl fmt::Display for Response { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let headers: String = self .headers .iter() .map(|x| format!("{}:{}", x.0, x.1)) .fold(String::from(""), |a, b| format!("{}\n{}", a, b)); // possibly slow write!( f, "{} {} {}\n{}\n\n{}", HTTP_VERSION, self.status as i32, self.status.message(), headers, self.body, ) } }