errors.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. package cli
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "strings"
  7. )
  8. // OsExiter is the function used when the app exits. If not set defaults to os.Exit.
  9. var OsExiter = os.Exit
  10. // ErrWriter is used to write errors to the user. This can be anything
  11. // implementing the io.Writer interface and defaults to os.Stderr.
  12. var ErrWriter io.Writer = os.Stderr
  13. // MultiError is an error that wraps multiple errors.
  14. type MultiError struct {
  15. Errors []error
  16. }
  17. // NewMultiError creates a new MultiError. Pass in one or more errors.
  18. func NewMultiError(err ...error) MultiError {
  19. return MultiError{Errors: err}
  20. }
  21. // Error implements the error interface.
  22. func (m MultiError) Error() string {
  23. errs := make([]string, len(m.Errors))
  24. for i, err := range m.Errors {
  25. errs[i] = err.Error()
  26. }
  27. return strings.Join(errs, "\n")
  28. }
  29. type ErrorFormatter interface {
  30. Format(s fmt.State, verb rune)
  31. }
  32. // ExitCoder is the interface checked by `App` and `Command` for a custom exit
  33. // code
  34. type ExitCoder interface {
  35. error
  36. ExitCode() int
  37. }
  38. // ExitError fulfills both the builtin `error` interface and `ExitCoder`
  39. type ExitError struct {
  40. exitCode int
  41. message interface{}
  42. }
  43. // NewExitError makes a new *ExitError
  44. func NewExitError(message interface{}, exitCode int) *ExitError {
  45. return &ExitError{
  46. exitCode: exitCode,
  47. message: message,
  48. }
  49. }
  50. // Error returns the string message, fulfilling the interface required by
  51. // `error`
  52. func (ee *ExitError) Error() string {
  53. return fmt.Sprintf("%v", ee.message)
  54. }
  55. // ExitCode returns the exit code, fulfilling the interface required by
  56. // `ExitCoder`
  57. func (ee *ExitError) ExitCode() int {
  58. return ee.exitCode
  59. }
  60. // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
  61. // so prints the error to stderr (if it is non-empty) and calls OsExiter with the
  62. // given exit code. If the given error is a MultiError, then this func is
  63. // called on all members of the Errors slice.
  64. func HandleExitCoder(err error) {
  65. if err == nil {
  66. return
  67. }
  68. if exitErr, ok := err.(ExitCoder); ok {
  69. if err.Error() != "" {
  70. if _, ok := exitErr.(ErrorFormatter); ok {
  71. fmt.Fprintf(ErrWriter, "%+v\n", err)
  72. } else {
  73. fmt.Fprintln(ErrWriter, err)
  74. }
  75. }
  76. OsExiter(exitErr.ExitCode())
  77. return
  78. }
  79. if multiErr, ok := err.(MultiError); ok {
  80. for _, merr := range multiErr.Errors {
  81. HandleExitCoder(merr)
  82. }
  83. return
  84. }
  85. if err.Error() != "" {
  86. if _, ok := err.(ErrorFormatter); ok {
  87. fmt.Fprintf(ErrWriter, "%+v\n", err)
  88. } else {
  89. fmt.Fprintln(ErrWriter, err)
  90. }
  91. }
  92. OsExiter(1)
  93. }