GoToSocial/vendor/codeberg.org/gruf/go-errors/v2/standard.go

160 lines
3.8 KiB
Go

package errors
import (
"errors"
"reflect"
_ "unsafe"
"codeberg.org/gruf/go-bitutil"
)
// errtype is a ptr to the error interface type.
var errtype = reflect.TypeOf((*error)(nil)).Elem()
// Comparable is functionally equivalent to calling errors.Is() on multiple errors (up to a max of 64).
func Comparable(err error, targets ...error) bool {
var flags bitutil.Flags64
// Flags only has 64 bit-slots
if len(targets) > 64 {
panic("too many targets")
}
for i := 0; i < len(targets); {
if targets[i] == nil {
if err == nil {
return true
}
// Drop nil targets from slice.
copy(targets[i:], targets[i+1:])
targets = targets[:len(targets)-1]
continue
}
// Check if this error is directly comparable
if reflect.TypeOf(targets[i]).Comparable() {
flags = flags.Set(uint8(i))
}
i++
}
for err != nil {
// Check if this layer supports .Is interface
is, ok := err.(interface{ Is(error) bool })
if !ok {
// Error does not support interface
//
// Only try perform direct compare
for i := 0; i < len(targets); i++ {
// Try directly compare errors
if flags.Get(uint8(i)) &&
err == targets[i] {
return true
}
}
} else {
// Error supports the .Is interface
//
// Perform direct compare AND .Is()
for i := 0; i < len(targets); i++ {
if (flags.Get(uint8(i)) &&
err == targets[i]) ||
is.Is(targets[i]) {
return true
}
}
}
// Unwrap to next layer
err = errors.Unwrap(err)
}
return false
}
// Assignable is functionally equivalent to calling errors.As() on multiple errors,
// except that it only checks assignability as opposed to setting the target.
func Assignable(err error, targets ...error) bool {
if err == nil {
// Fastest case.
return false
}
for i := 0; i < len(targets); {
if targets[i] == nil {
// Drop nil targets from slice.
copy(targets[i:], targets[i+1:])
targets = targets[:len(targets)-1]
continue
}
i++
}
for err != nil {
// Check if this layer supports .As interface
as, ok := err.(interface{ As(any) bool })
// Get reflected err type.
te := reflect.TypeOf(err)
if !ok {
// Error does not support interface.
//
// Check assignability using reflection.
for i := 0; i < len(targets); i++ {
tt := reflect.TypeOf(targets[i])
if te.AssignableTo(tt) {
return true
}
}
} else {
// Error supports the .As interface.
//
// Check using .As() and reflection.
for i := 0; i < len(targets); i++ {
if as.As(targets[i]) {
return true
} else if tt := reflect.TypeOf(targets[i]); // nocollapse
te.AssignableTo(tt) {
return true
}
}
}
// Unwrap to next layer.
err = errors.Unwrap(err)
}
return false
}
// As finds the first error in err's tree that matches target, and if one is found, sets
// target to that error value and returns true. Otherwise, it returns false.
//
// The tree consists of err itself, followed by the errors obtained by repeatedly
// calling Unwrap. When err wraps multiple errors, As examines err followed by a
// depth-first traversal of its children.
//
// An error matches target if the error's concrete value is assignable to the value
// pointed to by target, or if the error has a method As(interface{}) bool such that
// As(target) returns true. In the latter case, the As method is responsible for
// setting target.
//
// An error type might provide an As method so it can be treated as if it were a
// different error type.
//
// As panics if target is not a non-nil pointer to either a type that implements
// error, or to any interface type.
//
//go:linkname As errors.As
func As(err error, target any) bool
// Unwrap returns the result of calling the Unwrap method on err, if err's
// type contains an Unwrap method returning error. Otherwise, Unwrap returns nil.
//
//go:linkname Unwrap errors.Unwrap
func Unwrap(err error) error