1use std::collections::HashMap;
4
5#[derive(Debug, Clone)]
7pub struct Response {
8 status_code: u16,
9 headers: HashMap<String, String>,
10 body: Vec<u8>,
11}
12
13impl Response {
14 pub fn new(status_code: u16) -> Self {
16 Self {
17 status_code,
18 headers: HashMap::new(),
19 body: Vec::new(),
20 }
21 }
22
23 pub fn ok() -> Self {
25 Self::new(200)
26 }
27
28 pub fn not_found(message: &str) -> Self {
30 Self::new(404).with_text(message)
31 }
32
33 pub fn internal_error(message: &str) -> Self {
35 Self::new(500).with_text(message)
36 }
37
38 pub fn with_header(mut self, name: &str, value: &str) -> Self {
40 self.headers.insert(name.to_string(), value.to_string());
41 self
42 }
43
44 pub fn with_text(mut self, text: &str) -> Self {
46 self.body = text.as_bytes().to_vec();
47 self.headers.insert(
48 "Content-Type".to_string(),
49 "text/plain; charset=utf-8".to_string(),
50 );
51 self.headers
52 .insert("Content-Length".to_string(), self.body.len().to_string());
53 self
54 }
55
56 pub fn with_json(mut self, json_str: &str) -> Self {
58 self.body = json_str.as_bytes().to_vec();
59 self.headers
60 .insert("Content-Type".to_string(), "application/json".to_string());
61 self.headers
62 .insert("Content-Length".to_string(), self.body.len().to_string());
63 self
64 }
65
66 pub fn with_html(mut self, html: &str) -> Self {
68 self.body = html.as_bytes().to_vec();
69 self.headers.insert(
70 "Content-Type".to_string(),
71 "text/html; charset=utf-8".to_string(),
72 );
73 self.headers
74 .insert("Content-Length".to_string(), self.body.len().to_string());
75 self
76 }
77
78 pub fn with_binary(mut self, data: Vec<u8>, content_type: &str) -> Self {
80 self.body = data;
81 self.headers
82 .insert("Content-Type".to_string(), content_type.to_string());
83 self.headers
84 .insert("Content-Length".to_string(), self.body.len().to_string());
85 self
86 }
87
88 pub fn status_code(&self) -> u16 {
90 self.status_code
91 }
92
93 pub fn headers(&self) -> &HashMap<String, String> {
95 &self.headers
96 }
97
98 pub fn body(&self) -> &[u8] {
100 &self.body
101 }
102
103 pub fn to_http_response(&self) -> String {
105 let status_line = format!("HTTP/1.1 {} {}", self.status_code, self.status_text());
106 let mut headers_string = String::new();
107 for (key, value) in &self.headers {
108 headers_string.push_str(&format!("{}: {}\r\n", key, value));
109 }
110 let body_string = String::from_utf8_lossy(&self.body);
111
112 format!("{}\r\n{}\r\n{}", status_line, headers_string, body_string)
113 }
114
115 fn status_text(&self) -> &'static str {
117 match self.status_code {
118 200 => "OK",
119 201 => "Created",
120 204 => "No Content",
121 400 => "Bad Request",
122 401 => "Unauthorized",
123 403 => "Forbidden",
124 404 => "Not Found",
125 500 => "Internal Server Error",
126 _ => "Unknown",
127 }
128 }
129}
130
131impl Response {
133 pub fn json(data: &str) -> Self {
135 Self::ok().with_json(data)
136 }
137
138 pub fn text(text: &str) -> Self {
140 Self::ok().with_text(text)
141 }
142
143 pub fn html(html: &str) -> Self {
145 Self::ok().with_html(html)
146 }
147}