///|
/// The typed body extractors. `Context::body_validated[T]` checks the JSON body
/// against a descriptor and only then deserialises into a `derive(FromJson)`
/// struct, returning either the built value or a FastAPI-shaped `422`;
/// `Context::body[T]` is the unchecked, total counterpart that yields `None`
/// rather than erroring. `GreetReq` is a library struct with `derive(FromJson)`
/// and a `schema()`.
///
///     moon run src/examples/06-typed-body
#coverage.skip
async fn main {
  let app = @moonapi.App::new()
  // Validated extraction: conform, or get located errors.
  app.post("/greet", ctx => {
    match (ctx.body_validated(GreetReq::schema()) : Result[GreetReq, _]) {
      Ok(g) => @moonapi.text(200, "Hello, " + g.name)
      Err(errs) => @moonapi.unprocessable(errs)
    }
  })
  // Unchecked extraction: a total `T?`, `None` when the body cannot shape-match.
  app.post("/echo", ctx => {
    let parsed : GreetReq? = ctx.body()
    match parsed {
      Some(g) => @moonapi.text(200, "parsed name=" + g.name)
      None => @moonapi.text(200, "unparseable")
    }
  })
  let hit = (path : String, body : String) => {
    let req : @moonasgi.Request = {
      http_method: "POST",
      path,
      query_string: b"",
      headers: [("content-type", "application/json")],
      body: @utf8.encode(body),
    }
    let r = app.handle(req)
    println(
      "POST \{path} \{body} -> \{r.status} \{@utf8.decode_lossy(r.body[:])}",
    )
  }
  hit("/greet", "{\"name\":\"lfanke\"}") // 200
  hit("/greet", "{\"name\":233}") // 422 string_type
  hit("/greet", "") // 422 missing body
  hit("/echo", "{\"name\":\"qwq\"}") // parsed
  hit("/echo", "not json at all") // unparseable
}