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}