raw
1use axum::{
2 http::{HeaderValue, StatusCode, header},
3 response::IntoResponse,
4};
5
6pub struct Css<T>(pub T);
7
8impl<T: IntoResponse> IntoResponse for Css<T> {
9 fn into_response(self) -> axum::response::Response {
10 (
11 [
12 (
13 header::CONTENT_TYPE,
14 HeaderValue::from_static(mime::TEXT_CSS_UTF_8.as_ref()),
15 ),
16 (
17 header::CACHE_CONTROL,
18 HeaderValue::from_static("max-age=31536000, immutable"),
19 ),
20 ],
21 self.0,
22 )
23 .into_response()
24 }
25}
26
27pub struct Ico<T>(pub T);
28
29impl<T: IntoResponse> IntoResponse for Ico<T> {
30 fn into_response(self) -> axum::response::Response {
31 (
32 [
33 (
34 header::CONTENT_TYPE,
35 HeaderValue::from_static("image/x-icon"),
36 ),
37 (
38 header::CACHE_CONTROL,
39 HeaderValue::from_static("max-age=31536000, immutable"),
40 ),
41 ],
42 self.0,
43 )
44 .into_response()
45 }
46}
47
48pub struct Json<T>(pub T);
49
50impl<T: IntoResponse> IntoResponse for Json<T> {
51 fn into_response(self) -> axum::response::Response {
52 (
53 [
54 (
55 header::CONTENT_TYPE,
56 HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()),
57 ),
58 (
59 header::CACHE_CONTROL,
60 HeaderValue::from_static("max-age=31536000, immutable"),
61 ),
62 ],
63 self.0,
64 )
65 .into_response()
66 }
67}
68
69pub struct Png<T>(pub T);
70
71impl<T: IntoResponse> IntoResponse for Png<T> {
72 fn into_response(self) -> axum::response::Response {
73 (
74 [
75 (
76 header::CONTENT_TYPE,
77 HeaderValue::from_static(mime::IMAGE_PNG.as_ref()),
78 ),
79 (
80 header::CACHE_CONTROL,
81 HeaderValue::from_static("max-age=31536000, immutable"),
82 ),
83 ],
84 self.0,
85 )
86 .into_response()
87 }
88}
89
90pub struct Text<T>(pub T);
91
92impl<T: IntoResponse> IntoResponse for Text<T> {
93 fn into_response(self) -> axum::response::Response {
94 (
95 [
96 (
97 header::CONTENT_TYPE,
98 HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
99 ),
100 (
101 header::CACHE_CONTROL,
102 HeaderValue::from_static("max-age=300, private"),
103 ),
104 ],
105 self.0,
106 )
107 .into_response()
108 }
109}
110
111pub struct Html<T: askama::Template>(pub T);
112
113impl<T: askama::Template> IntoResponse for Html<T> {
114 fn into_response(self) -> axum::response::Response {
115 match self.0.render() {
116 Ok(rendered) => (
117 [
118 (
119 header::CONTENT_TYPE,
120 HeaderValue::from_static(mime::TEXT_HTML_UTF_8.as_ref()),
121 ),
122 (
123 header::CACHE_CONTROL,
124 HeaderValue::from_static("max-age=300, private"),
125 ),
126 ],
127 rendered,
128 )
129 .into_response(),
130 Err(err) => {
131 tracing::error!(err=?err, "failed to render html response");
132
133 (
134 StatusCode::INTERNAL_SERVER_ERROR,
135 [(
136 header::CONTENT_TYPE,
137 HeaderValue::from_static(mime::TEXT_HTML_UTF_8.as_ref()),
138 )],
139 "a serious error has occured",
140 )
141 .into_response()
142 }
143 }
144 }
145}
146
147pub struct Xml<T: askama::Template>(pub T);
148
149impl<T: askama::Template> IntoResponse for Xml<T> {
150 fn into_response(self) -> axum::response::Response {
151 match self.0.render() {
152 Ok(rendered) => (
153 [
154 (
155 header::CONTENT_TYPE,
156 HeaderValue::from_static(mime::TEXT_XML.as_ref()),
157 ),
158 (
159 header::CACHE_CONTROL,
160 HeaderValue::from_static("max-age=300, private"),
161 ),
162 ],
163 rendered,
164 )
165 .into_response(),
166 Err(err) => {
167 tracing::error!(err=?err, "failed to render xml response");
168
169 (
170 StatusCode::INTERNAL_SERVER_ERROR,
171 [(
172 header::CONTENT_TYPE,
173 HeaderValue::from_static(mime::TEXT_HTML_UTF_8.as_ref()),
174 )],
175 "a serious error has occured",
176 )
177 .into_response()
178 }
179 }
180 }
181}
182
183#[must_use = "needs to be returned from a handler or otherwise turned into a Response to be useful"]
184#[derive(Debug, Clone)]
185pub struct Redirect {
186 status_code: StatusCode,
187 location: HeaderValue,
188}
189
190impl Redirect {
191 pub const PERMANENT_ROOT: Self = Self {
192 status_code: StatusCode::PERMANENT_REDIRECT,
193 location: HeaderValue::from_static("/"),
194 };
195 pub const TEMPORARY_ROOT: Self = Self {
196 status_code: StatusCode::TEMPORARY_REDIRECT,
197 location: HeaderValue::from_static("/"),
198 };
199
200 #[tracing::instrument(skip_all)]
201 pub fn to(uri: &str) -> Option<Self> {
202 Self::with_status_code(StatusCode::SEE_OTHER, uri)
203 }
204
205 #[tracing::instrument(skip_all)]
206 pub fn temporary(uri: &str) -> Option<Self> {
207 Self::with_status_code(StatusCode::TEMPORARY_REDIRECT, uri)
208 }
209
210 #[tracing::instrument(skip_all)]
211 pub fn permanent(uri: &str) -> Option<Self> {
212 Self::with_status_code(StatusCode::PERMANENT_REDIRECT, uri)
213 }
214
215 #[tracing::instrument(skip_all)]
216 fn with_status_code(status_code: StatusCode, uri: &str) -> Option<Self> {
217 assert!(
218 status_code.is_redirection(),
219 "not a redirection status code"
220 );
221
222 let location = match HeaderValue::try_from(uri) {
223 Ok(location) => location,
224 Err(err) => {
225 tracing::error!(err=?err, "failed to convert uri to header");
226
227 return None;
228 }
229 };
230
231 Some(Self {
232 status_code,
233 location,
234 })
235 }
236}
237
238impl IntoResponse for Redirect {
239 fn into_response(self) -> axum::response::Response {
240 (self.status_code, [(header::LOCATION, self.location)]).into_response()
241 }
242}
243