This commit is contained in:
juancwu 2026-04-25 18:49:56 +00:00
commit ff30f6c3d6
6 changed files with 302 additions and 1 deletions

89
errx.go Normal file
View file

@ -0,0 +1,89 @@
// 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}
}