/* * Copyright 2026 Flix Authors * * Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE.md file. */pubmod Util.Json.JsonError {use Util.Json.JsonPathuse Util.Json.JsonPath.JsonStepuse Util.Json.JsonPath.{cons, fromList, steps}////// An error that can occur when parsing or decoding JSON.////// `Parse` wraps a parser failure together with the UTF-16 code-unit/// offset into the input where the failure was detected.////// `Decode` wraps a decoder failure together with the path of the failing/// value inside the parsed `Json` document.///pubenumJsonErrorwithEq {case Parse(Int32, ParseErrorKind)case Decode(JsonPath, DecodeErrorKind) }////// A parser-side failure: produced by `Json.parse` while reading raw JSON/// text. Parser failures do not carry a `JsonPath`.///pubenumParseErrorKindwithEq {case DuplicateKey(String)case InvalidEscape(String)case InvalidNumber(String)case InvalidUnicode(String)case MaxDepthExceededcase UnexpectedChar(Char)case UnexpectedEof }////// A decoder-side failure: produced by `FromJson.fromJson` while turning a/// parsed `Json` value into a typed value.///pubenumDecodeErrorKindwithEq {case MissingIndex(Int32)case MissingKey(String)case TypeMismatch(String) }instanceToString[ParseErrorKind] {pubdeftoString(k: ParseErrorKind): String = matchk {case ParseErrorKind.DuplicateKey(k1) => "duplicate key '${k1}'"case ParseErrorKind.InvalidEscape(s) => "invalid escape sequence '${s}'"case ParseErrorKind.InvalidNumber(s) => "invalid number '${s}'"case ParseErrorKind.InvalidUnicode(s) => "invalid unicode '${s}'"case ParseErrorKind.MaxDepthExceeded => "maximum nesting depth exceeded"case ParseErrorKind.UnexpectedChar(c) => "unexpected character '${c}'"case ParseErrorKind.UnexpectedEof => "unexpected end of input" } }instanceToString[DecodeErrorKind] {pubdeftoString(k: DecodeErrorKind): String = matchk {case DecodeErrorKind.MissingIndex(i) => "missing index ${i}"case DecodeErrorKind.MissingKey(k1) => "missing key '${k1}'"case DecodeErrorKind.TypeMismatch(d) => "type mismatch: expected ${d}" } }instanceToString[JsonError] {pubdeftoString(e: JsonError): String = matche {case JsonError.Parse(o, k) => "${ToString.toString(k)} at offset ${o}"case JsonError.Decode(p, k) => matchsteps(p) {case Nil => ToString.toString(k)case_ => "${ToString.toString(k)} at ${p}" } } }instanceFormattable[ParseErrorKind] {pubdefformat(k: ParseErrorKind): RichString = RichString.fromString(ToString.toString(k)) }instanceFormattable[DecodeErrorKind] {pubdefformat(k: DecodeErrorKind): RichString = RichString.fromString(ToString.toString(k)) }instanceFormattable[JsonError] {pubdefformat(e: JsonError): RichString = matche {case JsonError.Parse(o, k) =>Formattable.format(k) +RichString.fromString(" at offset ") +RichString.yellow(Int32.toString(o))case JsonError.Decode(p, k) => matchsteps(p) {case Nil => Formattable.format(k)case_ => Formattable.format(k) +RichString.fromString(" at ") +Formattable.format(p) } } }////// Prepends `s` to the path of `e` if `e` is a `Decode`. Leaves `Parse`/// errors unchanged.////// Used by `FromJson` helpers and recursive instances to thread the path/// of a failing decode lazily — i.e. only on the error path.///pubdefprependStep(s: JsonStep, e: JsonError): JsonError = matche {case JsonError.Decode(p, k) => JsonError.Decode(cons(s, p), k)case JsonError.Parse(_, _) => e }////// Prepends the steps of `p` to the path of `e` if `e` is a `Decode`./// Leaves `Parse` errors unchanged.////// Used to stitch a leaf decoder's inner path onto a navigation prefix/// when decoding at a multi-step path.///pubdefprependPath(p: JsonPath, e: JsonError): JsonError = matche {case JsonError.Decode(p2, k) => JsonError.Decode(fromList(steps(p) :::steps(p2)), k)case JsonError.Parse(_, _) => e }}