1use base64::{Engine, engine::general_purpose};
2use bytes::Bytes;
3use http_body_util::Full;
4use hyper::{
5 HeaderMap, Response,
6 header::{HeaderName, HeaderValue},
7};
8use serde::Serialize;
9use time::{Duration, OffsetDateTime, format_description::well_known::Rfc2822};
10
11pub struct ResponseWriter {
12 pub body: String,
13 pub headers: HeaderMap,
14 pub status: u16,
15 pub has_error: bool,
16}
17
18impl ResponseWriter {
19 pub fn new() -> Self {
20 Self {
21 body: "".into(),
22 headers: HeaderMap::new(),
23 status: 200,
24 has_error: false,
25 }
26 }
27
28 pub fn status(&mut self, status: u16) -> &mut Self {
29 self.status = status;
30 self
31 }
32
33 pub fn set_header(&mut self, key: &str, value: &str) -> &mut Self {
34 self.headers.insert(
35 HeaderName::from_bytes(key.as_bytes()).unwrap(),
36 HeaderValue::from_str(value).unwrap(),
37 );
38 self
39 }
40
41 pub fn get_header(&self, key: &str) -> Option<&HeaderValue> {
42 self.headers.get(key)
43 }
44
45 pub fn send(&mut self, body: &str) -> &mut Self {
46 self.body = body.into();
47 self
48 }
49
50 pub fn json<T: Serialize>(&mut self, data: &T) -> &mut Self {
51 match serde_json::to_string(data) {
52 Ok(body) => {
53 self.set_header("Content-Type", "application/json");
54 self.body = body;
55 }
56 Err(_) => {
57 self.set_header("Content-Type", "application/json");
58 self.body = r#"{"error":"Failed to serialize JSON"}"#.to_string();
59 self.status = 500;
60 }
61 }
62 self
63 }
64
65 pub fn html(&mut self, html: &str) -> &mut Self {
66 self.set_header("Content-Type", "text/html; charset=utf-8");
67 self.body = html.to_string();
68 self
69 }
70
71 pub fn bytes(&mut self, bytes: &[u8]) -> &mut Self {
72 let encoded = general_purpose::STANDARD.encode(bytes);
73 self.body = encoded;
74 self.set_header("Content-Type", "application/octet-stream");
75 self
76 }
77
78 pub fn error(&mut self, status: u16, msg: &str) -> &mut Self {
79 self.status = status;
80 self.body = msg.to_string();
81 self.has_error = true;
82 self
83 }
84
85 pub fn has_error(&self) -> bool {
86 self.has_error
87 }
88
89 pub fn cookie(
90 &mut self,
91 name: &str,
92 value: &str,
93 max_age: Option<i64>,
94 path: Option<&str>,
95 domain: Option<&str>,
96 secure: bool,
97 http_only: bool,
98 same_site: Option<&str>,
99 ) -> &mut Self {
100 let mut cookie = format!("{}={}", name, value);
101
102 if let Some(age) = max_age {
103 if let Ok(expires) =
104 (OffsetDateTime::now_utc() + Duration::seconds(age)).format(&Rfc2822)
105 {
106 cookie.push_str(&format!("; Max-Age={}; Expires={}", age, expires));
107 }
108 }
109
110 if let Some(p) = path {
111 cookie.push_str(&format!("; Path={}", p));
112 }
113
114 if let Some(d) = domain {
115 cookie.push_str(&format!("; Domain={}", d));
116 }
117
118 if secure {
119 cookie.push_str("; Secure");
120 }
121
122 if http_only {
123 cookie.push_str("; HttpOnly");
124 }
125
126 if let Some(same_site_val) = same_site {
127 cookie.push_str(&format!("; SameSite={}", same_site_val));
128 }
129
130 self.headers.append(
131 hyper::header::SET_COOKIE,
132 hyper::header::HeaderValue::from_str(&cookie).unwrap(),
133 );
134
135 self
136 }
137
138 pub fn into_response(self) -> Response<Full<Bytes>> {
139 let mut builder = Response::builder().status(self.status);
140
141 for (key, value) in self.headers.iter() {
142 builder = builder.header(key, value);
143 }
144
145 builder.body(Full::new(Bytes::from(self.body))).unwrap()
146 }
147}