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
61 .body(Body::from(body))
62 .unwrap()
63 }
64}
65
66impl IntoResponse for StatusCode {
68 fn into_response(self) -> Response<Body> {
69 Response::builder()
70 .status(self)
71 .body(Body::empty())
72 .unwrap()
73 }
74}
75
76pub struct Json<T: Serialize>(pub T);
78
79impl<T: Serialize> IntoResponse for Json<T> {
80 fn into_response(self) -> Response<Body> {
81 let json = serde_json::to_string(&self.0).unwrap();
82 Response::builder()
83 .header(header::CONTENT_TYPE, "application/json")
84 .body(Body::from(json))
85 .unwrap()
86 }
87}
88
89pub struct Html(pub String);
91
92impl IntoResponse for Html {
93 fn into_response(self) -> Response<Body> {
94 Response::builder()
95 .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
96 .body(Body::from(self.0))
97 .unwrap()
98 }
99}
100
101pub struct Text(pub String);
103
104impl IntoResponse for Text {
105 fn into_response(self) -> Response<Body> {
106 Response::builder()
107 .header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
108 .body(Body::from(self.0))
109 .unwrap()
110 }
111}
112
113impl<T, E> IntoResponse for Result<T, E>
115where
116 T: IntoResponse,
117 E: IntoResponse,
118{
119 fn into_response(self) -> Response<Body> {
120 match self {
121 Ok(value) => value.into_response(),
122 Err(err) => err.into_response(),
123 }
124 }
125}
126
127pub struct Redirect(pub String);
129
130impl IntoResponse for Redirect {
131 fn into_response(self) -> Response<Body> {
132 Response::builder()
133 .status(302)
134 .header("Location", self.0)
135 .body(Body::empty())
136 .unwrap()
137 }
138}
139
140pub struct PermRedirect(pub String);
142
143impl IntoResponse for PermRedirect {
144 fn into_response(self) -> Response<Body> {
145 Response::builder()
146 .status(301)
147 .header("Location", self.0)
148 .body(Body::empty())
149 .unwrap()
150 }
151}
152
153impl Redirect {
154 pub fn perm(url: String) -> PermRedirect {
155 PermRedirect(url)
156 }
157}
158
159pub struct File(pub String, pub bool);
161
162impl IntoResponse for File {
163 fn into_response(self) -> Response<Body> {
164 Response::builder()
165 .header(
166 header::CONTENT_TYPE,
167 if self.1 {
168 "application/octet-stream; charset=utf-8".to_string()
169 } else {
170 format!(
171 "{}; charset=utf-8",
172 get_mine_from_path(&self.0)
173 .first_or_octet_stream()
174 .to_string()
175 )
176 },
177 )
178 .body(Body::from(read_file(self.0).unwrap_or(String::new())))
179 .unwrap()
180 }
181}