PageRenderTime 59ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/error.go

https://bitbucket.org/lavalamp/easy
Go | 105 lines | 70 code | 15 blank | 20 comment | 13 complexity | a39086902d15b7358d5a47864342863b MD5 | raw file
  1. package easy
  2. import (
  3. "runtime"
  4. "fmt"
  5. "errors"
  6. )
  7. //HelpfulError includes a base error and some context from the call stack.
  8. type HelpfulError struct {
  9. //base error
  10. Err error
  11. //ErrChainf will produce/add to the comentary here.
  12. Commentary string
  13. //A few lines of the call stack so you can figure out where
  14. //the hell your error came from
  15. Context string
  16. }
  17. func (he HelpfulError) Error() string {
  18. if len(he.Commentary) == 0 {
  19. return he.Err.Error() + "\n<callstack:\n" + he.Context + "\n>"
  20. }
  21. return fmt.Sprintf("%s\n<commentary:\n%s\ncallstack:\n%s\n>",
  22. he.Err.Error(), he.Commentary, he.Context)
  23. }
  24. //Error makes a HelpfulError out of err. If err is nil, it returns nil.
  25. //So you can use on actual errors without checking. Also, this won't like
  26. //stack up a zillion call stacks if you call it on the same root error
  27. //multiple times.
  28. func Error(err error) error {
  29. if err == nil {
  30. return nil
  31. }
  32. //don't let these stack, that would be annoying.
  33. if _, ok := err.(HelpfulError); ok {
  34. return err
  35. }
  36. return HelpfulError{
  37. Err: err,
  38. Context: CallStackToString(3),
  39. }
  40. }
  41. //Errorf returns a new error with the given formatted message, plus a call
  42. //stack for context.
  43. func Errorf(format string, vargs ...interface{}) error {
  44. return HelpfulError{
  45. Err: errors.New(fmt.Sprintf(format, vargs...)),
  46. Context: CallStackToString(2),
  47. }
  48. }
  49. //ErrChainf returns an error with your commentary and a callstack.
  50. //If err is already a helpful error, it adds your commentary but
  51. //doesn't replace the base error or the callstack. If err is nil,
  52. //it returns nil and ignores your commentary.
  53. func ErrChainf(err error, format string, vargs ...interface{}) error {
  54. if err == nil {
  55. return nil
  56. }
  57. comments := fmt.Sprintf(format, vargs...)
  58. if he, ok := err.(HelpfulError); ok {
  59. he.Commentary += comments + "\n"
  60. return he
  61. }
  62. return HelpfulError{
  63. Err: err,
  64. Commentary: comments + "\n",
  65. Context: CallStackToString(3),
  66. }
  67. }
  68. //CallStackToString returns the callstack as a string. For nice error
  69. //or panic messages. Probably you want to skip one or two frames, at least.
  70. func CallStackToString(skipFrames int) (str string) {
  71. i := skipFrames
  72. for {
  73. pc, file, line, ok := runtime.Caller(i)
  74. if !ok {
  75. break
  76. }
  77. str += fmt.Sprintf("%16.16x: %s(%d)\r\n", pc, file, line)
  78. i++
  79. }
  80. return
  81. }
  82. // Is returns true if the two errors match, even if one or both are wrapped in
  83. // a HelpfulError.
  84. func Is(err1, err2 error) bool {
  85. found, ok := err1.(HelpfulError)
  86. if ok { err1 = found }
  87. found, ok = err2.(HelpfulError)
  88. if ok { err2 = found }
  89. return err1 == err2
  90. }