use std::fmt; use std::str::{FromStr, Lines}; 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, } impl Status { pub fn message(&self) -> &'static str { match self { Self::Ok => "OK", Self::NotFound => "NOT FOUND", } } } // TODO proper errors impl FromStr for Request { type Err = String; fn from_str(s: &str) -> Result { let mut iter = s.lines(); let mut first = iter.next().unwrap().split_whitespace(); let method = first.next().unwrap(); let uri = first.next().unwrap(); 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, ) } }