// One error type for decoding, carrying where it happened.
//
// The path is what makes a decode failure actionable. A response from
// `getPostThread` is a tree hundreds of nodes deep, and "expected string, got
// integer" without a location is a bug report nobody can act on;
// "thread.post.author.handle" is one somebody can.
//
// It is a plain string rather than a structured list of segments because it is
// only ever read by a person. Building it costs a concatenation per level,
// which is paid only on the failing path -- the helpers in `fields.mbt` pass
// the prefix down and never build a path for a field that decodes.
///|
pub(all) suberror DecodeError {
DecodeError(path~ : String, reason~ : String)
} derive(Eq, Debug)
///|
/// Dotted for fields, bracketed for indices: `feed[0].post.author.did`. Empty
/// at the root, where the path prefix is dropped rather than shown as a leading
/// dot.
pub fn field_path(prefix : String, field : String) -> String {
if prefix == "" {
field
} else {
prefix + "." + field
}
}
///|
pub fn index_path(prefix : String, index : Int) -> String {
prefix + "[" + index.to_string() + "]"
}
///|
pub fn DecodeError::path(self : Self) -> String {
match self {
DecodeError(path~, ..) => path
}
}
///|
pub fn DecodeError::reason(self : Self) -> String {
match self {
DecodeError(reason~, ..) => reason
}
}
///|
pub fn DecodeError::describe_error(self : Self) -> String {
match self {
DecodeError(path~, reason~) =>
if path == "" {
reason
} else {
"\{path}: \{reason}"
}
}
}
///|
pub impl Show for DecodeError with fn output(self, logger) {
logger.write_string(self.describe_error())
}