// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

///|
/// Where something went wrong.
///
/// The offset is in UTF-16 code units from the start, which is what a
/// [`StringView`] indexes by; line and column are one-based, because that is
/// how every editor counts and an error a person cannot find in their file is
/// half an error.
pub(all) struct At {
  at : Int
  line : Int
  column : Int
} derive(Eq, Debug)

///|
pub extend At with Eq::{equal, not_equal}

///|
pub extend At with Debug::{to_repr}

///|
/// Why a document could not be read, and where.
pub(all) suberror Malformed {
  /// A character that cannot start or continue what is being read.
  Unexpected(At, Char)
  /// The document ends inside a value.
  Truncated(At)
  /// An escape sequence that is not one, or a lone half of a surrogate pair.
  BadEscape(At)
  /// A number the grammar does not admit — a leading zero, a bare minus, an
  /// exponent with no digits.
  BadNumber(At)
  /// A second value after the first one ended.
  Trailing(At)
  /// Nesting past the depth limit. Without this, a document of ten thousand
  /// open brackets would take the stack down with it.
  TooDeep(At)
  /// Bytes that are not UTF-8, when reading a document that arrived as bytes.
  BadUtf8(At)
  /// A member name that appears twice, when the dialect refuses that.
  Repeated(At, String)
} derive(Eq, Debug)

///|
pub extend Malformed with Eq::{equal, not_equal}

///|
pub extend Malformed with Debug::{to_repr}

///|
/// Where an error happened, as a person would say it.
pub fn Malformed::at(self : Malformed) -> At {
  match self {
    Unexpected(spot, _) => spot
    Truncated(spot)
    | BadEscape(spot)
    | BadNumber(spot)
    | Trailing(spot)
    | TooDeep(spot)
    | BadUtf8(spot) => spot
    Repeated(spot, _) => spot
  }
}