gopkg.in/hedzr/errors.v3@v3.3.1/asisunwrap.go (about) 1 package errors 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 ) 8 9 // As finds the first error in `err`'s chain that matches target, 10 // and if so, sets target to that error value and returns true. 11 // 12 // The chain consists of err itself followed by the sequence of errors 13 // obtained by repeatedly calling Unwrap. 14 // 15 // An error matches target if the error's concrete value is assignable 16 // to the value pointed to by target, or if the error has a method 17 // As(interface{}) bool such that As(target) returns true. In the 18 // latter case, the As method is responsible for setting target. 19 // 20 // As will panic if target is not a non-nil pointer to either a 21 // type that implements error, or to any interface type. "As" 22 // returns false if err is nil. 23 func As(err error, target interface{}) bool { //nolint:revive 24 if target == nil { 25 panic("errors: target cannot be nil") 26 } 27 val := reflect.ValueOf(target) 28 typ := val.Type() 29 if typ.Kind() != reflect.Ptr || val.IsNil() { 30 panic("errors: target must be a non-nil pointer") 31 } 32 // e := typ.Elem() 33 // k := e.Kind() 34 // if k != reflect.Interface && k != reflect.Slice && !e.Implements(errorType) { 35 // // panic("errors: *target must be interface or implement error") 36 // return false 37 // } 38 targetType := typ.Elem() 39 for err != nil { 40 if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { //nolint:revive 41 return true 42 } 43 if reflect.TypeOf(err).AssignableTo(targetType) { 44 val.Elem().Set(reflect.ValueOf(err)) 45 return true 46 } 47 err = Unwrap(err) //nolint:revive 48 } 49 return false 50 } 51 52 // AsSlice tests err.As for errs slice 53 func AsSlice(errs []error, target interface{}) bool { //nolint:revive 54 if target == nil { 55 panic("errors: target cannot be nil") 56 } 57 val := reflect.ValueOf(target) 58 typ := val.Type() 59 if typ.Kind() != reflect.Ptr || val.IsNil() { 60 panic("errors: target must be a non-nil pointer") 61 } 62 if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) { 63 // panic("errors: *target must be interface or implement error") 64 return false 65 } 66 targetType := typ.Elem() 67 for _, err := range errs { 68 if reflect.TypeOf(err).AssignableTo(targetType) { 69 val.Elem().Set(reflect.ValueOf(err)) 70 return true 71 } 72 if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { //nolint:revive 73 return true 74 } 75 err = Unwrap(err) //nolint:ineffassign,staticcheck 76 } 77 return false 78 } 79 80 var errorType = reflect.TypeOf((*error)(nil)).Elem() 81 82 // IsAnyOf tests whether any of `targets` is in `err`. 83 func IsAnyOf(err error, targets ...error) bool { 84 for _, tgt := range targets { 85 if Is(err, tgt) { 86 return true 87 } 88 } 89 return false 90 } 91 92 // Is reports whether any error in `err`'s chain matches target. 93 // 94 // The chain consists of err itself followed by the sequence of 95 // errors obtained by repeatedly calling Unwrap. 96 // 97 // An error is considered to match a target if it is equal to that 98 // target or if it implements a method Is(error) bool such that 99 // Is(target) returns true. 100 func Is(err, target error) bool { //nolint:revive 101 if target == nil { 102 return err == nil 103 } 104 105 isComparable := reflect.TypeOf(target).Comparable() 106 tv := reflect.ValueOf(target) 107 // target is not Code-based, try convert source err with target's type, and test whether its plain text message is equal 108 var savedMsg string 109 if !isNil(tv) { 110 savedMsg = target.Error() 111 } 112 for { 113 if isComparable && err == target { 114 return true 115 } 116 if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { 117 return true 118 } 119 if _, ok := target.(Code); !ok { 120 var te Code 121 if ok = As(err, &te); ok && !isNil(reflect.ValueOf(err)) && strings.EqualFold(te.Error(), savedMsg) { 122 return true 123 } 124 } 125 126 // // TODO: consider supporting target.Is(err). This would allow 127 // // user-definable predicates, but also may allow for coping with sloppy 128 // // APIs, thereby making it easier to get away with them. 129 // if err = Unwrap(err); err == nil { 130 // errors.Is() 131 // return false 132 // } 133 134 switch x := err.(type) { 135 case interface{ Unwrap() error }: 136 err = x.Unwrap() //nolint:revive 137 if err == nil { 138 return false 139 } 140 case interface{ Unwrap() []error }: 141 for _, err := range x.Unwrap() { 142 if Is(err, target) { 143 return true 144 } 145 } 146 return false 147 default: 148 return false 149 } 150 } 151 } 152 153 func IsStd(err, target error) bool { //nolint:revive 154 if target == nil { 155 return err == target 156 } 157 158 isComparable := reflect.TypeOf(target).Comparable() 159 for { 160 if isComparable && err == target { 161 return true 162 } 163 if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { 164 return true 165 } 166 switch x := err.(type) { 167 case interface{ Unwrap() error }: 168 err = x.Unwrap() //nolint:revive 169 if err == nil { 170 return false 171 } 172 case interface{ Unwrap() []error }: 173 for _, err := range x.Unwrap() { 174 if Is(err, target) { 175 return true 176 } 177 } 178 return false 179 default: 180 return false 181 } 182 } 183 } 184 185 func Iss(err error, targets ...error) (matched bool) { //nolint:revive 186 if targets == nil { 187 return err == nil 188 } 189 if err == nil { 190 return true 191 } 192 193 for _, target := range targets { 194 isComparable := reflect.TypeOf(target).Comparable() 195 tv := reflect.ValueOf(target) 196 // target is not Code-based, try convert source err with target's type, and test whether its plain text message is equal 197 var savedMsg string 198 if !isNil(tv) { 199 savedMsg = target.Error() 200 } 201 for { 202 if isComparable && err == target { 203 return true 204 } 205 if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { 206 return true 207 } 208 if _, ok := target.(Code); !ok { 209 if ok = As(err, &target); ok && !isNil(reflect.ValueOf(target)) && strings.EqualFold(target.Error(), savedMsg) { 210 return true 211 } 212 } 213 214 // // TODO: consider supporting target.Is(err). This would allow 215 // // user-definable predicates, but also may allow for coping with sloppy 216 // // APIs, thereby making it easier to get away with them. 217 // if err = Unwrap(err); err == nil { 218 // return false 219 // } 220 221 switch x := err.(type) { 222 case interface{ Unwrap() error }: 223 err = x.Unwrap() //nolint:revive 224 if err == nil { 225 return false 226 } 227 case interface{ Unwrap() []error }: 228 for _, err := range x.Unwrap() { 229 if Is(err, target) { 230 return true 231 } 232 } 233 return false 234 default: 235 // return false 236 // 237 // here is a bug which causes the rest errors expect the first one could never be processed. 238 // this bug might cause the unexpected testing result returned. 239 } 240 } 241 } 242 return 243 } 244 245 // isNil for go1.12+, the difference is it never panic on unavailable kinds. 246 // see also reflect.IsNil. 247 func isNil(v reflect.Value) bool { 248 return isNilv(&v) 249 } 250 251 // IsNilv for go1.12+, the difference is it never panic on unavailable kinds. 252 // see also reflect.IsNil. 253 func isNilv(v *reflect.Value) bool { 254 if v != nil { 255 switch k := v.Kind(); k { //nolint:exhaustive //no need 256 case reflect.Uintptr: 257 if v.CanAddr() { 258 return v.UnsafeAddr() == 0 // special: reflect.IsNil assumed nil check on an uintptr is illegal, faint! 259 } 260 case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr: 261 return v.IsNil() 262 case reflect.UnsafePointer: 263 return v.Pointer() == 0 // for go1.11, this is a workaround even not bad 264 case reflect.Interface, reflect.Slice: 265 return v.IsNil() 266 // case reflect.Array: 267 // // never true, for an array, it is never IsNil 268 // case reflect.String: 269 // case reflect.Struct: 270 } 271 } 272 return false 273 } 274 275 // IsSlice tests err.Is for errs slice 276 func IsSlice(errs []error, target error) bool { //nolint:revive 277 if target == nil { 278 // for _, e := range errs { 279 // if e == target { 280 // return true 281 // } 282 // } 283 return false 284 } 285 286 isComparable := reflect.TypeOf(target).Comparable() 287 for { 288 if isComparable { 289 for _, e := range errs { 290 if e == target { 291 return true 292 } 293 } 294 // return false 295 } 296 297 for _, e := range errs { 298 if x, ok := e.(interface{ Is(error) bool }); ok && x.Is(target) { 299 return true 300 } 301 // if err := Unwrap(e); err == nil { 302 // return false 303 // } 304 } 305 return false //nolint:staticcheck 306 } 307 } 308 309 // TypeIs reports whether any error in `err`'s chain matches target. 310 // 311 // The chain consists of err itself followed by the sequence of errors obtained by 312 // repeatedly calling Unwrap. 313 // 314 // An error is considered to match a target if it is equal to that target or if 315 // it implements a method Is(error) bool such that Is(target) returns true. 316 func TypeIs(err, target error) bool { //nolint:revive 317 if target == nil { 318 return err == target 319 } 320 321 isComparable := reflect.TypeOf(target).Comparable() 322 for { 323 if isComparable { 324 if reflect.TypeOf(target) == reflect.TypeOf(err) { 325 return true 326 } 327 } 328 if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { 329 return true 330 } 331 332 // // TODO: consider supporting target.Is(err). This would allow 333 // // user-definable predicates, but also may allow for coping with sloppy 334 // // APIs, thereby making it easier to get away with them. 335 // if err = Unwrap(err); err == nil { 336 // return false 337 // } 338 339 switch x := err.(type) { 340 case interface{ Unwrap() error }: 341 err = x.Unwrap() //nolint:revive 342 if err == nil { 343 return false 344 } 345 case interface{ Unwrap() []error }: 346 for _, err := range x.Unwrap() { 347 if Is(err, target) { 348 return true 349 } 350 } 351 return false 352 default: 353 return false 354 } 355 } 356 } 357 358 // TypeIsSlice tests err.Is for errs slice 359 func TypeIsSlice(errs []error, target error) bool { //nolint:revive 360 if target == nil { 361 // for _, e := range errs { 362 // if e == target { 363 // return true 364 // } 365 // } 366 return false 367 } 368 369 isComparable := reflect.TypeOf(target).Comparable() 370 for { 371 if isComparable { 372 tt := reflect.TypeOf(target) 373 for _, e := range errs { 374 // if e == target { 375 // return true 376 // } 377 if reflect.TypeOf(e) == tt { 378 return true 379 } 380 } 381 // return false 382 } 383 384 for _, e := range errs { 385 if x, ok := e.(interface{ Is(error) bool }); ok && x.Is(target) { 386 return true 387 } 388 // if err := Unwrap(e); err == nil { 389 // return false 390 // } 391 } 392 return false //nolint:staticcheck 393 } 394 } 395 396 // func As(err error, target interface{}) bool 397 // func Is(err, target error) bool 398 // func New(text string) error 399 // func Unwrap(err error) error 400 401 // Unwrap returns the result of calling the Unwrap method on err, if 402 // `err`'s type contains an Unwrap method returning error. 403 // Otherwise, Unwrap returns nil. 404 // 405 // An errors.Error is an unwrappable error object, all its inner errors 406 // can be unwrapped in turn. Therefore it maintains an internal unwrapping 407 // index and it can't be reset externally. The only approach to clear it 408 // and make Unwrap work from head is, to keep Unwrap till this turn ending 409 // by returning nil. 410 // 411 // Examples for Unwrap: 412 // 413 // var err = errors.New("hello").WithErrors(io.EOF, io.ShortBuffers) 414 // var e error = err 415 // for e != nil { 416 // e = errors.Unwrap(err) 417 // // test if e is not nil and process it... 418 // } 419 func Unwrap(err error) error { 420 u, ok := err.(interface { 421 Unwrap() error 422 }) 423 if !ok { 424 return nil 425 } 426 return u.Unwrap() 427 } 428 429 // Wrap returns an error annotating err with a Stack trace 430 // at the point Wrap is called, and the supplied message. 431 // If err is nil, Wrap returns nil. 432 func Wrap(err error, message string, args ...interface{}) *WithStackInfo { //nolint:revive 433 if err == nil { 434 return nil 435 } 436 437 if len(args) > 0 { 438 message = fmt.Sprintf(message, args...) //nolint:revive 439 } 440 441 return &WithStackInfo{ 442 causes2: causes2{ 443 Causers: []error{err}, 444 msg: message, 445 }, 446 Stack: callers(1), 447 } 448 }