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