errors.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. package errors
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "path/filepath"
  7. "reflect"
  8. "runtime"
  9. )
  10. var (
  11. // The function used to encode errors and their tags.
  12. Encoder func(any) ([]byte, error)
  13. )
  14. func init() {
  15. SetInlineEncoder()
  16. }
  17. // SetInlineEncoder is a helper function that set the error encoder to
  18. // json.Marshal.
  19. func SetInlineEncoder() {
  20. Encoder = json.Marshal
  21. }
  22. // SetIndentEncoder is a helper function that set the error encoder to a
  23. // function that uses json.MarshalIndent.
  24. func SetIndentEncoder() {
  25. Encoder = func(v any) ([]byte, error) {
  26. return json.MarshalIndent(v, "", " ")
  27. }
  28. }
  29. // Unwrap returns the result of calling the Unwrap method on err, if err's type
  30. // contains an Unwrap method returning error.
  31. // Otherwise, Unwrap returns nil.
  32. func Unwrap(err error) error {
  33. return errors.Unwrap(err)
  34. }
  35. // Is reports whether any error in err's chain matches target.
  36. //
  37. // The chain consists of err itself followed by the sequence of errors obtained
  38. // by repeatedly calling Unwrap.
  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. //
  43. // An error type might provide an Is method so it can be treated as equivalent
  44. // to an existing error. For example, if MyError defines
  45. //
  46. // func (m MyError) Is(target error) bool { return target == fs.ErrExist }
  47. //
  48. // then Is(MyError{}, fs.ErrExist) returns true. See syscall.Errno.Is for an
  49. // example in the standard library. An Is method should only shallowly compare
  50. // err and the target and not call Unwrap on either.
  51. func Is(err, target error) bool {
  52. return errors.Is(err, target)
  53. }
  54. // As finds the first error in err's chain that matches target, and if one is
  55. // found, sets target to that error value and returns true. Otherwise, it
  56. // returns false.
  57. //
  58. // The chain consists of err itself followed by the sequence of errors obtained
  59. // by repeatedly calling Unwrap.
  60. //
  61. // An error matches target if the error's concrete value is assignable to the
  62. // value pointed to by target, or if the error has a method As(any) bool
  63. // such that As(target) returns true. In the latter case, the As method is
  64. // responsible for setting target.
  65. //
  66. // An error type might provide an As method so it can be treated as if it were a
  67. // different error type.
  68. //
  69. // As panics if target is not a non-nil pointer to either a type that implements
  70. // error, or to any interface type.
  71. func As(err error, target any) bool {
  72. return errors.As(err, target)
  73. }
  74. // Type returns the type of the error.
  75. //
  76. // Uses reflect.TypeOf() when the given error does not implements the Error
  77. // interface.
  78. func Type(err error) string {
  79. if err == nil {
  80. return ""
  81. }
  82. if err, ok := err.(interface{ Type() string }); ok {
  83. return err.Type()
  84. }
  85. return reflect.TypeOf(err).String()
  86. }
  87. // HasType reports whether any error in err's chain matches the given type.
  88. //
  89. // The chain consists of err itself followed by the sequence of errors obtained
  90. // by repeatedly calling Unwrap.
  91. //
  92. // An error matches the given type if the error has a method Type() string such
  93. // that Type() returns a string equal to the given type.
  94. func HasType(err error, v string) bool {
  95. for {
  96. if v == Type(err) {
  97. return true
  98. }
  99. if err = Unwrap(err); err == nil {
  100. return false
  101. }
  102. }
  103. }
  104. // Tag returns the first tag value in err's chain that matches the given key.
  105. //
  106. // The chain consists of err itself followed by the sequence of errors obtained
  107. // by repeatedly calling Unwrap.
  108. //
  109. // An error has a tag when it has a method Tag(string) string such that Tag(k)
  110. // returns a non-empty string value.
  111. func Tag(err error, k string) any {
  112. for {
  113. if err, ok := err.(Error); ok {
  114. if v := err.Tag(k); v != nil {
  115. return v
  116. }
  117. }
  118. if err = Unwrap(err); err == nil {
  119. return nil
  120. }
  121. }
  122. }
  123. // An enriched error.
  124. type Error struct {
  125. Line string
  126. Message string
  127. DefinedType string
  128. Tags map[string]any
  129. WrappedErr error
  130. }
  131. // New returns an error with the given message that can be enriched with a type
  132. // and tags.
  133. func New(msg string) Error {
  134. return makeError(msg)
  135. }
  136. // Newf returns an error with the given formatted message that can be enriched
  137. // with a type and tags.
  138. func Newf(msgFormat string, v ...any) Error {
  139. return makeError(msgFormat, v...)
  140. }
  141. func makeError(msgFormat string, v ...any) Error {
  142. _, filename, line, _ := runtime.Caller(2)
  143. err := Error{
  144. Line: fmt.Sprintf("%s:%v", filepath.Base(filename), line),
  145. Message: fmt.Sprintf(msgFormat, v...),
  146. }
  147. return err
  148. }
  149. func (e Error) WithType(v string) Error {
  150. e.DefinedType = v
  151. return e
  152. }
  153. func (e Error) Type() string {
  154. if e.DefinedType != "" {
  155. return e.DefinedType
  156. }
  157. if e.WrappedErr != nil {
  158. return Type(e.WrappedErr)
  159. }
  160. return reflect.TypeOf(e).String()
  161. }
  162. func (e Error) WithTag(k string, v any) Error {
  163. if e.Tags == nil {
  164. e.Tags = make(map[string]any)
  165. }
  166. e.Tags[k] = v
  167. return e
  168. }
  169. func (e Error) Tag(k string) any {
  170. return e.Tags[k]
  171. }
  172. func (e Error) Wrap(err error) Error {
  173. e.WrappedErr = err
  174. return e
  175. }
  176. func (e Error) Unwrap() error {
  177. return e.WrappedErr
  178. }
  179. func (e Error) Error() string {
  180. s, err := Encoder(e)
  181. if err != nil {
  182. return fmt.Sprintf(`{"message": "encoding error failed: %s"}`, err)
  183. }
  184. return string(s)
  185. }
  186. func (e Error) MarshalJSON() ([]byte, error) {
  187. var wrappedErr any = e.WrappedErr
  188. if _, ok := e.WrappedErr.(Error); !ok && e.WrappedErr != nil {
  189. wrappedErr = e.WrappedErr.Error()
  190. }
  191. return Encoder(struct {
  192. Line string `json:"line,omitempty"`
  193. Message string `json:"message"`
  194. DefinedType string `json:"type,omitempty"`
  195. Tags map[string]any `json:"tags,omitempty"`
  196. WrappedErr any `json:"wrap,omitempty"`
  197. }{
  198. Line: e.Line,
  199. Message: e.Message,
  200. DefinedType: e.DefinedType,
  201. Tags: e.Tags,
  202. WrappedErr: wrappedErr,
  203. })
  204. }
  205. func (e Error) Is(err error) bool {
  206. rerr, ok := err.(Error)
  207. if !ok {
  208. return false
  209. }
  210. return rerr.Line == e.Line &&
  211. rerr.Message == e.Message &&
  212. rerr.DefinedType == e.DefinedType &&
  213. reflect.DeepEqual(rerr.Tags, e.Tags) &&
  214. rerr.WrappedErr == e.WrappedErr
  215. }