Merge pull request #1091 from rliebz/document-exit-errors

Update docs and tests around cli.Exit
This commit is contained in:
Robert Liebowitz 2020-03-25 17:52:14 -04:00 committed by GitHub
commit dd51d24ce6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 11 deletions

5
app.go
View File

@ -76,8 +76,9 @@ type App struct {
Writer io.Writer Writer io.Writer
// ErrWriter writes error output // ErrWriter writes error output
ErrWriter io.Writer ErrWriter io.Writer
// Execute this function to handle ExitErrors. If not provided, HandleExitCoder is provided to // ExitErrHandler processes any error encountered while running an App before
// function as a default, so this is optional. // it is returned to the caller. If no function is provided, HandleExitCoder
// is used as the default behavior.
ExitErrHandler ExitErrHandlerFunc ExitErrHandler ExitErrHandlerFunc
// Other custom info // Other custom info
Metadata map[string]interface{} Metadata map[string]interface{}

View File

@ -17,11 +17,10 @@ var ErrWriter io.Writer = os.Stderr
// MultiError is an error that wraps multiple errors. // MultiError is an error that wraps multiple errors.
type MultiError interface { type MultiError interface {
error error
// Errors returns a copy of the errors slice
Errors() []error Errors() []error
} }
// NewMultiError creates a new MultiError. Pass in one or more errors. // newMultiError creates a new MultiError. Pass in one or more errors.
func newMultiError(err ...error) MultiError { func newMultiError(err ...error) MultiError {
ret := multiError(err) ret := multiError(err)
return &ret return &ret
@ -65,13 +64,20 @@ type exitError struct {
message interface{} message interface{}
} }
// NewExitError makes a new *exitError // 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 { func NewExitError(message interface{}, exitCode int) ExitCoder {
return Exit(message, exitCode) return Exit(message, exitCode)
} }
// Exit wraps a message and exit code into an ExitCoder suitable for handling by // Exit wraps a message and exit code into an error, which by default is
// HandleExitCoder // 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 overiding the ExitErrHandler function on an App or the package-global
// OsExiter function.
func Exit(message interface{}, exitCode int) ExitCoder { func Exit(message interface{}, exitCode int) ExitCoder {
return &exitError{ return &exitError{
message: message, message: message,
@ -87,10 +93,14 @@ func (ee *exitError) ExitCode() int {
return ee.exitCode return ee.exitCode
} }
// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if // HandleExitCoder handles errors implementing ExitCoder by printing their
// so prints the error to stderr (if it is non-empty) and calls OsExiter with the // message and calling OsExiter with the given exit code.
// given exit code. If the given error is a MultiError, then this func is //
// called on all members of the Errors slice and calls OsExiter with the last 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) { func HandleExitCoder(err error) {
if err == nil { if err == nil {
return return

View File

@ -67,6 +67,26 @@ func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) {
expect(t, called, true) expect(t, called, true)
} }
func TestHandleExitCoder_MultiErrorWithoutExitCoder(t *testing.T) {
exitCode := 0
called := false
OsExiter = func(rc int) {
if !called {
exitCode = rc
called = true
}
}
defer func() { OsExiter = fakeOsExiter }()
err := newMultiError(errors.New("wowsa"), errors.New("egad"))
HandleExitCoder(err)
expect(t, exitCode, 1)
expect(t, called, true)
}
// make a stub to not import pkg/errors // make a stub to not import pkg/errors
type ErrorWithFormat struct { type ErrorWithFormat struct {
error error