| package cli |
| |
| import ( |
| "fmt" |
| "io" |
| "os" |
| "strings" |
| ) |
| |
| // OsExiter is the function used when the app exits. If not set defaults to os.Exit. |
| var OsExiter = os.Exit |
| |
| // ErrWriter is used to write errors to the user. This can be anything |
| // implementing the io.Writer interface and defaults to os.Stderr. |
| var ErrWriter io.Writer = os.Stderr |
| |
| // MultiError is an error that wraps multiple errors. |
| type MultiError struct { |
| Errors []error |
| } |
| |
| // NewMultiError creates a new MultiError. Pass in one or more errors. |
| func NewMultiError(err ...error) MultiError { |
| return MultiError{Errors: err} |
| } |
| |
| // Error implements the error interface. |
| func (m MultiError) Error() string { |
| errs := make([]string, len(m.Errors)) |
| for i, err := range m.Errors { |
| errs[i] = err.Error() |
| } |
| |
| return strings.Join(errs, "\n") |
| } |
| |
| type ErrorFormatter interface { |
| Format(s fmt.State, verb rune) |
| } |
| |
| // ExitCoder is the interface checked by `App` and `Command` for a custom exit |
| // code |
| type ExitCoder interface { |
| error |
| ExitCode() int |
| } |
| |
| // ExitError fulfills both the builtin `error` interface and `ExitCoder` |
| type ExitError struct { |
| exitCode int |
| message interface{} |
| } |
| |
| // NewExitError makes a new *ExitError |
| func NewExitError(message interface{}, exitCode int) *ExitError { |
| return &ExitError{ |
| exitCode: exitCode, |
| message: message, |
| } |
| } |
| |
| // Error returns the string message, fulfilling the interface required by |
| // `error` |
| func (ee *ExitError) Error() string { |
| return fmt.Sprintf("%v", ee.message) |
| } |
| |
| // ExitCode returns the exit code, fulfilling the interface required by |
| // `ExitCoder` |
| func (ee *ExitError) ExitCode() int { |
| return ee.exitCode |
| } |
| |
| // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if |
| // so prints the error to stderr (if it is non-empty) and calls OsExiter with the |
| // given exit code. If the given error is a MultiError, then this func is |
| // called on all members of the Errors slice. |
| func HandleExitCoder(err error) { |
| if err == nil { |
| return |
| } |
| |
| if exitErr, ok := err.(ExitCoder); ok { |
| if err.Error() != "" { |
| if _, ok := exitErr.(ErrorFormatter); ok { |
| fmt.Fprintf(ErrWriter, "%+v\n", err) |
| } else { |
| fmt.Fprintln(ErrWriter, err) |
| } |
| } |
| OsExiter(exitErr.ExitCode()) |
| return |
| } |
| |
| if multiErr, ok := err.(MultiError); ok { |
| for _, merr := range multiErr.Errors { |
| HandleExitCoder(merr) |
| } |
| return |
| } |
| |
| if err.Error() != "" { |
| if _, ok := err.(ErrorFormatter); ok { |
| fmt.Fprintf(ErrWriter, "%+v\n", err) |
| } else { |
| fmt.Fprintln(ErrWriter, err) |
| } |
| } |
| OsExiter(1) |
| } |