rocket_response/lib.rs
1//! # rocket-response - Provides enum for variable Rocket Responses
2//!
3//! This [crate] provides 3 enums to be flexible in returning [Responses].
4//!
5//! 1. [RocketResponse] provides all non-generic [Response] types.
6//! 2. [RocketResponseGeneric] provides [Response]-types non-generic
7//! and generic using a single generic type.
8//! 3. [RocketResponseGeneric2] allows a different [Flash](rocket::response::Flash) type.
9//!
10//! If you miss any [Response], you are welcome to open an [issue]
11//! or even better provide a pull-request!
12//!
13//! Because it is difficult to decide on the generics what might be useful, your usecases are really
14//! welcome in an [issue].
15//!
16//! ## Usage
17//!
18//! For usage add the crate to your dependencies
19//!
20//! ```toml
21//! [dependencies]
22//! rocket-response = { version = "0.0.1-rc.2" }
23//! ```
24//!
25//! ## Features
26//!
27//! You can depend on a couple of features, which provide additional types.
28//!
29//! * json
30//! * msgpack
31//! * templates-handlebars or templates-tera
32//!
33//! ```toml
34//! [dependencies]
35//! rocket-response = { version = "0.0.1-rc.2", features = ["json", "templates-tera"] }
36//! ```
37//!
38//!
39//! [Response]: rocket::response::Response
40//! [Responses]: rocket::response::Response
41//! [issue]: https://github.com/kolbma/rocket-response/issues
42
43#![deny(unsafe_code)]
44#![deny(warnings)]
45#![deny(clippy::all)]
46#![deny(missing_docs)]
47#![deny(rustdoc::missing_doc_code_examples)]
48
49#[cfg(any(feature = "json", feature = "msgpack"))]
50use rocket::serde;
51use rocket::{
52 fs::NamedFile,
53 http::Status,
54 response::{
55 content::{RawCss, RawHtml, RawJavaScript, RawJson, RawMsgPack, RawText, RawXml},
56 status::{
57 Accepted, BadRequest, Conflict, Created, Forbidden, NoContent, NotFound, Unauthorized,
58 },
59 Flash, Redirect,
60 },
61 serde::Serialize,
62 tokio, Responder,
63};
64#[cfg(any(feature = "templates-tera", feature = "templates-handlebars"))]
65use rocket_dyn_templates::Template;
66use std::fs::File;
67
68/// The non-generic [Responses](rocket::response::Response).
69///
70/// ## Example usage
71///
72/// ```rust
73/// use rocket::{get, response::{self, Redirect}};
74/// use rocket_response::RocketResponse;
75///
76/// #[get("/<id>")]
77/// pub(crate) fn route_example(id: usize) -> RocketResponse {
78/// match id {
79/// 0 => RocketResponse::NoContent(response::status::NoContent),
80/// 1 => RocketResponse::Redirect(Redirect::to("/admin")),
81/// _ => RocketResponse::StaticStr("Hello world"),
82/// }
83/// }
84/// ```
85#[derive(Responder)]
86pub enum RocketResponse {
87 /// see [rocket::response::status::Accepted]
88 Accepted(Accepted<&'static str>),
89 /// see [rocket::response::status::BadRequest]
90 BadRequest(BadRequest<&'static str>),
91 /// see [rocket::response::status::Conflict]
92 Conflict(Conflict<&'static str>),
93 /// see [rocket::response::status::Created]
94 Created(Created<&'static str>),
95 /// see [rocket::response::content::RawCss]
96 Css(RawCss<&'static str>),
97 /// see [File]
98 File(File),
99 /// see [rocket::response::Flash]
100 Flash(Flash<&'static str>),
101 /// see [rocket::response::status::Forbidden]
102 Forbidden(Forbidden<&'static str>),
103 /// see [rocket::response::content::RawHtml]
104 Html(RawHtml<&'static str>),
105 /// see [rocket::response::content::RawJavaScript]
106 JavaScript(RawJavaScript<&'static str>),
107 /// see [rocket::response::content::RawJson]
108 Json(RawJson<&'static str>),
109 /// see [rocket::response::content::RawMsgPack]
110 MsgPack(RawMsgPack<&'static str>),
111 /// see [NamedFile](rocket::fs::NamedFile)
112 NamedFiled(NamedFile),
113 /// see [rocket::response::status::NotFound]
114 NotFound(NotFound<&'static str>),
115 /// see [NoContent](rocket::response::status::NoContent)
116 NoContent(NoContent),
117 /// see [rocket::response::content::RawText]
118 Plain(RawText<&'static str>),
119 /// see [Redirect](rocket::response::Redirect)
120 Redirect(Redirect),
121
122 #[cfg(feature = "json")]
123 /// see [rocket::serde::json::Json]
124 SerdeJson(serde::json::Json<&'static str>),
125 #[cfg(feature = "msgpack")]
126 /// see [rocket::serde::msgpack::MsgPack]
127 SerdeMsgPack(serde::msgpack::MsgPack<&'static str>),
128 #[cfg(feature = "json")]
129 /// see [Value](rocket::serde::json::Value)
130 SerdeValue(serde::json::Value),
131
132 /// see [slice](std::slice)
133 StaticSlice(&'static [u8]),
134 /// see [str]
135 StaticStr(&'static str),
136 /// see [String]
137 String(String),
138 /// see [Status](rocket::http::Status)
139 Status(Status),
140
141 #[cfg(any(feature = "templates-tera", feature = "templates-handlebars"))]
142 /// see [Template](rocket_dyn_templates::Template)
143 Template(Template),
144
145 /// see [File](rocket::tokio::fs::File)
146 TokioFile(tokio::fs::File),
147 /// see [Unauthorized](rocket::response::status::Unauthorized)
148 Unauthorized(Unauthorized<&'static str>),
149 /// see [Vec](std::vec::Vec)
150 Vec(Vec<u8>),
151 /// see [Xml](rocket::response::content::RawXml)
152 Xml(RawXml<&'static str>),
153}
154
155/// The non-generic and generic [Responses](rocket::response::Response) with a single type.
156///
157/// ## Example usage
158///
159/// ```rust
160/// use rocket::{get, response::{self, status, Redirect}};
161/// use rocket_response::RocketResponseGeneric as RocketResponse;
162///
163/// #[get("/<id>")]
164/// pub(crate) fn route_response_generic(id: usize) -> RocketResponse<&'static str> {
165/// match id {
166/// 0 => RocketResponse::NoContent(status::NoContent),
167/// 1 => RocketResponse::Unauthorized(status::Unauthorized(Some(
168/// "admin need authentication",
169/// ))),
170/// _ => RocketResponse::Html(response::content::RawHtml(
171/// "<html><body>Hello world</body></html",
172/// )),
173/// }
174/// }
175/// ```
176#[derive(Responder)]
177pub enum RocketResponseGeneric<T>
178where
179 T: Serialize,
180{
181 /// see [rocket::response::status::Accepted]
182 Accepted(Accepted<T>),
183 /// see [rocket::response::status::BadRequest]
184 BadRequest(BadRequest<T>),
185 /// see [rocket::response::status::Conflict]
186 Conflict(Conflict<T>),
187 /// see [rocket::response::status::Created]
188 Created(Created<T>),
189 /// see [rocket::response::content::RawCss]
190 Css(RawCss<T>),
191 /// see [File]
192 File(File),
193 /// see [rocket::response::Flash]
194 Flash(Flash<T>),
195 /// see [rocket::response::status::Forbidden]
196 Forbidden(Forbidden<T>),
197 /// see [rocket::response::content::RawHtml]
198 Html(RawHtml<T>),
199 /// see [rocket::response::content::RawJavaScript]
200 JavaScript(RawJavaScript<T>),
201 /// see [rocket::response::content::RawJson]
202 Json(RawJson<T>),
203 /// see [rocket::response::content::RawMsgPack]
204 MsgPack(RawMsgPack<T>),
205 /// see [NamedFile](rocket::fs::NamedFile)
206 NamedFiled(NamedFile),
207 /// see [rocket::response::status::NotFound]
208 NotFound(NotFound<T>),
209 /// see [NoContent](rocket::response::status::NoContent)
210 NoContent(NoContent),
211 /// see [rocket::response::content::RawText]
212 Plain(RawText<T>),
213 /// see [Redirect](rocket::response::Redirect)
214 Redirect(Redirect),
215
216 #[cfg(feature = "json")]
217 /// see [rocket::serde::json::Json]
218 SerdeJson(serde::json::Json<T>),
219 #[cfg(feature = "msgpack")]
220 /// see [rocket::serde::msgpack::MsgPack]
221 SerdeMsgPack(serde::msgpack::MsgPack<T>),
222 #[cfg(feature = "json")]
223 /// see [Value](rocket::serde::json::Value)
224 SerdeValue(serde::json::Value),
225
226 /// see [slice](std::slice)
227 StaticSlice(&'static [u8]),
228 /// see [str]
229 StaticStr(&'static str),
230 /// see [String]
231 String(String),
232 /// see [Status](rocket::http::Status)
233 Status(Status),
234
235 #[cfg(any(feature = "templates-tera", feature = "templates-handlebars"))]
236 /// see [Template](rocket_dyn_templates::Template)
237 Template(Template),
238
239 /// see [File](rocket::tokio::fs::File)
240 TokioFile(tokio::fs::File),
241 /// see [Unauthorized](rocket::response::status::Unauthorized)
242 Unauthorized(Unauthorized<T>),
243 /// see [Vec](std::vec::Vec)
244 Vec(Vec<u8>),
245 /// see [Xml](rocket::response::content::RawXml)
246 Xml(RawXml<T>),
247}
248
249/// The non-generic and generic [Responses](rocket::response::Response) with 2 types.
250///
251/// ## Example usage
252///
253/// ```rust
254/// use rocket::{get, response::{self, status, Redirect}};
255/// use rocket_response::RocketResponseGeneric2 as RocketResponse;
256///
257/// #[get("/<id>")]
258/// pub(crate) fn rocket_response_generic2(
259/// id: usize,
260/// ) -> RocketResponse<&'static str, Redirect> {
261/// match id {
262/// 0 => RocketResponse::Flash(response::Flash::error(
263/// Redirect::to("/"),
264/// format!("Invalid id {}", id),
265/// )),
266/// 1 => RocketResponse::Unauthorized(status::Unauthorized(Some(
267/// "admin need authentication",
268/// ))),
269/// _ => RocketResponse::Html(response::content::RawHtml(
270/// "<html><body>Hello world</body></html",
271/// )),
272/// }
273/// }
274/// ```
275#[derive(Responder)]
276pub enum RocketResponseGeneric2<T, U>
277where
278 T: Serialize,
279{
280 /// see [rocket::response::status::Accepted]
281 Accepted(Accepted<T>),
282 /// see [rocket::response::status::BadRequest]
283 BadRequest(BadRequest<T>),
284 /// see [rocket::response::status::Conflict]
285 Conflict(Conflict<T>),
286 /// see [rocket::response::status::Created]
287 Created(Created<T>),
288 /// see [rocket::response::content::RawCss]
289 Css(RawCss<T>),
290 /// see [File]
291 File(File),
292 /// with generic type U
293 /// see [rocket::response::Flash]
294 Flash(Flash<U>),
295 /// see [rocket::response::status::Forbidden]
296 Forbidden(Forbidden<T>),
297 /// see [rocket::response::content::RawHtml]
298 Html(RawHtml<T>),
299 /// see [rocket::response::content::RawJavaScript]
300 JavaScript(RawJavaScript<T>),
301 /// see [rocket::response::content::RawJson]
302 Json(RawJson<T>),
303 /// see [rocket::response::content::RawMsgPack]
304 MsgPack(RawMsgPack<T>),
305 /// see [NamedFile](rocket::fs::NamedFile)
306 NamedFiled(NamedFile),
307 /// see [NoContent](rocket::response::status::NoContent)
308 NotFound(NotFound<T>),
309 /// see [rocket::response::status::NoContent]
310 NoContent(NoContent),
311 /// see [rocket::response::content::RawText]
312 Plain(RawText<T>),
313 /// see [rocket::response::Redirect]
314 Redirect(Redirect),
315
316 #[cfg(feature = "json")]
317 /// see [rocket::serde::json::Json]
318 SerdeJson(serde::json::Json<T>),
319 #[cfg(feature = "msgpack")]
320 /// see [rocket::serde::msgpack::MsgPack]
321 SerdeMsgPack(serde::msgpack::MsgPack<T>),
322 #[cfg(feature = "json")]
323 /// see [rocket::serde::json::Value]
324 SerdeValue(serde::json::Value),
325
326 /// see [slice](std::slice)
327 StaticSlice(&'static [u8]),
328 /// see [str]
329 StaticStr(&'static str),
330 /// see [String]
331 String(String),
332 /// see [Status](rocket::http::Status)
333 Status(Status),
334
335 #[cfg(any(feature = "templates-tera", feature = "templates-handlebars"))]
336 /// see [Template](rocket_dyn_templates::Template)
337 Template(Template),
338
339 /// see [File](rocket::tokio::fs::File)
340 TokioFile(tokio::fs::File),
341 /// see [Unauthorized](rocket::response::status::Unauthorized)
342 Unauthorized(Unauthorized<T>),
343 /// see [Vec](std::vec::Vec)
344 Vec(Vec<u8>),
345 /// see [Xml](rocket::response::content::RawXml)
346 Xml(RawXml<T>),
347}
348
349#[cfg(test)]
350mod tests {
351 use super::{RocketResponse, RocketResponseGeneric, RocketResponseGeneric2};
352 use rocket::{
353 get,
354 http::ContentType,
355 http::Status,
356 local::blocking::Client,
357 response::{self, status, Redirect},
358 routes,
359 };
360
361 #[get("/response/<id>")]
362 pub(crate) fn route_response(id: usize) -> RocketResponse {
363 match id {
364 0 => RocketResponse::NoContent(response::status::NoContent),
365 1 => RocketResponse::Redirect(Redirect::to("/admin")),
366 _ => RocketResponse::StaticStr("Hello world"),
367 }
368 }
369
370 #[get("/response_generic/<id>")]
371 pub(crate) fn route_response_generic(id: usize) -> RocketResponseGeneric<&'static str> {
372 match id {
373 0 => RocketResponseGeneric::NoContent(status::NoContent),
374 1 => RocketResponseGeneric::Unauthorized(status::Unauthorized(Some(
375 "admin need authentication",
376 ))),
377 _ => RocketResponseGeneric::Html(response::content::RawHtml(
378 "<html><body>Hello world</body></html",
379 )),
380 }
381 }
382
383 #[get("/response_generic2/<id>")]
384 pub(crate) fn route_response_generic2(
385 id: usize,
386 ) -> RocketResponseGeneric2<&'static str, Redirect> {
387 match id {
388 0 => RocketResponseGeneric2::Flash(response::Flash::error(
389 Redirect::to("/"),
390 format!("Invalid id {}", id),
391 )),
392 1 => RocketResponseGeneric2::Unauthorized(status::Unauthorized(Some(
393 "admin need authentication",
394 ))),
395 _ => RocketResponseGeneric2::Html(response::content::RawHtml(
396 "<html><body>Hello world</body></html",
397 )),
398 }
399 }
400
401 #[test]
402 fn test_rocket_response() {
403 let rocket = rocket::build().mount("/", routes![route_response]);
404 let client = Client::tracked(rocket).expect("no rocket instance");
405 let req = client.get("/response/2");
406 let res = req.dispatch();
407
408 assert_eq!(Status::Ok, res.status());
409 assert_eq!(ContentType::Plain, res.content_type().unwrap());
410 }
411
412 #[test]
413 fn test_rocket_response_generic() {
414 let rocket = rocket::build().mount("/", routes![route_response_generic]);
415 let client = Client::tracked(rocket).expect("no rocket instance");
416 let req = client.get("/response_generic/0");
417 let res = req.dispatch();
418
419 assert_eq!(Status::NoContent, res.status());
420 }
421
422 #[test]
423 fn test_rocket_response_generic2() {
424 let rocket = rocket::build().mount("/", routes![route_response_generic2]);
425 let client = Client::tracked(rocket).expect("no rocket instance");
426 let req = client.get("/response_generic2/0");
427 let res = req.dispatch();
428
429 assert_eq!(Status::SeeOther, res.status());
430 }
431}