89 lines
2.5 KiB
Go
89 lines
2.5 KiB
Go
// Package errx provides a small error wrapper that records the operation
|
|
// where each error occurred, producing a readable chain in place of a
|
|
// runtime stack trace.
|
|
//
|
|
// Each function declares its own operation name and wraps the underlying
|
|
// error with that op (and an optional message). The resulting error
|
|
// formats top-down as a colon-joined breadcrumb:
|
|
//
|
|
// users.Get: lookup failed: db.Query: connection refused
|
|
//
|
|
// errx is fully compatible with errors.Is and errors.As via Unwrap.
|
|
//
|
|
// Basic usage:
|
|
//
|
|
// const op = "users.Get"
|
|
// row, err := db.Query(...)
|
|
// if err != nil {
|
|
// return errx.Wrap(op, err)
|
|
// }
|
|
package errx
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Error is the concrete error type produced by this package. Op identifies
|
|
// the operation that failed; Msg adds optional context; Err is the
|
|
// underlying error being wrapped (may be nil).
|
|
type Error struct {
|
|
// Op is the operation name, conventionally "package.Func" or
|
|
// "Receiver.Method".
|
|
Op string
|
|
// Msg is an optional context message describing what went wrong.
|
|
Msg string
|
|
// Err is the underlying error being wrapped. May be nil.
|
|
Err error
|
|
}
|
|
|
|
// Error returns the colon-joined chain of op, message, and wrapped error.
|
|
// Empty pieces are omitted.
|
|
func (e *Error) Error() string {
|
|
parts := make([]string, 0, 3)
|
|
if e.Op != "" {
|
|
parts = append(parts, e.Op)
|
|
}
|
|
if e.Msg != "" {
|
|
parts = append(parts, e.Msg)
|
|
}
|
|
if e.Err != nil {
|
|
parts = append(parts, e.Err.Error())
|
|
}
|
|
return strings.Join(parts, ": ")
|
|
}
|
|
|
|
// Unwrap returns the wrapped error, enabling errors.Is and errors.As.
|
|
func (e *Error) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// New returns a new error tagged with op and the static message msg.
|
|
func New(op, msg string) error {
|
|
return &Error{Op: op, Msg: msg}
|
|
}
|
|
|
|
// Newf returns a new error tagged with op and a message formatted per
|
|
// fmt.Sprintf rules.
|
|
func Newf(op, format string, args ...any) error {
|
|
return &Error{Op: op, Msg: fmt.Sprintf(format, args...)}
|
|
}
|
|
|
|
// Wrap returns an error tagged with op that wraps err. If err is nil,
|
|
// Wrap returns nil so callers can write `return errx.Wrap(op, doThing())`
|
|
// without a guard.
|
|
func Wrap(op string, err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
return &Error{Op: op, Err: err}
|
|
}
|
|
|
|
// Wrapf returns an error tagged with op that wraps err and adds a message
|
|
// formatted per fmt.Sprintf rules. If err is nil, Wrapf returns nil.
|
|
func Wrapf(op string, err error, format string, args ...any) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
return &Error{Op: op, Msg: fmt.Sprintf(format, args...), Err: err}
|
|
}
|