Merge pull request #628 from phinnaeus/master

Allow custom ExitError handler function
This commit is contained in:
Jesse Szwedko 2017-10-14 13:27:26 -07:00 committed by GitHub
commit 7bc6a0acff
4 changed files with 64 additions and 11 deletions

26
app.go
View File

@ -83,6 +83,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
// function as a default, so this is optional.
ExitErrHandler ExitErrHandlerFunc
// Other custom info // Other custom info
Metadata map[string]interface{} Metadata map[string]interface{}
// Carries a function which returns app specific info. // Carries a function which returns app specific info.
@ -207,7 +210,7 @@ func (a *App) Run(arguments []string) (err error) {
if err != nil { if err != nil {
if a.OnUsageError != nil { if a.OnUsageError != nil {
err := a.OnUsageError(context, err, false) err := a.OnUsageError(context, err, false)
HandleExitCoder(err) a.handleExitCoder(context, err)
return err return err
} }
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
@ -240,8 +243,9 @@ func (a *App) Run(arguments []string) (err error) {
if a.Before != nil { if a.Before != nil {
beforeErr := a.Before(context) beforeErr := a.Before(context)
if beforeErr != nil { if beforeErr != nil {
fmt.Fprintf(a.Writer, "%v\n\n", beforeErr)
ShowAppHelp(context) ShowAppHelp(context)
HandleExitCoder(beforeErr) a.handleExitCoder(context, beforeErr)
err = beforeErr err = beforeErr
return err return err
} }
@ -263,7 +267,7 @@ func (a *App) Run(arguments []string) (err error) {
// Run default Action // Run default Action
err = HandleAction(a.Action, context) err = HandleAction(a.Action, context)
HandleExitCoder(err) a.handleExitCoder(context, err)
return err return err
} }
@ -330,7 +334,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
if err != nil { if err != nil {
if a.OnUsageError != nil { if a.OnUsageError != nil {
err = a.OnUsageError(context, err, true) err = a.OnUsageError(context, err, true)
HandleExitCoder(err) a.handleExitCoder(context, err)
return err return err
} }
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
@ -352,7 +356,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
defer func() { defer func() {
afterErr := a.After(context) afterErr := a.After(context)
if afterErr != nil { if afterErr != nil {
HandleExitCoder(err) a.handleExitCoder(context, err)
if err != nil { if err != nil {
err = NewMultiError(err, afterErr) err = NewMultiError(err, afterErr)
} else { } else {
@ -365,7 +369,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
if a.Before != nil { if a.Before != nil {
beforeErr := a.Before(context) beforeErr := a.Before(context)
if beforeErr != nil { if beforeErr != nil {
HandleExitCoder(beforeErr) a.handleExitCoder(context, beforeErr)
err = beforeErr err = beforeErr
return err return err
} }
@ -383,7 +387,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
// Run default Action // Run default Action
err = HandleAction(a.Action, context) err = HandleAction(a.Action, context)
HandleExitCoder(err) a.handleExitCoder(context, err)
return err return err
} }
@ -464,6 +468,14 @@ func (a *App) appendFlag(flag Flag) {
} }
} }
func (a *App) handleExitCoder(context *Context, err error) {
if a.ExitErrHandler != nil {
a.ExitErrHandler(context, err)
} else {
HandleExitCoder(err)
}
}
// Author represents someone who has contributed to a cli project. // Author represents someone who has contributed to a cli project.
type Author struct { type Author struct {
Name string // The Authors name Name string // The Authors name

View File

@ -1658,6 +1658,42 @@ func TestHandleAction_WithInvalidFuncReturnSignature(t *testing.T) {
} }
} }
func TestHandleExitCoder_Default(t *testing.T) {
app := NewApp()
fs, err := flagSet(app.Name, app.Flags)
if err != nil {
t.Errorf("error creating FlagSet: %s", err)
}
ctx := NewContext(app, fs, nil)
app.handleExitCoder(ctx, NewExitError("Default Behavior Error", 42))
output := fakeErrWriter.String()
if !strings.Contains(output, "Default") {
t.Fatalf("Expected Default Behavior from Error Handler but got: %s", output)
}
}
func TestHandleExitCoder_Custom(t *testing.T) {
app := NewApp()
fs, err := flagSet(app.Name, app.Flags)
if err != nil {
t.Errorf("error creating FlagSet: %s", err)
}
app.ExitErrHandler = func(_ *Context, _ error) {
fmt.Fprintln(ErrWriter, "I'm a Custom error handler, I print what I want!")
}
ctx := NewContext(app, fs, nil)
app.handleExitCoder(ctx, NewExitError("Default Behavior Error", 42))
output := fakeErrWriter.String()
if !strings.Contains(output, "Custom") {
t.Fatalf("Expected Custom Behavior from Error Handler but got: %s", output)
}
}
func TestHandleAction_WithUnknownPanic(t *testing.T) { func TestHandleAction_WithUnknownPanic(t *testing.T) {
defer func() { refute(t, recover(), nil) }() defer func() { refute(t, recover(), nil) }()

View File

@ -167,7 +167,7 @@ func (c Command) Run(ctx *Context) (err error) {
if err != nil { if err != nil {
if c.OnUsageError != nil { if c.OnUsageError != nil {
err := c.OnUsageError(context, err, false) err := c.OnUsageError(context, err, false)
HandleExitCoder(err) context.App.handleExitCoder(context, err)
return err return err
} }
fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
@ -184,7 +184,7 @@ func (c Command) Run(ctx *Context) (err error) {
defer func() { defer func() {
afterErr := c.After(context) afterErr := c.After(context)
if afterErr != nil { if afterErr != nil {
HandleExitCoder(err) context.App.handleExitCoder(context, err)
if err != nil { if err != nil {
err = NewMultiError(err, afterErr) err = NewMultiError(err, afterErr)
} else { } else {
@ -198,7 +198,7 @@ func (c Command) Run(ctx *Context) (err error) {
err = c.Before(context) err = c.Before(context)
if err != nil { if err != nil {
ShowCommandHelp(context, c.Name) ShowCommandHelp(context, c.Name)
HandleExitCoder(err) context.App.handleExitCoder(context, err)
return err return err
} }
} }
@ -210,7 +210,7 @@ func (c Command) Run(ctx *Context) (err error) {
err = HandleAction(c.Action, context) err = HandleAction(c.Action, context)
if err != nil { if err != nil {
HandleExitCoder(err) context.App.handleExitCoder(context, err)
} }
return err return err
} }

View File

@ -23,6 +23,10 @@ type CommandNotFoundFunc func(*Context, string)
// is displayed and the execution is interrupted. // is displayed and the execution is interrupted.
type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error
// ExitErrHandlerFunc is executed if provided in order to handle ExitError values
// returned by Actions and Before/After functions.
type ExitErrHandlerFunc func(context *Context, err error)
// FlagStringFunc is used by the help generation to display a flag, which is // FlagStringFunc is used by the help generation to display a flag, which is
// expected to be a single line. // expected to be a single line.
type FlagStringFunc func(Flag) string type FlagStringFunc func(Flag) string
@ -34,3 +38,4 @@ type FlagNamePrefixFunc func(fullName, placeholder string) string
// FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help // FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help
// with the environment variable details. // with the environment variable details.
type FlagEnvHintFunc func(envVar, str string) string type FlagEnvHintFunc func(envVar, str string) string