decode_json.pony

use json = "json"

primitive DecodeJSON[A: Any val]
  """
  Parse and decode an HTTP response body as a typed domain object in one step.

  Combines `ResponseJSON` (JSON parsing) with a `JSONDecoder` (structural
  decoding) into a single call. The three-way return type distinguishes
  between success, parse failure, and decode failure:

  - `A` — the response body was valid JSON and matched the decoder's expected
    structure
  - `JSONParseError` — the response body was not valid JSON syntax
  - `JSONDecodeError` — the JSON was valid but didn't match the decoder's
    expected structure (missing fields, wrong types, etc.)

  ```pony
  use "http_client"
  use json = "json"

  // In on_response_complete():
  match DecodeJSON[User](response, UserDecoder)
  | let user: User =>
    env.out.print("Hello, " + user.name)
  | let err: json.JSONParseError =>
    env.out.print("Invalid JSON: " + err.string())
  | let err: JSONDecodeError =>
    env.out.print("Unexpected structure: " + err.string())
  end
  ```
  """

  fun apply(
    response: HTTPResponse,
    decoder: JSONDecoder[A])
    : (A | json.JSONParseError | JSONDecodeError)
  =>
    """
    Parse `response.body` as JSON, then decode with `decoder`.

    Returns `JSONParseError` if the body isn't valid JSON, `JSONDecodeError` if
    the JSON doesn't match the decoder's expected structure, or the decoded
    value on success.
    """
    match \exhaustive\ ResponseJSON(response)
    | let value: json.JSONValue => decoder(value)
    | let err: json.JSONParseError => err
    end