1use std::collections::HashMap;
13
14use crate::status::{HttpStatus, ServerErrorResponse};
15
16pub trait Response {
18 fn to_response(&self) -> HttpResponse;
19}
20
21impl Response for &str {
22 fn to_response(&self) -> HttpResponse {
23 HttpResponse::new_body((*self).to_string(), HttpStatus::default())
24 }
25}
26
27impl Response for String {
28 fn to_response(&self) -> HttpResponse {
29 HttpResponse::new_body(self.clone(), HttpStatus::default())
30 }
31}
32
33impl<S: Response> Response for Option<S> {
34 fn to_response(&self) -> HttpResponse {
35 match self {
36 Some(e) => e.to_response(),
37 None => HttpResponse::new().set_status(HttpStatus::ServerError(
38 ServerErrorResponse::InternalServerError,
39 )),
40 }
41 }
42}
43
44impl<S: Response, E: Response> Response for Result<S, E> {
45 fn to_response(&self) -> HttpResponse {
46 match self {
47 Ok(s) => s.to_response().set_status(HttpStatus::default()),
48 Err(e) => e
49 .to_response()
50 .set_status(ServerErrorResponse::InternalServerError.into()),
51 }
52 }
53}
54
55impl Response for HttpResponse {
56 fn to_response(&self) -> HttpResponse {
58 self.clone()
59 }
60}
61
62#[derive(Eq, PartialEq, Clone, Debug, Default)]
64pub struct HttpResponse {
65 pub headers: HashMap<String, String>,
66 pub status: HttpStatus,
67 pub body: Option<String>,
68}
69
70impl HttpResponse {
71 #[must_use]
72 pub fn new_body(body: String, status: HttpStatus) -> Self {
73 let mut headers: HashMap<String, String> = HashMap::new();
74 headers.insert("Content-Length".into(), body.chars().count().to_string());
75 Self {
76 headers,
77 status,
78 body: Some(body),
79 }
80 }
81
82 #[must_use]
83 pub fn insert_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
84 self.headers.insert(key.into(), value.into());
85 self
86 }
87
88 #[must_use]
89 pub fn set_body(mut self, body: impl Into<String>) -> Self {
90 let body = body.into();
91 let body_len = body.chars().count();
92 self.body.replace(body);
93 self.headers
94 .insert("Content-Length".into(), body_len.to_string());
95 self
96 }
97
98 #[must_use]
99 pub fn set_status(mut self, status: HttpStatus) -> Self {
100 self.status = status;
101 self
102 }
103
104 #[must_use]
105 pub fn new() -> Self {
106 Self {
107 headers: HashMap::new(),
108 status: HttpStatus::default(),
109 body: None,
110 }
111 }
112
113 pub(crate) fn into_bytes(self) -> Vec<u8> {
114 self.into_string().into_bytes()
115 }
116
117 pub(crate) fn into_string(self) -> String {
118 use std::fmt::Write;
119
120 let headers = self.headers.iter().fold(String::new(), |mut acc, (k, v)| {
121 acc.write_fmt(core::format_args!("{k}: {v}\r\n"))
122 .expect("if this fails fuck clippy");
123 acc
124 });
125
126 format!(
127 "HTTP/1.1 {}\r\n{headers}\r\n{}",
128 self.status,
129 self.body.unwrap_or_default()
130 )
131 }
132}