rocket_autodocu/handlers/
content.rs

1use rocket::http::{ContentType, Method, Status};
2use rocket::response::Responder;
3use rocket::route::{Handler, Outcome};
4use rocket::{Data, Request, Route};
5
6/// A content handler is a wrapper type around `rocket::response::Content`, which can be turned into
7/// a `rocket::Route` that serves the content with correct content-type.
8#[derive(Clone)]
9pub struct ContentHandler<R: AsRef<[u8]> + Clone + Send + Sync> {
10    content: (ContentType, R),
11}
12
13impl ContentHandler<String> {
14    /// Create a `ContentHandler<String>` which serves its content as JSON.
15    pub fn json(content: &impl serde::Serialize) -> Self {
16        let json =
17            serde_json::to_string_pretty(content).expect("Could not serialize content as JSON.");
18        ContentHandler {
19            content: (ContentType::JSON, json),
20        }
21    }
22}
23
24impl ContentHandler<&'static [u8]> {
25    /// Create a `ContentHandler<&[u8]>`, which serves its content with the specified
26    /// `content_type`.
27    pub fn bytes(content_type: ContentType, content: &'static [u8]) -> Self {
28        ContentHandler {
29            content: (content_type, content),
30        }
31    }
32}
33
34impl ContentHandler<Vec<u8>> {
35    /// Create a `ContentHandler<Vec<u8>>`, which serves its content with the specified
36    /// `content_type`.
37    pub fn bytes_owned(content_type: ContentType, content: Vec<u8>) -> Self {
38        ContentHandler {
39            content: (content_type, content),
40        }
41    }
42}
43
44impl<R: AsRef<[u8]> + Clone + Send + Sync + 'static> ContentHandler<R> {
45    /// Create a `rocket::Route` from the current `ContentHandler`.
46    pub fn into_route(self, path: impl AsRef<str>) -> Route {
47        Route::new(Method::Get, path.as_ref(), self)
48    }
49}
50
51#[rocket::async_trait]
52impl<R> Handler for ContentHandler<R>
53where
54    R: AsRef<[u8]> + Clone + Send + Sync + 'static,
55{
56    async fn handle<'r>(&self, req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r> {
57        // match e.g. "/index.html" but not "/index.html/"
58        if req.uri().path().ends_with('/') {
59            Outcome::forward(data, Status::PermanentRedirect)
60        } else {
61            let content: (_, Vec<u8>) = (self.content.0.clone(), self.content.1.as_ref().into());
62            match content.respond_to(req) {
63                Ok(response) => Outcome::Success(response),
64                Err(status) => Outcome::Error(status),
65            }
66        }
67    }
68}