1fn escape(s: &str) -> String {
6 s.chars()
7 .map(|c| match c {
8 '&' => "&".to_owned(),
9 '<' => "<".to_owned(),
10 '>' => ">".to_owned(),
11 '"' => """.to_owned(),
12 '\'' => "'".to_owned(),
13 c => c.to_string(),
14 })
15 .collect()
16}
17
18pub fn swagger_ui_html(title: &str, spec_url: &str) -> String {
20 let (title, url) = (escape(title), escape(spec_url));
21 format!(
22 r##"<!doctype html>
23<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
24<title>{title} – Swagger UI</title>
25<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css">
26</head><body><div id="swagger-ui" data-url="{url}"></div>
27<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
28<script>
29const el = document.getElementById("swagger-ui");
30SwaggerUIBundle({{ url: el.dataset.url, dom_id: "#swagger-ui" }});
31</script></body></html>"##
32 )
33}
34
35pub fn redoc_html(title: &str, spec_url: &str) -> String {
37 let (title, url) = (escape(title), escape(spec_url));
38 format!(
39 r##"<!doctype html>
40<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
41<title>{title} – ReDoc</title></head>
42<body><redoc spec-url="{url}"></redoc>
43<script src="https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js"></script>
44</body></html>"##
45 )
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn pages_escape_inputs() {
54 let html = swagger_ui_html("<x>", "/openapi.json?a=\"b\"");
55 assert!(html.contains("<x>"));
56 assert!(html.contains("data-url=\"/openapi.json?a="b"\""));
57 assert!(redoc_html("t", "/o").contains("spec-url=\"/o\""));
58 }
59}