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} }