standard.go (3928B)
1 package errors 2 3 import ( 4 "errors" 5 "reflect" 6 _ "unsafe" 7 8 "codeberg.org/gruf/go-bitutil" 9 ) 10 11 // errtype is a ptr to the error interface type. 12 var errtype = reflect.TypeOf((*error)(nil)).Elem() 13 14 // Comparable is functionally equivalent to calling errors.Is() on multiple errors (up to a max of 64). 15 func Comparable(err error, targets ...error) bool { 16 var flags bitutil.Flags64 17 18 // Flags only has 64 bit-slots 19 if len(targets) > 64 { 20 panic("too many targets") 21 } 22 23 for i := 0; i < len(targets); { 24 if targets[i] == nil { 25 if err == nil { 26 return true 27 } 28 29 // Drop nil targets from slice. 30 copy(targets[i:], targets[i+1:]) 31 targets = targets[:len(targets)-1] 32 continue 33 } 34 35 // Check if this error is directly comparable 36 if reflect.TypeOf(targets[i]).Comparable() { 37 flags = flags.Set(uint8(i)) 38 } 39 40 i++ 41 } 42 43 for err != nil { 44 // Check if this layer supports .Is interface 45 is, ok := err.(interface{ Is(error) bool }) 46 47 if !ok { 48 // Error does not support interface 49 // 50 // Only try perform direct compare 51 for i := 0; i < len(targets); i++ { 52 // Try directly compare errors 53 if flags.Get(uint8(i)) && 54 err == targets[i] { 55 return true 56 } 57 } 58 } else { 59 // Error supports the .Is interface 60 // 61 // Perform direct compare AND .Is() 62 for i := 0; i < len(targets); i++ { 63 if (flags.Get(uint8(i)) && 64 err == targets[i]) || 65 is.Is(targets[i]) { 66 return true 67 } 68 } 69 } 70 71 // Unwrap to next layer 72 err = errors.Unwrap(err) 73 } 74 75 return false 76 } 77 78 // Assignable is functionally equivalent to calling errors.As() on multiple errors, 79 // except that it only checks assignability as opposed to setting the target. 80 func Assignable(err error, targets ...error) bool { 81 if err == nil { 82 // Fastest case. 83 return false 84 } 85 86 for i := 0; i < len(targets); { 87 if targets[i] == nil { 88 // Drop nil targets from slice. 89 copy(targets[i:], targets[i+1:]) 90 targets = targets[:len(targets)-1] 91 continue 92 } 93 i++ 94 } 95 96 for err != nil { 97 // Check if this layer supports .As interface 98 as, ok := err.(interface{ As(any) bool }) 99 100 // Get reflected err type. 101 te := reflect.TypeOf(err) 102 103 if !ok { 104 // Error does not support interface. 105 // 106 // Check assignability using reflection. 107 for i := 0; i < len(targets); i++ { 108 tt := reflect.TypeOf(targets[i]) 109 if te.AssignableTo(tt) { 110 return true 111 } 112 } 113 } else { 114 // Error supports the .As interface. 115 // 116 // Check using .As() and reflection. 117 for i := 0; i < len(targets); i++ { 118 if as.As(targets[i]) { 119 return true 120 } else if tt := reflect.TypeOf(targets[i]); // nocollapse 121 te.AssignableTo(tt) { 122 return true 123 } 124 } 125 } 126 127 // Unwrap to next layer. 128 err = errors.Unwrap(err) 129 } 130 131 return false 132 } 133 134 // As finds the first error in err's tree that matches target, and if one is found, sets 135 // target to that error value and returns true. Otherwise, it returns false. 136 // 137 // The tree consists of err itself, followed by the errors obtained by repeatedly 138 // calling Unwrap. When err wraps multiple errors, As examines err followed by a 139 // depth-first traversal of its children. 140 // 141 // An error matches target if the error's concrete value is assignable to the value 142 // pointed to by target, or if the error has a method As(interface{}) bool such that 143 // As(target) returns true. In the latter case, the As method is responsible for 144 // setting target. 145 // 146 // An error type might provide an As method so it can be treated as if it were a 147 // different error type. 148 // 149 // As panics if target is not a non-nil pointer to either a type that implements 150 // error, or to any interface type. 151 // 152 //go:linkname As errors.As 153 func As(err error, target any) bool 154 155 // Unwrap returns the result of calling the Unwrap method on err, if err's 156 // type contains an Unwrap method returning error. Otherwise, Unwrap returns nil. 157 // 158 //go:linkname Unwrap errors.Unwrap 159 func Unwrap(err error) error