loader revisited
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
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}
|
||||
}
|
||||
Reference in New Issue
Block a user