///|
pub struct Info {
  title : String
  version : String
  description : String?
}

///|
pub struct Parameter {
  name : String
  required : Bool
  schema : Json
  description : String?
  examples : Array[Example]
}

///|
pub struct ResultDescriptor {
  name : String
  schema : Json
  description : String?
  examples : Array[Example]
}

///|
pub struct Method {
  name : String
  params : Array[Parameter]
  result : ResultDescriptor?
  summary : String?
  description : String?
  deprecated : Bool
  tags : Array[Tag]
  servers : Array[Server]
  examples : Array[ExamplePairing]
}

///|
pub struct Document {
  openrpc : String
  info : Info
  methods : Array[Method]
  servers : Array[Server]
  tags : Array[Tag]
  components : Components
  source : Json
}

///|
pub enum DiagnosticCode {
  InvalidJson
  ExpectedObject
  MissingField
  InvalidField
  UnsupportedVersion
  DuplicateMethod
  MissingParameter
  UnknownParameter
  TooManyParameters
  EmptyMethodName
  EmptyParameterName
  EmptyResultName
  DuplicateParameter
  OptionalBeforeRequired
  InvalidSchemaBoundary
  EmptyBatch
  UnknownMethod
  DuplicateRequestId
  SchemaViolation
  UnsupportedSchema
  InvalidResponse
  InvalidBatchResponse
  UnknownResponseId
  DuplicateResponseId
  MissingResponse
  ResponseIdMismatch
  InvalidRpcError
  DuplicateTag
  UnsupportedReference
  UnresolvedReference
  CircularReference
  InvalidServerTemplate
  UnknownServerVariable
  InvalidServerVariableValue
  InvalidExamplePairing
}

///|
pub struct Diagnostic {
  path : String
  code : DiagnosticCode
  message : String
}

///|
pub struct RpcError {
  code : Int
  message : String
  data : Json?
}

///|
pub enum RpcResponse {
  Result(Json)
  Error(RpcError)
}

///|
/// A response correlated with one of the calls in a JSON-RPC batch.
pub struct BatchResponse {
  id : Json
  method_name : String
  response : RpcResponse
}

///|
pub enum RequestParams {
  Named(Map[String, Json])
  Positional(Array[Json])
}

///|
pub struct BatchCall {
  id : Json
  method_name : String
  params : RequestParams
}

///|
pub fn RequestParams::named(params : Map[String, Json]) -> RequestParams {
  RequestParams::Named(params)
}

///|
pub fn RequestParams::positional(values : Array[Json]) -> RequestParams {
  RequestParams::Positional(values)
}

///|
pub fn BatchCall::new(
  id : Json,
  method_name : String,
  params : RequestParams,
) -> BatchCall {
  { id, method_name, params, }
}

///|
pub fn Document::parse(source : String) -> Result[Document, Array[Diagnostic]] {
  let value = @json.parse(source) catch {
    _ => return Err([diagnostic("$", InvalidJson, "source is not valid JSON")])
  }
  parse_document(value)
}

///|
pub fn Document::find_method(self : Document, name : String) -> Method? {
  for method_value in self.methods {
    if method_value.name == name {
      return Some(method_value)
    }
  }
  None
}

///|
pub fn Document::build_batch(
  self : Document,
  calls : Array[BatchCall],
) -> Result[Json, Array[Diagnostic]] {
  if calls.length() == 0 {
    return Err([
      diagnostic("$", EmptyBatch, "a JSON-RPC batch must contain a call"),
    ])
  }
  let requests : Array[Json] = []
  let ids : Array[Json] = []
  for index, call in calls {
    let call_path = "$[" + index.to_string() + "]"
    for previous_id in ids {
      if Json::equal(previous_id, call.id) {
        return Err([
          diagnostic(
            call_path + ".id",
            DuplicateRequestId,
            "request ids must be unique within a batch",
          ),
        ])
      }
    }
    ids.push(call.id)
    let method_value = match self.find_method(call.method_name) {
      Some(value) => value
      None =>
        return Err([
          diagnostic(
            call_path + ".method",
            UnknownMethod,
            "method is not declared by the document",
          ),
        ])
    }
    let request : Json = match call.params {
      RequestParams::Named(params) =>
        match validate_named_params(method_value.params, params, call_path) {
          Err(diagnostics) => return Err(diagnostics)
          Ok(_) =>
            {
              "jsonrpc": "2.0",
              "id": call.id,
              "method": method_value.name,
              "params": Json::object(params),
            }
        }
      RequestParams::Positional(values) =>
        match
          validate_positional_params(method_value.params, values, call_path) {
          Err(diagnostics) => return Err(diagnostics)
          Ok(_) =>
            {
              "jsonrpc": "2.0",
              "id": call.id,
              "method": method_value.name,
              "params": Json::array(values),
            }
        }
    }
    requests.push(request)
  }
  Ok(Json::array(requests))
}

///|
pub fn Method::build_request(
  self : Method,
  id : Json,
  params : Map[String, Json],
) -> Result[Json, Array[Diagnostic]] {
  match validate_named_params(self.params, params, "$") {
    Err(diagnostics) => Err(diagnostics)
    Ok(_) =>
      Ok({
        "jsonrpc": "2.0",
        "id": id,
        "method": self.name,
        "params": Json::object(params),
      })
  }
}

///|
pub fn Method::build_notification(
  self : Method,
  params : Map[String, Json],
) -> Result[Json, Array[Diagnostic]] {
  match validate_named_params(self.params, params, "$") {
    Err(diagnostics) => Err(diagnostics)
    Ok(_) =>
      Ok({
        "jsonrpc": "2.0",
        "method": self.name,
        "params": Json::object(params),
      })
  }
}

///|
pub fn Method::build_positional_request(
  self : Method,
  id : Json,
  values : Array[Json],
) -> Result[Json, Array[Diagnostic]] {
  match validate_positional_params(self.params, values, "$") {
    Err(diagnostics) => Err(diagnostics)
    Ok(_) =>
      Ok({
        "jsonrpc": "2.0",
        "id": id,
        "method": self.name,
        "params": Json::array(values),
      })
  }
}

///|
pub fn Method::build_positional_notification(
  self : Method,
  values : Array[Json],
) -> Result[Json, Array[Diagnostic]] {
  match validate_positional_params(self.params, values, "$") {
    Err(diagnostics) => Err(diagnostics)
    Ok(_) =>
      Ok({
        "jsonrpc": "2.0",
        "method": self.name,
        "params": Json::array(values),
      })
  }
}

///|
/// Validate one value against this parameter's declared schema.
///
/// Validation is explicit and opt-in. Request builders intentionally keep
/// their schema-agnostic behavior so callers can choose when validation is
/// appropriate for their transport or trust boundary.
pub fn Parameter::validate(
  self : Parameter,
  value : Json,
) -> Result[Unit, Array[Diagnostic]] {
  validate_schema_at(value, self.schema, "$.params." + self.name)
}

///|
/// Validate a result value against its declared schema.
pub fn ResultDescriptor::validate(
  self : ResultDescriptor,
  value : Json,
) -> Result[Unit, Array[Diagnostic]] {
  validate_schema_at(value, self.schema, "$.result")
}

///|
/// Validate named parameters against declarations and their schemas.
pub fn Method::validate_named_params(
  self : Method,
  params : Map[String, Json],
) -> Result[Unit, Array[Diagnostic]] {
  match validate_named_params(self.params, params, "$") {
    Err(diagnostics) => return Err(diagnostics)
    Ok(_) => ()
  }
  for parameter in self.params {
    match params.get(parameter.name) {
      None => ()
      Some(value) =>
        match
          validate_schema_at(
            value,
            parameter.schema,
            "$.params." + parameter.name,
          ) {
          Ok(_) => ()
          Err(diagnostics) => return Err(diagnostics)
        }
    }
  }
  Ok(())
}

///|
/// Validate positional parameters against declarations and their schemas.
pub fn Method::validate_positional_params(
  self : Method,
  values : Array[Json],
) -> Result[Unit, Array[Diagnostic]] {
  match validate_positional_params(self.params, values, "$") {
    Err(diagnostics) => return Err(diagnostics)
    Ok(_) => ()
  }
  for index, value in values {
    let parameter = self.params[index]
    match
      validate_schema_at(
        value,
        parameter.schema,
        "$.params[" + index.to_string() + "]",
      ) {
      Ok(_) => ()
      Err(diagnostics) => return Err(diagnostics)
    }
  }
  Ok(())
}

///|
/// Validate a result value when this method declares a result schema.
pub fn Method::validate_result(
  self : Method,
  value : Json,
) -> Result[Unit, Array[Diagnostic]] {
  match self.result {
    None => Ok(())
    Some(result) => result.validate(value)
  }
}

///|
fn validate_named_params(
  declarations : Array[Parameter],
  params : Map[String, Json],
  path_prefix : String,
) -> Result[Unit, Array[Diagnostic]] {
  for parameter in declarations {
    if parameter.required && params.get(parameter.name) is None {
      return Err([
        diagnostic(
          path_prefix + ".params." + parameter.name,
          MissingParameter,
          "required parameter is missing",
        ),
      ])
    }
  }
  for name, _value in params {
    let mut declared = false
    for parameter in declarations {
      if parameter.name == name {
        declared = true
        break
      }
    }
    if !declared {
      return Err([
        diagnostic(
          path_prefix + ".params." + name,
          UnknownParameter,
          "parameter is not declared by the method",
        ),
      ])
    }
  }
  Ok(())
}

///|
fn validate_positional_params(
  declarations : Array[Parameter],
  values : Array[Json],
  path_prefix : String,
) -> Result[Unit, Array[Diagnostic]] {
  if values.length() > declarations.length() {
    return Err([
      diagnostic(
        path_prefix + ".params",
        TooManyParameters,
        "too many positional parameters were provided",
      ),
    ])
  }
  for index, parameter in declarations {
    if index >= values.length() && parameter.required {
      return Err([
        diagnostic(
          path_prefix + ".params[" + index.to_string() + "]",
          MissingParameter,
          "required parameter is missing",
        ),
      ])
    }
  }
  Ok(())
}

///|
pub fn Method::decode_response(
  _self : Method,
  response : Json,
  request_id : Json,
) -> Result[RpcResponse, Array[Diagnostic]] {
  guard response is Object(response_object) else {
    return Err([
      diagnostic("$", InvalidResponse, "JSON-RPC response must be an object"),
    ])
  }
  match response_object.get("jsonrpc") {
    Some(String("2.0")) => ()
    Some(_) | None =>
      return Err([
        diagnostic(
          "$.jsonrpc",
          InvalidResponse,
          "response jsonrpc must be the string 2.0",
        ),
      ])
  }
  let response_id = match response_object.get("id") {
    Some(value) => value
    None =>
      return Err([
        diagnostic("$.id", InvalidResponse, "response id is required"),
      ])
  }
  if !Json::equal(response_id, request_id) {
    return Err([
      diagnostic(
        "$.id",
        ResponseIdMismatch,
        "response id does not match the request id",
      ),
    ])
  }
  let result = response_object.get("result")
  let error = response_object.get("error")
  if result is Some(_) && error is Some(_) {
    return Err([
      diagnostic(
        "$",
        InvalidResponse,
        "response must contain either result or error, not both",
      ),
    ])
  }
  match result {
    Some(value) => return Ok(RpcResponse::Result(value))
    None => ()
  }
  match error {
    Some(value) => decode_rpc_error(value)
    None =>
      Err([
        diagnostic(
          "$",
          InvalidResponse,
          "response must contain a result or error",
        ),
      ])
  }
}

///|
/// Decode and correlate a JSON-RPC batch response with its original calls.
///
/// Servers may return responses in any order. The returned array is reordered
/// to match `calls`, making downstream result handling deterministic while
/// preserving each method name and structured error payload.
pub fn Document::decode_batch_response(
  self : Document,
  response : Json,
  calls : Array[BatchCall],
) -> Result[Array[BatchResponse], Array[Diagnostic]] {
  if calls.length() == 0 {
    return Err([
      diagnostic(
        "$",
        EmptyBatch,
        "a batch response needs at least one expected call",
      ),
    ])
  }
  let response_values = match response {
    Array(values) => values
    _ =>
      return Err([
        diagnostic(
          "$",
          InvalidBatchResponse,
          "a batch response must be a JSON array",
        ),
      ])
  }
  if response_values.length() == 0 {
    return Err([
      diagnostic(
        "$",
        InvalidBatchResponse,
        "a batch response array cannot be empty",
      ),
    ])
  }
  let seen_ids : Array[Json] = []
  let decoded : Array[BatchResponse] = []
  for response_index, response_value in response_values {
    guard response_value is Object(response_object) else {
      return Err([
        diagnostic(
          "$[" + response_index.to_string() + "]",
          InvalidBatchResponse,
          "each batch response must be an object",
        ),
      ])
    }
    let response_id = match response_object.get("id") {
      Some(value) => value
      None =>
        return Err([
          diagnostic(
            "$[" + response_index.to_string() + "].id",
            InvalidBatchResponse,
            "each batch response must contain an id",
          ),
        ])
    }
    for previous_id in seen_ids {
      if Json::equal(previous_id, response_id) {
        return Err([
          diagnostic(
            "$[" + response_index.to_string() + "].id",
            DuplicateResponseId,
            "a batch response id may occur only once",
          ),
        ])
      }
    }
    seen_ids.push(response_id)
    let call_index : Int? = find_batch_call_index(calls, response_id)
    let call = match call_index {
      Some(index) => calls[index]
      None =>
        return Err([
          diagnostic(
            "$[" + response_index.to_string() + "].id",
            UnknownResponseId,
            "response id is not present in the original calls",
          ),
        ])
    }
    let method_value = match self.find_method(call.method_name) {
      Some(value) => value
      None =>
        return Err([
          diagnostic(
            "$[" + response_index.to_string() + "].method",
            UnknownMethod,
            "the original call method is not declared by the document",
          ),
        ])
    }
    let decoded_response = match
      method_value.decode_response(response_value, response_id) {
      Ok(value) => value
      Err(diagnostics) => return Err(diagnostics)
    }
    decoded.push({
      id: response_id,
      method_name: call.method_name,
      response: decoded_response,
    })
  }
  let ordered : Array[BatchResponse] = []
  for call_index, call in calls {
    let mut matched : BatchResponse? = None
    for value in decoded {
      if Json::equal(value.id, call.id) {
        matched = Some(value)
        break
      }
    }
    match matched {
      Some(value) => ordered.push(value)
      None =>
        return Err([
          diagnostic(
            "$.responses[" + call_index.to_string() + "]",
            MissingResponse,
            "the original call has no matching response",
          ),
        ])
    }
  }
  Ok(ordered)
}

///|
/// Find a call by JSON structural id equality.
fn find_batch_call_index(calls : Array[BatchCall], response_id : Json) -> Int? {
  for index, call in calls {
    if Json::equal(call.id, response_id) {
      return Some(index)
    }
  }
  None
}

///|
fn decode_rpc_error(value : Json) -> Result[RpcResponse, Array[Diagnostic]] {
  guard value is Object(error_object) else {
    return Err([
      diagnostic("$.error", InvalidRpcError, "error must be an object"),
    ])
  }
  let code = match error_object.get("code") {
    Some(Number(number, ..)) => {
      let integer = number.to_int()
      if integer.to_double() != number {
        return Err([
          diagnostic(
            "$.error.code",
            InvalidRpcError,
            "error code must be an integer",
          ),
        ])
      }
      integer
    }
    Some(_) =>
      return Err([
        diagnostic(
          "$.error.code",
          InvalidRpcError,
          "error code must be a number",
        ),
      ])
    None =>
      return Err([
        diagnostic("$.error.code", InvalidRpcError, "error code is required"),
      ])
  }
  let message = match error_object.get("message") {
    Some(String(value)) => value
    Some(_) =>
      return Err([
        diagnostic(
          "$.error.message",
          InvalidRpcError,
          "error message must be a string",
        ),
      ])
    None =>
      return Err([
        diagnostic(
          "$.error.message",
          InvalidRpcError,
          "error message is required",
        ),
      ])
  }
  Ok(RpcResponse::Error({ code, message, data: error_object.get("data"), }))
}

///|
fn parse_document(value : Json) -> Result[Document, Array[Diagnostic]] {
  guard value is Object(root) else {
    return Err([
      diagnostic("$", ExpectedObject, "OpenRPC document must be an object"),
    ])
  }
  let version = match root.get("openrpc") {
    Some(String(value)) => value
    Some(_) =>
      return Err([
        diagnostic("$.openrpc", InvalidField, "openrpc must be a string"),
      ])
    None =>
      return Err([
        diagnostic("$.openrpc", MissingField, "required field is missing"),
      ])
  }
  if !version.has_prefix("1.") {
    return Err([
      diagnostic(
        "$.openrpc",
        UnsupportedVersion,
        "only OpenRPC 1.x documents are supported",
      ),
    ])
  }
  let info = match root.get("info") {
    Some(Object(info)) =>
      match parse_info(info) {
        Ok(value) => value
        Err(diagnostics) => return Err(diagnostics)
      }
    Some(_) =>
      return Err([diagnostic("$.info", InvalidField, "info must be an object")])
    None =>
      return Err([
        diagnostic("$.info", MissingField, "required field is missing"),
      ])
  }
  let methods = match root.get("methods") {
    Some(Array(methods)) =>
      match parse_methods(methods, value) {
        Ok(value) => value
        Err(diagnostics) => return Err(diagnostics)
      }
    Some(_) =>
      return Err([
        diagnostic("$.methods", InvalidField, "methods must be an array"),
      ])
    None =>
      return Err([
        diagnostic("$.methods", MissingField, "required field is missing"),
      ])
  }
  let servers = match parse_optional_servers(root.get("servers"), "$.servers") {
    Ok(value) => value
    Err(diagnostics) => return Err(diagnostics)
  }
  let tags = match parse_optional_tags(root.get("tags"), "$.tags", value) {
    Ok(value) => value
    Err(diagnostics) => return Err(diagnostics)
  }
  let components = match parse_components(root.get("components")) {
    Ok(value) => value
    Err(diagnostics) => return Err(diagnostics)
  }
  Ok({
    openrpc: version,
    info,
    methods,
    servers,
    tags,
    components,
    source: value,
  })
}

///|
fn parse_info(value : Map[String, Json]) -> Result[Info, Array[Diagnostic]] {
  let title = match value.get("title") {
    Some(String(value)) => value
    Some(_) =>
      return Err([
        diagnostic("$.info.title", InvalidField, "title must be a string"),
      ])
    None =>
      return Err([
        diagnostic("$.info.title", MissingField, "required field is missing"),
      ])
  }
  let version = match value.get("version") {
    Some(String(value)) => value
    Some(_) =>
      return Err([
        diagnostic("$.info.version", InvalidField, "version must be a string"),
      ])
    None =>
      return Err([
        diagnostic("$.info.version", MissingField, "required field is missing"),
      ])
  }
  let description = match
    parse_optional_string(value, "description", "$.info") {
    Ok(value) => value
    Err(diagnostics) => return Err(diagnostics)
  }
  Ok({ title, version, description, })
}

///|
fn parse_methods(
  values : Array[Json],
  root : Json,
) -> Result[Array[Method], Array[Diagnostic]] {
  let methods : Array[Method] = []
  let names : Map[String, Bool] = Map([])
  for index, raw_value in values {
    let method_path = "$.methods[" + index.to_string() + "]"
    let value = match resolve_reference_value(root, raw_value, method_path) {
      Ok(value) => value
      Err(diagnostics) => return Err(diagnostics)
    }
    guard value is Object(method_object) else {
      return Err([
        diagnostic(method_path, ExpectedObject, "each method must be an object"),
      ])
    }
    let name = match method_object.get("name") {
      Some(String(value)) => value
      Some(_) =>
        return Err([
          diagnostic(
            method_path + ".name",
            InvalidField,
            "method name must be a string",
          ),
        ])
      None =>
        return Err([
          diagnostic(
            method_path + ".name",
            MissingField,
            "required field is missing",
          ),
        ])
    }
    if names.get(name) is Some(_) {
      return Err([
        diagnostic(
          method_path + ".name",
          DuplicateMethod,
          "method names must be unique",
        ),
      ])
    }
    names[name] = true
    let params = match method_object.get("params") {
      None => []
      Some(Array(values)) =>
        match parse_parameters(values, method_path, root) {
          Ok(value) => value
          Err(diagnostics) => return Err(diagnostics)
        }
      Some(_) =>
        return Err([
          diagnostic(
            method_path + ".params",
            InvalidField,
            "params must be an array",
          ),
        ])
    }
    let result = match method_object.get("result") {
      None => None
      Some(raw_result) => {
        let resolved_result = match
          resolve_reference_value(root, raw_result, method_path + ".result") {
          Ok(value) => value
          Err(diagnostics) => return Err(diagnostics)
        }
        match resolved_result {
          Object(value) =>
            match parse_result(value, method_path, root) {
              Ok(value) => Some(value)
              Err(diagnostics) => return Err(diagnostics)
            }
          _ =>
            return Err([
              diagnostic(
                method_path + ".result",
                InvalidField,
                "result must be an object or reference",
              ),
            ])
        }
      }
    }
    let summary = match
      parse_optional_string(method_object, "summary", method_path) {
      Ok(value) => value
      Err(diagnostics) => return Err(diagnostics)
    }
    let description = match
      parse_optional_string(method_object, "description", method_path) {
      Ok(value) => value
      Err(diagnostics) => return Err(diagnostics)
    }
    let deprecated = match
      parse_optional_bool(method_object, "deprecated", method_path) {
      Ok(Some(value)) => value
      Ok(None) => false
      Err(diagnostics) => return Err(diagnostics)
    }
    let tags = match
      parse_optional_tags(
        method_object.get("tags"),
        method_path + ".tags",
        root,
      ) {
      Ok(value) => value
      Err(diagnostics) => return Err(diagnostics)
    }
    let servers = match
      parse_optional_servers(
        method_object.get("servers"),
        method_path + ".servers",
      ) {
      Ok(value) => value
      Err(diagnostics) => return Err(diagnostics)
    }
    let examples = match
      parse_optional_example_pairings(
        method_object.get("examples"),
        method_path + ".examples",
        root,
      ) {
      Ok(value) => value
      Err(diagnostics) => return Err(diagnostics)
    }
    methods.push({
      name,
      params,
      result,
      summary,
      description,
      deprecated,
      tags,
      servers,
      examples,
    })
  }
  Ok(methods)
}

///|
fn parse_parameters(
  values : Array[Json],
  method_path : String,
  root : Json,
) -> Result[Array[Parameter], Array[Diagnostic]] {
  let params : Array[Parameter] = []
  for index, raw_value in values {
    let param_path = method_path + ".params[" + index.to_string() + "]"
    let value = match resolve_reference_value(root, raw_value, param_path) {
      Ok(value) => value
      Err(diagnostics) => return Err(diagnostics)
    }
    guard value is Object(parameter_object) else {
      return Err([
        diagnostic(
          param_path,
          ExpectedObject,
          "each parameter must be an object",
        ),
      ])
    }
    let name = match parameter_object.get("name") {
      Some(String(value)) => value
      Some(_) =>
        return Err([
          diagnostic(
            param_path + ".name",
            InvalidField,
            "parameter name must be a string",
          ),
        ])
      None =>
        return Err([
          diagnostic(
            param_path + ".name",
            MissingField,
            "required field is missing",
          ),
        ])
    }
    let required = match parameter_object.get("required") {
      None => false
      Some(True) => true
      Some(False) => false
      Some(_) =>
        return Err([
          diagnostic(
            param_path + ".required",
            InvalidField,
            "required must be a boolean",
          ),
        ])
    }
    let schema = match parameter_object.get("schema") {
      Some(value) => value
      None =>
        return Err([
          diagnostic(
            param_path + ".schema",
            MissingField,
            "required field is missing",
          ),
        ])
    }
    let description = match
      parse_optional_string(parameter_object, "description", param_path) {
      Ok(value) => value
      Err(diagnostics) => return Err(diagnostics)
    }
    let examples = match
      parse_optional_examples(
        parameter_object.get("examples"),
        param_path + ".examples",
        root,
      ) {
      Ok(value) => value
      Err(diagnostics) => return Err(diagnostics)
    }
    params.push({ name, required, schema, description, examples, })
  }
  Ok(params)
}

///|
fn parse_result(
  value : Map[String, Json],
  method_path : String,
  root : Json,
) -> Result[ResultDescriptor, Array[Diagnostic]] {
  let name = match value.get("name") {
    Some(String(value)) => value
    Some(_) =>
      return Err([
        diagnostic(
          method_path + ".result.name",
          InvalidField,
          "result name must be a string",
        ),
      ])
    None =>
      return Err([
        diagnostic(
          method_path + ".result.name",
          MissingField,
          "required field is missing",
        ),
      ])
  }
  let schema = match value.get("schema") {
    Some(value) => value
    None =>
      return Err([
        diagnostic(
          method_path + ".result.schema",
          MissingField,
          "required field is missing",
        ),
      ])
  }
  let description = match
    parse_optional_string(value, "description", method_path + ".result") {
    Ok(value) => value
    Err(diagnostics) => return Err(diagnostics)
  }
  let examples = match
    parse_optional_examples(
      value.get("examples"),
      method_path + ".result.examples",
      root,
    ) {
    Ok(value) => value
    Err(diagnostics) => return Err(diagnostics)
  }
  Ok({ name, schema, description, examples, })
}

///|
fn diagnostic(
  path : String,
  code : DiagnosticCode,
  message : String,
) -> Diagnostic {
  { path, code, message, }
}