1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use hypers_core::{
    async_trait,
    prelude::{Request, Response, Text},
    Handler,
};
use std::borrow::Cow;

/// Implements [`Handler`] for serving RapiDoc.
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct RapiDoc {
    /// The title of the html page. The default title is "RapiDoc".
    pub title: Cow<'static, str>,
    /// The version of the html page.
    pub keywords: Option<Cow<'static, str>>,
    /// The description of the html page.
    pub description: Option<Cow<'static, str>>,
    /// The lib url path.
    pub lib_url: Cow<'static, str>,
    /// The spec url path.
    pub spec_url: Cow<'static, str>,
}
impl RapiDoc {
    /// Create a new [`RapiDoc`] for given path.
    /// Path argument will expose the RapiDoc to the user and should be something that
    /// the underlying application framework / library supports.
    /// # Examples
    /// ```rust
    /// # use hypers_openapi::rapidoc::RapiDoc;
    /// let doc = RapiDoc::new("/openapi.json");
    /// ```
    pub fn new(spec_url: impl Into<Cow<'static, str>>) -> Self {
        Self {
            title: "RapiDoc".into(),
            keywords: None,
            description: None,
            lib_url: "https://unpkg.com/rapidoc/dist/rapidoc-min.js".into(),
            spec_url: spec_url.into(),
        }
    }
    /// Set title of the html page. The default title is "RapiDoc".
    pub fn title(mut self, title: impl Into<Cow<'static, str>>) -> Self {
        self.title = title.into();
        self
    }
    /// Set keywords of the html page.
    pub fn keywords(mut self, keywords: impl Into<Cow<'static, str>>) -> Self {
        self.keywords = Some(keywords.into());
        self
    }
    /// Set description of the html page.
    pub fn description(mut self, description: impl Into<Cow<'static, str>>) -> Self {
        self.description = Some(description.into());
        self
    }
    /// Set the lib url path.
    pub fn lib_url(mut self, lib_url: impl Into<Cow<'static, str>>) -> Self {
        self.lib_url = lib_url.into();
        self
    }
}
#[async_trait]
impl Handler for RapiDoc {
    #[inline]
    async fn handle(&self, _: Request) -> Response {
        let keywords = self
            .keywords
            .as_ref()
            .map(|s| {
                format!(
                    "<meta name=\"keywords\" content=\"{}\">",
                    s.split(',').map(|s| s.trim()).collect::<Vec<_>>().join(",")
                )
            })
            .unwrap_or_default();
        let description = self
            .description
            .as_ref()
            .map(|s| format!("<meta name=\"description\" content=\"{}\">", s))
            .unwrap_or_default();
        let html = r#"
            <!doctype html>
            <html>
                <head>
                    <title>{{title}}</title>
                    {{keywords}}
                    {{description}}
                    <meta charset="utf-8">
                    <script type="module" src="{{lib_url}}"></script>
                </head>
                <body>
                    <rapi-doc spec-url="{{spec_url}}"></rapi-doc>
                </body>
            </html>
            "#
        .replacen("{{spec_url}}", &self.spec_url, 1)
        .replacen("{{lib_url}}", &self.lib_url, 1)
        .replacen("{{description}}", &description, 1)
        .replacen("{{keywords}}", &keywords, 1)
        .replacen("{{title}}", &self.title, 1);
        Response::default().render(Text::Html(html))
    }
}