///|
using @error {type Path, type Invalid, err}

///|
/// A converted value that still carries its JSON path.
struct Typed[T] {
  val : T
  path : Path
}

///|
/// Unwrap the converted value.
pub fn[T] Typed::value(self : Typed[T]) -> T {
  self.val
}

///|
pub fn[T] Typed::new(val : T, path : Path) -> Typed[T] {
  { val, path, }
}

///|
/// Require `self == expected`.
pub fn[T : Eq + Show] Typed::eq(
  self : Typed[T],
  expected : T,
) -> Typed[T] raise Invalid {
  if self.val == expected {
    self
  } else {
    err(self.path, "must be \{expected}")
  }
}

///|
/// Apply a custom predicate. The message is used when it returns `false`.
pub fn[T] Typed::refine(
  self : Typed[T],
  predicate : (T) -> Bool,
  message : String,
) -> Typed[T] raise Invalid {
  if predicate(self.val) {
    self
  } else {
    err(self.path, message)
  }
}