///|
/// Sub-application mounting — composing routers. `App::mount(prefix, sub)` routes
/// a request under `prefix` through the sub-app with the prefix stripped, and the
/// sub-app's routes fold into the parent's merged OpenAPI document under that
/// prefix. Mounts nest: the API sub-app itself mounts an admin sub-app. The
/// composed app converts to a single `AsgiApp` with `App::to_asgi`, the value a
/// server (mooncat) runs.
///
///     moon run src/examples/15-mounting
#coverage.skip
async fn main {
  // A nested sub-app, mounted inside the API sub-app.
  let admin = @moonapi.App::new()
  admin.get(
    "/stats",
    _ctx => @moonapi.text(200, "admin stats"),
    summary="stats",
  )

  // The API sub-app.
  let api = @moonapi.App::new()
  api.get(
    "/items",
    _ctx => @moonapi.text(200, "the items"),
    summary="list items",
  )
  api.get(
    "/items/{id}",
    ctx => @moonapi.text(200, "item " + ctx.param("id").unwrap()),
    summary="get item",
  )
  api.mount("/admin", admin)

  // The root app mounts the API under a versioned prefix.
  let app = @moonapi.App::new()
  app.get("/", _ctx => @moonapi.text(200, "root"))
  app.mount("/api/v1", api)
  let hit = (path : String) => {
    let req : @moonasgi.Request = {
      http_method: "GET",
      path,
      query_string: b"",
      headers: [],
      body: b"",
    }
    let r = app.handle(req)
    println("GET \{path} -> \{r.status} \{@utf8.decode_lossy(r.body[:])}")
  }
  hit("/")
  hit("/api/v1/items")
  hit("/api/v1/items/233") // prefix stripped, sub-app extracts :id
  hit("/api/v1/admin/stats") // nested mount, two prefixes deep
  hit("/api/v1/missing") // under the mount but no route -> 404
  hit("/elsewhere") // outside every mount -> 404

  // The whole composition is one AsgiApp a server runs.
  let _asgi = app.to_asgi()
  println("to_asgi() built the AsgiApp mooncat serves")
  println("--- merged OpenAPI 3.1.0 (sub-app paths under their prefixes) ---")
  println(app.openapi_json(version=OpenApi31))
}