Update docs and tests around cli.Exit
Some of the docs were lacking or incomplete concerning how to properly use cli.Exit, cli.HandleExitCoder, and the ExitErrHandler field on cli.App. This change aims to clarify the usage of those pieces. I also noticed that we have two identical functions now: cli.Exit and cli.NewExitError. Since the latter seems to be more recent and less well documented, I went ahead and marked it as deprecated so that we can keep our public interface small. Also added a missing test case for behavior that's been around a while but was not documented or tested. Related: #1089
This commit is contained in:
parent
1c4bb12e86
commit
959cf9de8a
5
app.go
5
app.go
@ -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{}
|
||||||
|
28
errors.go
28
errors.go
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user