use std::str::FromStr; use crate::{version::Version, method::Method, uri::URI, header::{HeaderMap, Header}, error::HTTPError, parse::TryParse}; pub struct Request { pub method: Method, pub uri: URI, pub version: Version, pub headers: HeaderMap, pub body: Option } impl TryParse for Request { fn try_parse(s: impl Into) -> Result { let s = s.into(); let mut lines = s.lines(); let Some(head) = lines.next() else { return Err(HTTPError::MissingHeader) }; let mut head_parts = head.split_whitespace(); let err = HTTPError::InvalidHeader(s.clone()); let Some(method_str) = head_parts.next() else { return Err(err) }; let method = method_str.into(); let Some(uri_str) = head_parts.next() else { return Err(err) }; let uri = uri_str.try_into()?; let Some(version_str) = head_parts.next() else { return Err(err) }; let version = version_str.into(); let mut headers = HeaderMap::new(); loop { let Some(next) = lines.next() else { return Err(HTTPError::MissingHeaderBreak) }; if next.len() < 1 { break } let header = Header::try_parse(next)?; headers.insert(header); } let rest: String = lines.collect(); let body; if rest.len() < 1 { body = None } else { body = match T::from_str(&rest) { Ok(body) => Some(body), Err(_) => return Err(HTTPError::InvalidBody) } } Ok(Self { method, uri, version, headers, body }) } } impl ToString for Request { fn to_string(&self) -> String { let mut s = String::new(); s.push_str(&self.method.as_str()); s.push(' '); s.push_str(&self.uri.to_string()); s.push(' '); s.push_str(self.version.as_str()); s.push_str("\r\n"); let iter = self.headers.iter(); for headers in iter { for header in headers { s.push_str(header.name.as_str()); s.push_str(": "); s.push_str(header.value.as_str()); s.push_str("\r\n"); } } s.push_str("\r\n"); if let Some(body) = &self.body { s.push_str(&body.to_string()); } s } }