You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.1 KiB

  1. // Copyright 2014 Canonical Ltd.
  2. // Licensed under the LGPLv3, see LICENCE file for details.
  3. package errors
  4. import (
  5. "fmt"
  6. "reflect"
  7. "runtime"
  8. )
  9. // Err holds a description of an error along with information about
  10. // where the error was created.
  11. //
  12. // It may be embedded in custom error types to add extra information that
  13. // this errors package can understand.
  14. type Err struct {
  15. // message holds an annotation of the error.
  16. message string
  17. // cause holds the cause of the error as returned
  18. // by the Cause method.
  19. cause error
  20. // previous holds the previous error in the error stack, if any.
  21. previous error
  22. // file and line hold the source code location where the error was
  23. // created.
  24. file string
  25. line int
  26. }
  27. // NewErr is used to return an Err for the purpose of embedding in other
  28. // structures. The location is not specified, and needs to be set with a call
  29. // to SetLocation.
  30. //
  31. // For example:
  32. // type FooError struct {
  33. // errors.Err
  34. // code int
  35. // }
  36. //
  37. // func NewFooError(code int) error {
  38. // err := &FooError{errors.NewErr("foo"), code}
  39. // err.SetLocation(1)
  40. // return err
  41. // }
  42. func NewErr(format string, args ...interface{}) Err {
  43. return Err{
  44. message: fmt.Sprintf(format, args...),
  45. }
  46. }
  47. // NewErrWithCause is used to return an Err with case by other error for the purpose of embedding in other
  48. // structures. The location is not specified, and needs to be set with a call
  49. // to SetLocation.
  50. //
  51. // For example:
  52. // type FooError struct {
  53. // errors.Err
  54. // code int
  55. // }
  56. //
  57. // func (e *FooError) Annotate(format string, args ...interface{}) error {
  58. // err := &FooError{errors.NewErrWithCause(e.Err, format, args...), e.code}
  59. // err.SetLocation(1)
  60. // return err
  61. // })
  62. func NewErrWithCause(other error, format string, args ...interface{}) Err {
  63. return Err{
  64. message: fmt.Sprintf(format, args...),
  65. cause: Cause(other),
  66. previous: other,
  67. }
  68. }
  69. // Location is the file and line of where the error was most recently
  70. // created or annotated.
  71. func (e *Err) Location() (filename string, line int) {
  72. return e.file, e.line
  73. }
  74. // Underlying returns the previous error in the error stack, if any. A client
  75. // should not ever really call this method. It is used to build the error
  76. // stack and should not be introspected by client calls. Or more
  77. // specifically, clients should not depend on anything but the `Cause` of an
  78. // error.
  79. func (e *Err) Underlying() error {
  80. return e.previous
  81. }
  82. // The Cause of an error is the most recent error in the error stack that
  83. // meets one of these criteria: the original error that was raised; the new
  84. // error that was passed into the Wrap function; the most recently masked
  85. // error; or nil if the error itself is considered the Cause. Normally this
  86. // method is not invoked directly, but instead through the Cause stand alone
  87. // function.
  88. func (e *Err) Cause() error {
  89. return e.cause
  90. }
  91. // Message returns the message stored with the most recent location. This is
  92. // the empty string if the most recent call was Trace, or the message stored
  93. // with Annotate or Mask.
  94. func (e *Err) Message() string {
  95. return e.message
  96. }
  97. // Error implements error.Error.
  98. func (e *Err) Error() string {
  99. // We want to walk up the stack of errors showing the annotations
  100. // as long as the cause is the same.
  101. err := e.previous
  102. if !sameError(Cause(err), e.cause) && e.cause != nil {
  103. err = e.cause
  104. }
  105. switch {
  106. case err == nil:
  107. return e.message
  108. case e.message == "":
  109. return err.Error()
  110. }
  111. return fmt.Sprintf("%s: %v", e.message, err)
  112. }
  113. // SetLocation records the source location of the error at callDepth stack
  114. // frames above the call.
  115. func (e *Err) SetLocation(callDepth int) {
  116. _, file, line, _ := runtime.Caller(callDepth + 1)
  117. e.file = trimGoPath(file)
  118. e.line = line
  119. }
  120. // StackTrace returns one string for each location recorded in the stack of
  121. // errors. The first value is the originating error, with a line for each
  122. // other annotation or tracing of the error.
  123. func (e *Err) StackTrace() []string {
  124. return errorStack(e)
  125. }
  126. // Ideally we'd have a way to check identity, but deep equals will do.
  127. func sameError(e1, e2 error) bool {
  128. return reflect.DeepEqual(e1, e2)
  129. }