2022-05-08 17:49:45 +00:00
|
|
|
package errors
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"reflect"
|
2023-01-17 11:25:13 +00:00
|
|
|
_ "unsafe"
|
2022-05-08 17:49:45 +00:00
|
|
|
|
|
|
|
"codeberg.org/gruf/go-bitutil"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Is reports whether any error in err's chain matches any of targets
|
|
|
|
// (up to a max of 64 targets).
|
|
|
|
//
|
|
|
|
// The chain consists of err itself followed by the sequence of errors obtained by
|
|
|
|
// repeatedly calling Unwrap.
|
|
|
|
//
|
|
|
|
// An error is considered to match a target if it is equal to that target or if
|
|
|
|
// it implements a method Is(error) bool such that Is(target) returns true.
|
|
|
|
func Is(err error, targets ...error) bool {
|
|
|
|
var flags bitutil.Flags64
|
|
|
|
|
2023-01-17 11:25:13 +00:00
|
|
|
// Flags only has 64 bit-slots
|
2022-05-08 17:49:45 +00:00
|
|
|
if len(targets) > 64 {
|
|
|
|
panic("too many targets")
|
|
|
|
}
|
|
|
|
|
2022-07-10 15:18:21 +00:00
|
|
|
// Check if error is nil so we can catch
|
|
|
|
// the fast-case where a target is nil
|
|
|
|
isNil := (err == nil)
|
|
|
|
|
2022-05-08 17:49:45 +00:00
|
|
|
for i := 0; i < len(targets); {
|
2022-07-10 15:18:21 +00:00
|
|
|
// Drop nil targets
|
2022-05-08 17:49:45 +00:00
|
|
|
if targets[i] == nil {
|
2022-07-10 15:18:21 +00:00
|
|
|
if isNil /* match! */ {
|
|
|
|
return true
|
|
|
|
}
|
2022-05-08 17:49:45 +00:00
|
|
|
targets = append(targets[:i], targets[i+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 })
|
|
|
|
|
2023-01-17 11:25:13 +00:00
|
|
|
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
|
|
|
|
}
|
2022-05-08 17:49:45 +00:00
|
|
|
}
|
2023-01-17 11:25:13 +00:00
|
|
|
} 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
|
|
|
|
}
|
2022-05-08 17:49:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unwrap to next layer
|
|
|
|
err = errors.Unwrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// As finds the first error in err's chain that matches target, and if one is found, sets
|
|
|
|
// target to that error value and returns true. Otherwise, it returns false.
|
|
|
|
//
|
|
|
|
// The chain consists of err itself followed by the sequence of errors obtained by
|
|
|
|
// repeatedly calling Unwrap.
|
|
|
|
//
|
|
|
|
// 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.
|
2023-01-17 11:25:13 +00:00
|
|
|
//
|
|
|
|
//go:linkname As errors.As
|
|
|
|
func As(err error, target interface{}) bool
|
2022-05-08 17:49:45 +00:00
|
|
|
|
|
|
|
// 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.
|
2023-01-17 11:25:13 +00:00
|
|
|
//
|
|
|
|
//go:linkname Unwrap errors.Unwrap
|
|
|
|
func Unwrap(err error) error
|