112 lines
3.1 KiB
Go
112 lines
3.1 KiB
Go
package error
|
|
|
|
import "errors"
|
|
|
|
// Class describes a top-level operational error class that can be used by
|
|
// higher layers to route failures to the appropriate UI or transport handler.
|
|
type Class string
|
|
|
|
const (
|
|
// ClassConnection marks connectivity and transport failures talking to remote services.
|
|
ClassConnection Class = "connection"
|
|
// ClassStorage marks local persistence and filesystem related failures.
|
|
ClassStorage Class = "storage"
|
|
// ClassService marks remote service contract and processing failures.
|
|
ClassService Class = "service"
|
|
)
|
|
|
|
// ClassifiedError wraps another error with a top-level error class.
|
|
//
|
|
// The wrapped error remains available through Unwrap so existing callers may
|
|
// continue using errors.Is or errors.As against the original cause.
|
|
type ClassifiedError struct {
|
|
class Class
|
|
err error
|
|
}
|
|
|
|
// Error returns either the wrapped error text or, when no wrapped error is available,
|
|
// the textual representation of the class itself.
|
|
func (e *ClassifiedError) Error() string {
|
|
if e == nil {
|
|
return ""
|
|
}
|
|
if e.err != nil {
|
|
return e.err.Error()
|
|
}
|
|
return string(e.class)
|
|
}
|
|
|
|
// Unwrap exposes the wrapped cause for standard Go error inspection.
|
|
func (e *ClassifiedError) Unwrap() error {
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
return e.err
|
|
}
|
|
|
|
// Class returns the top-level class recorded on this error.
|
|
func (e *ClassifiedError) Class() Class {
|
|
if e == nil {
|
|
return ""
|
|
}
|
|
return e.class
|
|
}
|
|
|
|
// Is reports class equality so errors.Is(err, ErrConnection) style checks remain possible.
|
|
func (e *ClassifiedError) Is(target error) bool {
|
|
t, ok := target.(*ClassifiedError)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return e.class != "" && e.class == t.class
|
|
}
|
|
|
|
var (
|
|
// ErrConnection is a class sentinel for connection related failures.
|
|
ErrConnection = &ClassifiedError{class: ClassConnection}
|
|
// ErrStorage is a class sentinel for storage related failures.
|
|
ErrStorage = &ClassifiedError{class: ClassStorage}
|
|
// ErrService is a class sentinel for service related failures.
|
|
ErrService = &ClassifiedError{class: ClassService}
|
|
)
|
|
|
|
// WrapConnection wraps err with the connection class unless it is already classified.
|
|
func WrapConnection(err error) error {
|
|
return wrapClass(ClassConnection, err)
|
|
}
|
|
|
|
// WrapStorage wraps err with the storage class unless it is already classified.
|
|
func WrapStorage(err error) error {
|
|
return wrapClass(ClassStorage, err)
|
|
}
|
|
|
|
// WrapService wraps err with the service class unless it is already classified.
|
|
func WrapService(err error) error {
|
|
return wrapClass(ClassService, err)
|
|
}
|
|
|
|
// IsConnection reports whether err is classified as a connection failure.
|
|
func IsConnection(err error) bool {
|
|
return errors.Is(err, ErrConnection)
|
|
}
|
|
|
|
// IsStorage reports whether err is classified as a storage failure.
|
|
func IsStorage(err error) bool {
|
|
return errors.Is(err, ErrStorage)
|
|
}
|
|
|
|
// IsService reports whether err is classified as a service failure.
|
|
func IsService(err error) bool {
|
|
return errors.Is(err, ErrService)
|
|
}
|
|
|
|
func wrapClass(class Class, err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if existing, ok := errors.AsType[*ClassifiedError](err); ok && existing.class == class {
|
|
return err
|
|
}
|
|
return &ClassifiedError{class: class, err: err}
|
|
}
|