http_rust/
httpresponse.rs1use std::collections::HashMap;
2use::std::io::{Result, Write};
3
4#[derive(Debug, PartialEq, Clone)]
5pub struct HttpResponse<'a>
6{
7 version: &'a str,
8 status_code: &'a str,
9 status_text: &'a str,
10 headers: Option<HashMap<&'a str, &'a str>>,
11 body: Option<String>,
12}
13
14impl<'a> Default for HttpResponse<'a>{
15 fn default () -> Self{
16 Self{
17 version: "HTTP/1.1".into(),
18 status_code: "200".into(),
19 status_text: "OK".into(),
20 headers: None,
21 body: None,
22 }
23 }
24}
25
26impl<'a> HttpResponse<'a>{
27 pub fn new(
28 status_code: &'a str,
29 headers: Option<HashMap<&'a str, &'a str>>,
30 body: Option<String>
31 ) -> HttpResponse<'a>{
32 let mut response: HttpResponse<'a> = HttpResponse::default();
33 if status_code != "200"
34 {
35 response.status_code = status_code.into();
36 };
37 response.headers = match &headers{
38 Some(_h) => headers,
39 None =>{
40 let mut h = HashMap::new();
41 h.insert("Content-Type", "text/html");
42 Some(h)
43 }
44 };
45
46 response.status_text = match response.status_code{
47
48 "200" => "OK".into(),
49 "400" => "Bad Request".into(),
50 "404" => "Not Found".into(),
51 "500" => "Internal Server Error".into(),
52 _ => "Not Found".into(),
53 };
54 response.body = body;
55 response
56 }
57
58 pub fn send_response(&self, write_stream:&mut impl Write) -> Result<()>
59 {
60 let res = self.clone();
61 let response_string:String = String::from(res);
62 let _ = write!(write_stream, "{}", response_string);
63 Ok(())
64 }
65
66 fn version(&self) -> &str
68 {
69 self.version
70 }
71 fn status_code(&self) -> &str
72 {
73 self.status_code
74 }
75 fn status_text(&self) -> &str
76 {
77 self.status_text
78 }
79 fn headers(&self)-> String
80 {
81 let map: HashMap<&str, &str> = self.headers.clone().unwrap();
82 let mut header_string:String = "".into();
83 for (k, v) in map.iter()
84 {
85 header_string = format!("{}{}:{}\r\n", header_string, k, v);
86 }
87 header_string
88 }
89 pub fn body(&self) ->&str
90 {
91 match &self.body{
92 Some(b) => b.as_str(),
93 None => ""
94 }
95 }
96}
97
98impl<'a> From <HttpResponse <'a>> for String{
99 fn from(res:HttpResponse) ->String
100 {
101 let res1 = res.clone();
102 format!("{} {} {}\r\n{}Content-Length: {}\r\n\r\n{}",
103 &res1.version(),
104 &res1.status_code(),
105 &res1.status_text(),
106 &res1.headers(),
107 &res.body.unwrap_or("".to_string()).len(),
108 &res1.body()
109 )
110 }
111}
112
113#[cfg(test)]
114mod tests{
115 use super::*;
116 #[test]
117 fn test_response_struct_creation_200()
118 {
119 let response_actual = HttpResponse::new(
120 "200",
121 None,
122 Some("Ifedayo is the best".into())
123 );
124 let response_expected = HttpResponse{
125 version: "HTTP/1.1",
126 status_code: "200",
127 status_text: "OK",
128 headers:{
129 let mut h = HashMap::new();
130 h.insert("Content-Type", "text/html");
131 Some(h)
132 },
133 body: Some("Ifedayo is the best".into())
134 };
135 assert_eq!(response_actual, response_expected)
136 }
137 #[test]
138 fn test_response_struct_creation_404()
139 {
140 let response_actual = HttpResponse::new(
141 "404",
142 None,
143 Some("Ifedayo is the best".into())
144 );
145 let response_expected = HttpResponse{
146 version: "HTTP/1.1",
147 status_code: "404",
148 status_text: "Not Found",
149 headers:{
150 let mut h = HashMap::new();
151 h.insert("Content-Type", "text/html");
152 Some(h)
153 },
154 body: Some("Ifedayo is the best".into())
155 };
156 assert_eq!(response_actual, response_expected)
157 }
158 #[test]
159 fn test_http_response_creation()
160 {
161 let response_expected = HttpResponse{
162 version: "HTTP/1.1",
163 status_code: "404",
164 status_text: "Not Found",
165 headers:{
166 let mut h = HashMap::new();
167 h.insert("Content-Type", "text/html");
168 Some(h)
169 },
170 body: Some("Ifedayo is the best".into())
171 };
172
173 let http_string:String = response_expected.into();
174 let response_actual = "HTTP/1.1 404 Not Found\r\nContent-Type:text/html\r\nContent-Length: 19\r\n\r\nIfedayo is the best";
175 assert_eq!(http_string, response_actual );
176 }
177
178}