feat(veans): add stable error envelope and code constants
This commit is contained in:
parent
3d0039df2d
commit
e4c4837805
|
|
@ -0,0 +1,69 @@
|
|||
// Package output defines stable error codes and the JSON envelope
|
||||
// veans uses for non-zero exits.
|
||||
package output
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Code string
|
||||
|
||||
const (
|
||||
CodeNotFound Code = "NOT_FOUND"
|
||||
CodeConflict Code = "CONFLICT"
|
||||
CodeValidation Code = "VALIDATION_ERROR"
|
||||
CodeAuth Code = "AUTH_ERROR"
|
||||
CodeRateLimited Code = "RATE_LIMITED"
|
||||
CodeBotUsersUnavailable Code = "BOT_USERS_UNAVAILABLE"
|
||||
CodeNotConfigured Code = "NOT_CONFIGURED"
|
||||
CodeUnknown Code = "UNKNOWN"
|
||||
)
|
||||
|
||||
// Error is the structured error type used for both internal flow and the
|
||||
// `--json` mutation envelope. Callers wrap underlying errors with codes via
|
||||
// Wrap; the cobra runner converts unmapped errors to CodeUnknown.
|
||||
type Error struct {
|
||||
Code Code `json:"code"`
|
||||
Message string `json:"error"`
|
||||
Cause error `json:"-"`
|
||||
}
|
||||
|
||||
func (e *Error) Error() string { return e.Message }
|
||||
func (e *Error) Unwrap() error { return e.Cause }
|
||||
|
||||
func New(code Code, format string, args ...any) *Error {
|
||||
return &Error{Code: code, Message: fmt.Sprintf(format, args...)}
|
||||
}
|
||||
|
||||
func Wrap(code Code, cause error, format string, args ...any) *Error {
|
||||
return &Error{Code: code, Message: fmt.Sprintf(format, args...), Cause: cause}
|
||||
}
|
||||
|
||||
// AsError extracts an *Error from any error chain, returning a CodeUnknown
|
||||
// wrapper for plain errors.
|
||||
func AsError(err error) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e
|
||||
}
|
||||
return &Error{Code: CodeUnknown, Message: err.Error(), Cause: err}
|
||||
}
|
||||
|
||||
// EmitError writes the JSON envelope when --json is set, or a plain message
|
||||
// otherwise. Always to stderr so stdout stays parseable.
|
||||
func EmitError(jsonMode bool, err error, w io.Writer) {
|
||||
if w == nil {
|
||||
w = os.Stderr
|
||||
}
|
||||
e := AsError(err)
|
||||
if jsonMode {
|
||||
_ = json.NewEncoder(w).Encode(e)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "veans: %s: %s\n", e.Code, e.Message)
|
||||
}
|
||||
Loading…
Reference in New Issue