wrap.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. // Copyright 2018 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package xerrors
  5. import (
  6. "reflect"
  7. )
  8. // A Wrapper provides context around another error.
  9. type Wrapper interface {
  10. // Unwrap returns the next error in the error chain.
  11. // If there is no next error, Unwrap returns nil.
  12. Unwrap() error
  13. }
  14. // Opaque returns an error with the same error formatting as err
  15. // but that does not match err and cannot be unwrapped.
  16. func Opaque(err error) error {
  17. return noWrapper{err}
  18. }
  19. type noWrapper struct {
  20. error
  21. }
  22. func (e noWrapper) FormatError(p Printer) (next error) {
  23. if f, ok := e.error.(Formatter); ok {
  24. return f.FormatError(p)
  25. }
  26. p.Print(e.error)
  27. return nil
  28. }
  29. // Unwrap returns the result of calling the Unwrap method on err, if err implements
  30. // Unwrap. Otherwise, Unwrap returns nil.
  31. func Unwrap(err error) error {
  32. u, ok := err.(Wrapper)
  33. if !ok {
  34. return nil
  35. }
  36. return u.Unwrap()
  37. }
  38. // Is reports whether any error in err's chain matches target.
  39. //
  40. // An error is considered to match a target if it is equal to that target or if
  41. // it implements a method Is(error) bool such that Is(target) returns true.
  42. func Is(err, target error) bool {
  43. if target == nil {
  44. return err == target
  45. }
  46. for {
  47. if err == target {
  48. return true
  49. }
  50. if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
  51. return true
  52. }
  53. // TODO: consider supporing target.Is(err). This would allow
  54. // user-definable predicates, but also may allow for coping with sloppy
  55. // APIs, thereby making it easier to get away with them.
  56. if err = Unwrap(err); err == nil {
  57. return false
  58. }
  59. }
  60. }
  61. // As finds the first error in err's chain that matches the type to which target
  62. // points, and if so, sets the target to its value and returns true. An error
  63. // matches a type if it is assignable to the target type, or if it has a method
  64. // As(interface{}) bool such that As(target) returns true. As will panic if target
  65. // is not a non-nil pointer to a type which implements error or is of interface type.
  66. //
  67. // The As method should set the target to its value and return true if err
  68. // matches the type to which target points.
  69. func As(err error, target interface{}) bool {
  70. if target == nil {
  71. panic("errors: target cannot be nil")
  72. }
  73. val := reflect.ValueOf(target)
  74. typ := val.Type()
  75. if typ.Kind() != reflect.Ptr || val.IsNil() {
  76. panic("errors: target must be a non-nil pointer")
  77. }
  78. if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) {
  79. panic("errors: *target must be interface or implement error")
  80. }
  81. targetType := typ.Elem()
  82. for err != nil {
  83. if reflect.TypeOf(err).AssignableTo(targetType) {
  84. val.Elem().Set(reflect.ValueOf(err))
  85. return true
  86. }
  87. if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
  88. return true
  89. }
  90. err = Unwrap(err)
  91. }
  92. return false
  93. }
  94. var errorType = reflect.TypeOf((*error)(nil)).Elem()