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