///|
/// Structured processing stage for errors.
pub(all) enum ProblemErrorStage {
  Input
  Json
  Member
  Uri
  Status
  Limit
  Registry
  Builder
  Context
} derive(Eq, Debug)

///|
/// Machine-readable error category.
pub(all) enum ProblemErrorKind {
  InvalidJson
  InvalidUtf8
  RootNotObject
  InvalidUriReference
  InvalidPercentEncoding
  InvalidStatus
  DuplicateReservedExtension
  LimitExceeded
  InvalidBuilderState
} derive(Eq, Debug)

///|
/// A structured error. JSON parser offsets are not invented when unavailable.
pub(all) suberror ProblemError {
  ProblemError(ProblemErrorStage, ProblemErrorKind, String, Int?)
}

///|
pub fn problem_error(
  stage : ProblemErrorStage,
  kind : ProblemErrorKind,
  context : String,
  byte_offset? : Int,
) -> ProblemError {
  ProblemError(stage, kind, context, byte_offset)
}

///|
pub fn ProblemError::stage(self : ProblemError) -> ProblemErrorStage {
  match self {
    ProblemError(stage, _, _, _) => stage
  }
}

///|
pub fn ProblemError::kind(self : ProblemError) -> ProblemErrorKind {
  match self {
    ProblemError(_, kind, _, _) => kind
  }
}

///|
pub fn ProblemError::context(self : ProblemError) -> String {
  match self {
    ProblemError(_, _, context, _) => context
  }
}

///|
pub fn ProblemError::byte_offset(self : ProblemError) -> Int? {
  match self {
    ProblemError(_, _, _, offset) => offset
  }
}

///|
pub fn ProblemErrorStage::name(self : ProblemErrorStage) -> String {
  match self {
    Input => "Input"
    Json => "Json"
    Member => "Member"
    Uri => "Uri"
    Status => "Status"
    Limit => "Limit"
    Registry => "Registry"
    Builder => "Builder"
    Context => "Context"
  }
}

///|
pub fn ProblemErrorKind::name(self : ProblemErrorKind) -> String {
  match self {
    InvalidJson => "InvalidJson"
    InvalidUtf8 => "InvalidUtf8"
    RootNotObject => "RootNotObject"
    InvalidUriReference => "InvalidUriReference"
    InvalidPercentEncoding => "InvalidPercentEncoding"
    InvalidStatus => "InvalidStatus"
    DuplicateReservedExtension => "DuplicateReservedExtension"
    LimitExceeded => "LimitExceeded"
    InvalidBuilderState => "InvalidBuilderState"
  }
}

///|
pub fn ProblemError::to_string(self : ProblemError) -> String {
  "\{self.stage().name()}/\{self.kind().name()}: \{self.context()}"
}

///|
fn unwrap_problem_error(error : Error) -> ProblemError {
  match error {
    ProblemError(stage, kind, context, offset) =>
      ProblemError(stage, kind, context, offset)
    _ => abort("internal error: unexpected error type")
  }
}