salvo_oapi/rapidoc/
mod.rs1use std::borrow::Cow;
8
9use salvo_core::writing::Text;
10use salvo_core::{async_trait, Depot, FlowCtrl, Handler, Request, Response, Router};
11
12const INDEX_TMPL: &str = r#"
13<!doctype html>
14<html>
15 <head>
16 <title>{{title}}</title>
17 {{keywords}}
18 {{description}}
19 <meta charset="utf-8">
20 <script type="module" src="{{lib_url}}"></script>
21 </head>
22 <body>
23 <rapi-doc spec-url="{{spec_url}}"></rapi-doc>
24 </body>
25</html>
26"#;
27
28#[non_exhaustive]
30#[derive(Clone, Debug)]
31pub struct RapiDoc {
32 pub title: Cow<'static, str>,
34 pub keywords: Option<Cow<'static, str>>,
36 pub description: Option<Cow<'static, str>>,
38 pub lib_url: Cow<'static, str>,
40 pub spec_url: Cow<'static, str>,
42}
43impl RapiDoc {
44 pub fn new(spec_url: impl Into<Cow<'static, str>>) -> Self {
56 Self {
57 title: "RapiDoc".into(),
58 keywords: None,
59 description: None,
60 lib_url: "https://unpkg.com/rapidoc/dist/rapidoc-min.js".into(),
61 spec_url: spec_url.into(),
62 }
63 }
64
65 pub fn title(mut self, title: impl Into<Cow<'static, str>>) -> Self {
67 self.title = title.into();
68 self
69 }
70
71 pub fn keywords(mut self, keywords: impl Into<Cow<'static, str>>) -> Self {
73 self.keywords = Some(keywords.into());
74 self
75 }
76
77 pub fn description(mut self, description: impl Into<Cow<'static, str>>) -> Self {
79 self.description = Some(description.into());
80 self
81 }
82
83 pub fn lib_url(mut self, lib_url: impl Into<Cow<'static, str>>) -> Self {
85 self.lib_url = lib_url.into();
86 self
87 }
88
89 pub fn into_router(self, path: impl Into<String>) -> Router {
91 Router::with_path(path.into()).goal(self)
92 }
93}
94
95#[async_trait]
96impl Handler for RapiDoc {
97 async fn handle(&self, _req: &mut Request, _depot: &mut Depot, res: &mut Response, _ctrl: &mut FlowCtrl) {
98 let keywords = self
99 .keywords
100 .as_ref()
101 .map(|s| {
102 format!(
103 "<meta name=\"keywords\" content=\"{}\">",
104 s.split(',').map(|s| s.trim()).collect::<Vec<_>>().join(",")
105 )
106 })
107 .unwrap_or_default();
108 let description = self
109 .description
110 .as_ref()
111 .map(|s| format!("<meta name=\"description\" content=\"{}\">", s))
112 .unwrap_or_default();
113 let html = INDEX_TMPL
114 .replacen("{{spec_url}}", &self.spec_url, 1)
115 .replacen("{{lib_url}}", &self.lib_url, 1)
116 .replacen("{{description}}", &description, 1)
117 .replacen("{{keywords}}", &keywords, 1)
118 .replacen("{{title}}", &self.title, 1);
119 res.render(Text::Html(html));
120 }
121}