///|
/// Errors raised by byte-level and SQLite format parsers.
pub(all) suberror ParseError {
  UnexpectedEnd(Int, Int, Int, String)
  InvalidRange(Int, Int, Int, String)
  InvalidMagic(String, String)
  InvalidValue(Int, String, String)
  InvalidPageNumber(UInt64, UInt64)
  IntegerOverflow(String)
  Unsupported(String)
} derive(Eq, Debug)

///|
/// Stable severity levels used by validation and consistency reports.
pub(all) enum Severity {
  Info
  Warning
  Error
} derive(Eq, Compare, Debug)

///|
/// A non-fatal parser or validation observation.
pub(all) struct Diagnostic {
  severity : Severity
  code : String
  message : String
  offset : Int?
  page_number : UInt64?
} derive(Eq, Debug)

///|
pub fn Diagnostic::info(
  code : String,
  message : String,
  offset? : Int,
  page_number? : UInt64,
) -> Diagnostic {
  { severity: Info, code, message, offset, page_number }
}

///|
pub fn Diagnostic::warning(
  code : String,
  message : String,
  offset? : Int,
  page_number? : UInt64,
) -> Diagnostic {
  { severity: Warning, code, message, offset, page_number }
}

///|
pub fn Diagnostic::error(
  code : String,
  message : String,
  offset? : Int,
  page_number? : UInt64,
) -> Diagnostic {
  { severity: Error, code, message, offset, page_number }
}

///|
pub fn Severity::label(self : Severity) -> String {
  match self {
    Info => "INFO"
    Warning => "WARNING"
    Error => "ERROR"
  }
}

///|
pub fn ParseError::message(self : ParseError) -> String {
  match self {
    UnexpectedEnd(offset, needed, available, context) =>
      context +
      ": truncated input at offset " +
      offset.to_string() +
      "; need " +
      needed.to_string() +
      " byte(s), have " +
      available.to_string()
    InvalidRange(offset, length, total, context) =>
      context +
      ": invalid range offset=" +
      offset.to_string() +
      ", length=" +
      length.to_string() +
      ", input=" +
      total.to_string()
    InvalidMagic(expected, actual) =>
      "invalid magic header; expected " + expected + ", found " + actual
    InvalidValue(offset, field, value) =>
      "invalid " + field + " at offset " + offset.to_string() + ": " + value
    InvalidPageNumber(page, max_page) =>
      "invalid page number " +
      page.to_string() +
      "; maximum is " +
      max_page.to_string()
    IntegerOverflow(context) => context + ": integer overflow"
    Unsupported(feature) => "unsupported SQLite feature: " + feature
  }
}