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

///|
/// Where in a document a value was written.
///
/// The offset is in UTF-16 code units from the start of the source, which is
/// what a [`StringView`] indexes by. Line and column are one-based, because
/// that is how an editor counts, and an error a reader cannot find in their file
/// is half an error.
///
/// A source that does not report positions — the JSON, YAML and TOML readers
/// this package borrows keep the tree but not the offsets — fills in
/// `{ at: 0, line: 1, column: 1 }`, and says so where it matters.
pub(all) struct At {
  /// The offset in UTF-16 code units from the start of the source.
  at : Int32
  /// The line the offset is on, counting from one.
  line : Int32
  /// The column the offset is at, counting from one.
  column : Int32
} derive(Eq, Debug)

///|
/// The first position in a source: what to use when nothing better is known.
pub let start : At = { at: 0, line: 1, column: 1, }

///|
/// The position of `offset` in `text`, counted only when something has already
/// gone wrong.
pub fn At::of(text : StringView, offset : Int32) -> At {
  let mut line = 1
  let mut column = 1
  let mut i = 0
  while i < offset && i < text.length() {
    if text.at(i) == '\n' {
      line += 1
      column = 1
    } else {
      column += 1
    }
    i += 1
  }
  { at: offset, line, column, }
}

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

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