// The pages an application serves to describe itself: Swagger UI, ReDoc, the
// Redocly reference and the Scalar reference, each of them reading the one
// document the application publishes. A page wears the site's theme unless told
// otherwise, and a plain page is the reader's own look, untouched.
///|
/// Which reader a page serves. `Scalar` is the current generation of API
/// reference, with a request client built in; `Swagger` is Swagger UI, the
/// long-standing one; `Redoc` reads without letting a caller try anything, and
/// `Redocly` is Redocly's own reference, which needs a licence key.
#derive(Eq, Debug)
pub(all) enum Ui {
Scalar
Swagger
Redoc
Redocly
}
///|
pub extend Ui with Eq::{equal, not_equal}
///|
pub extend Ui with Debug::{to_repr}
///|
/// How a page looks. `Site` is the theme of this library's own site: the
/// periwinkle paper, the iris accent, the display face and the thin scrollbars,
/// on a bar naming the application and pointing at its document. `Plain` is the
/// reader as its maker ships it — no bar, no fonts, no colours of ours — for a
/// page that is dropped into a site of its own.
#derive(Eq, Debug)
pub(all) enum Theme {
Site
Plain
}
///|
pub extend Theme with Eq::{equal, not_equal}
///|
pub extend Theme with Debug::{to_repr}
///|
/// The theme a page wears unless told otherwise.
pub let theme : Theme = Site
///|
/// The pages load their readers from a CDN at an exact version, so a page does
/// not change under an application that did not change. Self-hosting is a
/// matter of passing other URLs.
pub let swagger_js : String = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.34.1/swagger-ui-bundle.js"
///|
pub let swagger_css : String = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.34.1/swagger-ui.css"
///|
pub let swagger_redirect_js : String = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.34.1/oauth2-redirect.js"
///|
pub let redoc_js : String = "https://cdn.jsdelivr.net/npm/redoc@3.1.0/bundles/redoc.standalone.js"
///|
pub let scalar_js : String = "https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.77.0/dist/browser/standalone.js"
///|
/// Redocly's reference is served from Redocly's own CDN and is licensed per
/// deployment: the page carries the licence key its options name.
pub let redocly_js : String = "https://cdn.redoc.ly/reference-docs/v2.45.14/redocly-reference-docs.min.js"
///|
/// The faces the site's theme is set in. Only the weights the pages use are
/// asked for: a page that pulls a family's whole range delays its own text.
let site_fonts : String = "https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap"
///|
/// `s` escaped for HTML text and attribute values.
fn html_escape(s : String) -> String {
let out = StringBuilder()
for c in s {
match c {
'&' => out.write_string("&")
'<' => out.write_string("<")
'>' => out.write_string(">")
'"' => out.write_string(""")
'\'' => out.write_string("'")
_ => out.write_char(c)
}
}
out.to_string()
}
///|
/// `j` as a script literal. JSON is a subset of JavaScript; `<` is escaped too,
/// so no value can close the `\n"
match theme {
Plain =>
"\n\n\n\n\n\{html_escape(title)}\n\n\n\n\n\{script}\n\n\n"
Site =>
site_page(
title~,
spec_url~,
head="\n",
body="\n\{script}\n",
)
}
}
///|
/// The page an OAuth2 authorisation started from Swagger UI returns to: it hands
/// the code or token back to the window that opened it (← FastAPI's
/// `get_swagger_ui_oauth2_redirect_html`).
pub fn swagger_redirect(js_url~ : String = swagger_redirect_js) -> String {
"\n\n\nSwagger UI: OAuth2 Redirect\n\n\n\n\n\n"
}
///|
/// A ReDoc page rendering the document served at `spec_url` (← FastAPI's
/// `get_redoc_html`): three panels, built for reading rather than for trying
/// calls out. ReDoc reports usage to its maker unless told not to; `telemetry`
/// is off unless turned on, since the page is served to this API's readers.
pub fn redoc_ui(
spec_url~ : String = "/openapi.json",
title~ : String = "moonapi",
js_url~ : String = redoc_js,
telemetry~ : Bool = false,
theme~ : Theme = theme,
) -> String {
let opt_out = if telemetry { "" } else { " disable-telemetry=\"true\"" }
let script = ""
match theme {
Plain =>
"\n\n\n\n\n\{html_escape(title)}\n\n\n\n\n\{script}\n\n\n"
Site =>
site_page(
title~,
spec_url~,
head="",
body="\n\{script}\n",
)
}
}
///|
/// A Redocly reference rendering the document served at `spec_url`: the
/// reference and a request console on one page. `extra` is merged over the
/// options passed to `RedoclyReferenceDocs.init`, which is where the licence
/// key, the theming and the code samples go; without a key the reference
/// renders under Redocly's banner.
pub fn redocly_ui(
spec_url~ : String = "/openapi.json",
title~ : String = "moonapi",
js_url~ : String = redocly_js,
extra? : Map[String, Json],
theme~ : Theme = theme,
) -> String {
let options : Map[String, Json] = Map([])
for k, v in extra.unwrap_or(Map([])) {
options[k] = v
}
let script = "\n"
match theme {
Plain =>
"\n\n\n\n\n\{html_escape(title)}\n\n\n\n\n\{script}\n\n\n"
Site =>
site_page(
title~,
spec_url~,
head="",
body="\n\{script}\n",
)
}
}
///|
/// A Scalar API reference rendering the document served at `spec_url`: the
/// reference and a request client on one page. `extra` is merged over the
/// configuration passed to `Scalar.createApiReference`, which is where its
/// theme, layout and authentication defaults go.
pub fn scalar_ui(
spec_url~ : String = "/openapi.json",
title~ : String = "moonapi",
js_url~ : String = scalar_js,
extra? : Map[String, Json],
theme~ : Theme = theme,
) -> String {
let config : Map[String, Json] = { "url": Json(spec_url) }
for k, v in extra.unwrap_or(Map([])) {
config[k] = v
}
let script = "\n"
match theme {
Plain =>
"\n\n\n\n\n\{html_escape(title)}\n\n\n\n\{script}\n\n\n"
Site =>
site_page(
title~,
spec_url~,
head="",
body="\n\{script}\n",
)
}
}