1use hyper::{Body, Response, StatusCode, header};
4use mime_guess::from_path as get_mine_from_path;
5use serde::Serialize;
6use std::fs::read_to_string as read_file;
7
8pub trait IntoResponse {
10 fn into_response(self) -> Response<Body>;
11}
12
13impl IntoResponse for &'static str {
15 fn into_response(self) -> Response<Body> {
16 Response::builder()
17 .header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
18 .body(Body::from(self))
19 .unwrap()
20 }
21}
22
23impl IntoResponse for String {
25 fn into_response(self) -> Response<Body> {
26 Response::builder()
27 .header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
28 .body(Body::from(self))
29 .unwrap()
30 }
31}
32
33impl IntoResponse for Vec<u8> {
35 fn into_response(self) -> Response<Body> {
36 Response::builder().body(Body::from(self)).unwrap()
37 }
38}
39
40impl IntoResponse for () {
42 fn into_response(self) -> Response<Body> {
43 Response::builder()
44 .status(StatusCode::NO_CONTENT)
45 .body(Body::empty())
46 .unwrap()
47 }
48}
49
50impl<const N: usize> IntoResponse for ([(header::HeaderName, &'static str); N], String) {
52 fn into_response(self) -> Response<Body> {
53 let (headers, body) = self;
54 let mut builder = Response::builder();
55
56 for (name, value) in headers {
57 builder = builder.header(name, value);
58 }
59
60 builder.body(Body::from(body)).unwrap()
61 }
62}
63
64impl IntoResponse for StatusCode {
66 fn into_response(self) -> Response<Body> {
67 Response::builder()
68 .status(self)
69 .body(Body::empty())
70 .unwrap()
71 }
72}
73
74pub struct Json<T: Serialize>(pub T);
76
77impl<T: Serialize> IntoResponse for Json<T> {
78 fn into_response(self) -> Response<Body> {
79 let json = serde_json::to_string(&self.0).unwrap();
80 Response::builder()
81 .header(header::CONTENT_TYPE, "application/json")
82 .body(Body::from(json))
83 .unwrap()
84 }
85}
86
87pub struct Html(pub String);
89
90impl IntoResponse for Html {
91 fn into_response(self) -> Response<Body> {
92 Response::builder()
93 .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
94 .body(Body::from(self.0))
95 .unwrap()
96 }
97}
98
99pub struct Text(pub String);
101
102impl IntoResponse for Text {
103 fn into_response(self) -> Response<Body> {
104 Response::builder()
105 .header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
106 .body(Body::from(self.0))
107 .unwrap()
108 }
109}
110
111impl<T, E> IntoResponse for Result<T, E>
113where
114 T: IntoResponse,
115 E: IntoResponse,
116{
117 fn into_response(self) -> Response<Body> {
118 match self {
119 Ok(value) => value.into_response(),
120 Err(err) => err.into_response(),
121 }
122 }
123}
124
125pub struct Redirect(pub String);
127
128impl IntoResponse for Redirect {
129 fn into_response(self) -> Response<Body> {
130 Response::builder()
131 .status(302)
132 .header("Location", self.0)
133 .body(Body::empty())
134 .unwrap()
135 }
136}
137
138pub struct PermRedirect(pub String);
140
141impl IntoResponse for PermRedirect {
142 fn into_response(self) -> Response<Body> {
143 Response::builder()
144 .status(301)
145 .header("Location", self.0)
146 .body(Body::empty())
147 .unwrap()
148 }
149}
150
151impl Redirect {
152 pub fn perm(url: String) -> PermRedirect {
153 PermRedirect(url)
154 }
155}
156
157pub struct File(pub String, pub bool);
159
160impl IntoResponse for File {
161 fn into_response(self) -> Response<Body> {
162 Response::builder()
163 .header(
164 header::CONTENT_TYPE,
165 if self.1 {
166 "application/octet-stream; charset=utf-8".to_string()
167 } else {
168 format!(
169 "{}; charset=utf-8",
170 get_mine_from_path(&self.0)
171 .first_or_octet_stream()
172 .to_string()
173 )
174 },
175 )
176 .body(Body::from(read_file(self.0).unwrap_or(String::new())))
177 .unwrap()
178 }
179}