Where errors stand now #
There is one type: *Error{Result}, and a resultError helper that wraps a C result code. It's a solid foundation, but
three things are missing:
- No
errors.Issupport. Every failure allocates a fresh*Error, so there is nothing to compare against. There are a ton ofvar ge *Error; errors.As(err, &ge) && ge.Result == ResultNoValuereferences in the codebase and the test suite has a helper for exactly this. - There is no operation context. All 71
return resultError(C....)references produce the same string, "ghostty: no value", so a real app can't tell whether the grid ref, the formatter, or the snapshot decoder failed. Why don't we capture the Op on the struct (see my follow-up PR). - Callback errors are wrapped inconsistently. The formatter wraps the reader's original error so
errors.Is(err, io.EOF)works. Other paths that can carry a callback error do not follow the same shape.
Design #
This PR adds one exported sentinel per non-success Result (ErrNoValue, ErrInvalidValue, ...) and gives *Error an Is method that matches on Result.
This means errors.Is(err, ErrNoValue) now works directly and through fmt.Errorf %w wrapping. The existing errors.As usage is unchanged.