///|
pub(all) enum Path {
  Root
  Key(Path, String)
  Index(Path, Int)
} derive(Eq)

///|
pub suberror DecodeError (Path, Json)

///|
pub impl Show for Path with output(self, buf) {
  match self {
    Root => buf.write_string("$root")
    Key(path, key) => {
      path.output(buf)
      buf..write_string(".")..write_string(key)
    }
    Index(path, idx) => {
      path.output(buf)
      buf..write_string(".")..write_string(idx.to_string())
    }
  }
}

///|
pub impl Show for DecodeError with output(self, buf) {
  let DecodeError((path, json)) = self
  buf
  ..write_string("Failed to decode json at ")
  ..write_string(path.to_string())
  ..write_string(":\n")
  ..write_string(json.stringify(indent=2))
}

///|
/// convert value to json
pub(open) trait Encode {
  #as_free_fn
  encode(Self) -> Json
}

///|
/// convert json to value
pub(open) trait Decode {
  decode(Json, Path) -> Self raise DecodeError
}

///|
pub fn[T : Decode] decode(json : Json, path? : Path) -> T raise DecodeError {
  Decode::decode(json, path.unwrap_or(Root))
}

///|
pub fn[T] fail(path : Path, json : Json) -> T raise DecodeError {
  raise DecodeError((path, json))
}