// 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", ) } }