128 lines
2.4 KiB
Go
128 lines
2.4 KiB
Go
package errors
|
|
|
|
import (
|
|
"reflect"
|
|
)
|
|
|
|
func Wrap(err error, message string) error {
|
|
return &UniverseErrorImpl{
|
|
msg: message,
|
|
cause: err,
|
|
stack: CaptureStackIfEnabled(),
|
|
}
|
|
}
|
|
|
|
func Get[T *any](err error, field string) T {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
var uerr UniverseError
|
|
if As(err, &uerr) {
|
|
value := uerr.GetAny(field)
|
|
if value, ok := value.(T); ok {
|
|
return value
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func Unwrap(err error) error {
|
|
u, ok := err.(interface {
|
|
Unwrap() error
|
|
})
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return u.Unwrap()
|
|
}
|
|
|
|
func Is(err, target error) bool {
|
|
if err == nil || target == nil {
|
|
return err == target
|
|
}
|
|
|
|
isComparable := reflect.TypeOf(target).Comparable()
|
|
return is(err, target, isComparable)
|
|
}
|
|
|
|
func is(err, target error, targetComparable bool) bool {
|
|
for {
|
|
if targetComparable && err == target {
|
|
return true
|
|
}
|
|
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
|
|
return true
|
|
}
|
|
switch x := err.(type) {
|
|
case interface{ Unwrap() error }:
|
|
err = x.Unwrap()
|
|
if err == nil {
|
|
return false
|
|
}
|
|
case interface{ Unwrap() []error }:
|
|
for _, err := range x.Unwrap() {
|
|
if is(err, target, targetComparable) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
func As(err error, target any) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
if target == nil {
|
|
panic("errors: target cannot be nil")
|
|
}
|
|
val := reflect.ValueOf(target)
|
|
typ := val.Type()
|
|
if typ.Kind() != reflect.Ptr || val.IsNil() {
|
|
panic("errors: target must be a non-nil pointer")
|
|
}
|
|
targetType := typ.Elem()
|
|
if targetType.Kind() != reflect.Interface && !targetType.Implements(errorType) {
|
|
panic("errors: *target must be interface or implement error")
|
|
}
|
|
return as(err, target, val, targetType)
|
|
}
|
|
|
|
func as(err error, target any, targetVal reflect.Value, targetType reflect.Type) bool {
|
|
for {
|
|
if reflect.TypeOf(err).AssignableTo(targetType) {
|
|
targetVal.Elem().Set(reflect.ValueOf(err))
|
|
return true
|
|
}
|
|
if x, ok := err.(interface{ As(any) bool }); ok && x.As(target) {
|
|
return true
|
|
}
|
|
switch x := err.(type) {
|
|
case interface{ Unwrap() error }:
|
|
err = x.Unwrap()
|
|
if err == nil {
|
|
return false
|
|
}
|
|
case interface{ Unwrap() []error }:
|
|
for _, err := range x.Unwrap() {
|
|
if err == nil {
|
|
continue
|
|
}
|
|
if as(err, target, targetVal, targetType) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|