| 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 interface { | |
| error | |
| Errors() []error | |
| } | |
| // newMultiError creates a new MultiError. Pass in one or more errors. | |
| func newMultiError(err ...error) MultiError { | |
| ret := multiError(err) | |
| return &ret | |
| } | |
| type multiError []error | |
| // Error implements the error interface. | |
| func (m *multiError) Error() string { | |
| errs := make([]string, len(*m)) | |
| for i, err := range *m { | |
| errs[i] = err.Error() | |
| } | |
| return strings.Join(errs, "\n") | |
| } | |
| // Errors returns a copy of the errors slice | |
| func (m *multiError) Errors() []error { | |
| errs := make([]error, len(*m)) | |
| for _, err := range *m { | |
| errs = append(errs, err) | |
| } | |
| return errs | |
| } | |
| type requiredFlagsErr interface { | |
| error | |
| getMissingFlags() []string | |
| } | |
| type errRequiredFlags struct { | |
| missingFlags []string | |
| } | |
| func (e *errRequiredFlags) Error() string { | |
| numberOfMissingFlags := len(e.missingFlags) | |
| if numberOfMissingFlags == 1 { | |
| return fmt.Sprintf("Required flag %q not set", e.missingFlags[0]) | |
| } | |
| joinedMissingFlags := strings.Join(e.missingFlags, ", ") | |
| return fmt.Sprintf("Required flags %q not set", joinedMissingFlags) | |
| } | |
| func (e *errRequiredFlags) getMissingFlags() []string { | |
| return e.missingFlags | |
| } | |
| // ErrorFormatter is the interface that will suitably format the error output | |
| 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 | |
| } | |
| type exitError struct { | |
| exitCode int | |
| err error | |
| } | |
| // NewExitError calls Exit to create a new ExitCoder. | |
| // | |
| // Deprecated: This function is a duplicate of Exit and will eventually be removed. | |
| func NewExitError(message interface{}, exitCode int) ExitCoder { | |
| return Exit(message, exitCode) | |
| } | |
| // Exit wraps a message and exit code into an error, which by default is | |
| // handled with a call to os.Exit during default error handling. | |
| // | |
| // This is the simplest way to trigger a non-zero exit code for an App without | |
| // having to call os.Exit manually. During testing, this behavior can be avoided | |
| // by overriding the ExitErrHandler function on an App or the package-global | |
| // OsExiter function. | |
| func Exit(message interface{}, exitCode int) ExitCoder { | |
| var err error | |
| switch e := message.(type) { | |
| case ErrorFormatter: | |
| err = fmt.Errorf("%+v", message) | |
| case error: | |
| err = e | |
| default: | |
| err = fmt.Errorf("%+v", message) | |
| } | |
| return &exitError{ | |
| err: err, | |
| exitCode: exitCode, | |
| } | |
| } | |
| func (ee *exitError) Error() string { | |
| return ee.err.Error() | |
| } | |
| func (ee *exitError) ExitCode() int { | |
| return ee.exitCode | |
| } | |
| func (ee *exitError) Unwrap() error { | |
| return ee.err | |
| } | |
| // HandleExitCoder handles errors implementing ExitCoder by printing their | |
| // message and calling OsExiter with the given exit code. | |
| // | |
| // If the given error instead implements MultiError, each error will be checked | |
| // for the ExitCoder interface, and OsExiter will be called with the last exit | |
| // code found, or exit code 1 if no ExitCoder is found. | |
| // | |
| // This function is the default error-handling behavior for an App. | |
| 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 { | |
| code := handleMultiError(multiErr) | |
| OsExiter(code) | |
| return | |
| } | |
| } | |
| func handleMultiError(multiErr MultiError) int { | |
| code := 1 | |
| for _, merr := range multiErr.Errors() { | |
| if multiErr2, ok := merr.(MultiError); ok { | |
| code = handleMultiError(multiErr2) | |
| } else if merr != nil { | |
| fmt.Fprintln(ErrWriter, merr) | |
| if exitErr, ok := merr.(ExitCoder); ok { | |
| code = exitErr.ExitCode() | |
| } | |
| } | |
| } | |
| return code | |
| } |