use std::fmt; use std::str::FromStr; const HTTP_VERSION: &str = "HTTP/1.1"; // TODO use &str #[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, } impl Status { pub fn get_message(&self) -> &'static str { match self { Self::OK => "OK", Self::NOTFOUND => "NOT FOUND", } } } // TODO implement this with display and enums // TODO proper errors impl FromStr for Request { type Err = String; fn from_str(s: &str) -> Result { let mut iter = dbg!(s).lines(); let mut first = iter.nth(0).unwrap().split_whitespace(); let method = first.next().unwrap().to_string(); let uri = first.next().unwrap().to_string(); 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, uri, headers, body, }) } } 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.get_message(), headers, self.body, ) } }