Current section

Files

Jump to
oaspec src oaspec internal openapi location_index.gleam
Raw

src/oaspec/internal/openapi/location_index.gleam

import gleam/dict.{type Dict}
import gleam/list
import gleam/result
import oaspec/openapi/diagnostic.{type SourceLoc, NoSourceLoc, SourceLoc}
/// An index mapping dotted JSON-pointer paths to source locations.
/// Built by parsing YAML with yamerl (which preserves line/column),
/// then looked up when emitting diagnostics.
pub opaque type LocationIndex {
LocationIndex(entries: Dict(String, SourceLoc))
}
/// Build a location index from raw YAML/JSON content.
/// On failure (e.g. invalid YAML), returns an empty index.
pub fn build(content: String) -> LocationIndex {
do_build(content)
|> result.map(fn(pairs) {
let entries =
list.fold(pairs, dict.new(), fn(acc, pair) {
let #(path, #(line, col)) = pair
dict.insert(acc, path, SourceLoc(line:, column: col))
})
LocationIndex(entries:)
})
|> result.unwrap(empty())
}
/// An empty index (no location information available).
pub fn empty() -> LocationIndex {
LocationIndex(entries: dict.new())
}
/// Look up the source location for a given path.
/// Returns `NoSourceLoc` if the path is not in the index.
pub fn lookup(index: LocationIndex, path: String) -> SourceLoc {
dict.get(index.entries, path)
|> result.unwrap(NoSourceLoc)
}
/// Look up the source location for a field within a parent path.
/// Tries `parent.field` first, then falls back to `parent`, then `NoSourceLoc`.
pub fn lookup_field(
index: LocationIndex,
parent: String,
field: String,
) -> SourceLoc {
let field_path = case parent {
"" -> field
_ -> parent <> "." <> field
}
dict.get(index.entries, field_path)
|> result.lazy_or(fn() { dict.get(index.entries, parent) })
|> result.unwrap(NoSourceLoc)
}
@external(erlang, "yaml_loc_ffi", "build_location_index")
fn do_build(content: String) -> Result(List(#(String, #(Int, Int))), Nil)