errors.go

  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}