errors.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package errors
  2. import (
  3. "bytes"
  4. "fmt"
  5. "runtime"
  6. "strings"
  7. )
  8. // location represents a Location information of source code.
  9. type location struct {
  10. File string
  11. Line int
  12. }
  13. // String returns the string of Location with filename:line format.
  14. func (l *location) String() string {
  15. if len(l.File) == 0 {
  16. return ""
  17. }
  18. return fmt.Sprintf("%s:%d", l.File, l.Line)
  19. }
  20. // error2 represents an error.
  21. type error2 struct {
  22. Inner error
  23. Location *location
  24. Message string
  25. }
  26. // Error implements builtin.error interface.
  27. // It returns error message in a tree like format.
  28. func (e *error2) Error() string {
  29. return TreeMessage(e)
  30. }
  31. func locate(depth int) *location {
  32. _, file, line, _ := runtime.Caller(depth + 1)
  33. return &location{
  34. File: file,
  35. Line: line,
  36. }
  37. }
  38. // Equal detects whether the error is equal to a given error.
  39. // Errors are considered equal by this function if they are the same object, or if they both contain the same error inside.
  40. func Equal(err1 error, err2 error) bool {
  41. if err1 == err2 {
  42. return true
  43. }
  44. if v, ok := err1.(*error2); ok {
  45. return Equal(v.Inner, err2)
  46. }
  47. if v, ok := err2.(*error2); ok {
  48. return Equal(v.Inner, err1)
  49. }
  50. return false
  51. }
  52. // TreeMessage returns the errors message in a tree like format:
  53. // ├─ error message -> filename:line
  54. // | ├─ error message -> filename:line
  55. // | └─ error 3
  56. func TreeMessage(err error) string {
  57. if err == nil {
  58. return ""
  59. }
  60. var buf bytes.Buffer
  61. depth := 1
  62. for {
  63. if depth > 1 {
  64. buf.WriteString("\n|")
  65. buf.WriteString(strings.Repeat(" ", (depth-1)*3-1))
  66. }
  67. if e, ok := err.(*error2); ok {
  68. buf.WriteString("├─ " + e.Message + " -> " + e.Location.String())
  69. err = e.Inner
  70. } else {
  71. buf.WriteString("└─ " + err.Error())
  72. err = nil
  73. }
  74. if err == nil {
  75. break
  76. }
  77. depth++
  78. }
  79. return buf.String()
  80. }
  81. // New returns a new Error.
  82. // It's used to instead of built-in errors.New function.
  83. func New(message string) error {
  84. return &error2{
  85. Location: locate(1),
  86. Message: message,
  87. }
  88. }
  89. // Newf same as New, but with fmt.Printf-style parameters.
  90. func Newf(format string, args ...interface{}) error {
  91. return &error2{
  92. Location: locate(1),
  93. Message: fmt.Sprintf(format, args...),
  94. }
  95. }
  96. // Wrap returns a new wrapped Error.
  97. func Wrap(err error, message string) error {
  98. return &error2{
  99. Inner: err,
  100. Location: locate(2),
  101. Message: message,
  102. }
  103. }
  104. // Wrapf same as Wrap, but with fmt.Printf-style parameters.
  105. func Wrapf(err error, format string, args ...interface{}) error {
  106. return &error2{
  107. Inner: err,
  108. Location: locate(2),
  109. Message: fmt.Sprintf(format, args...),
  110. }
  111. }