Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow custom ExitError handler function #628

Merged
merged 15 commits into from
Oct 14, 2017
26 changes: 19 additions & 7 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ type App struct {
Writer io.Writer
// ErrWriter writes error output
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
Metadata map[string]interface{}
// Carries a function which returns app specific info.
Expand Down Expand Up @@ -207,7 +210,7 @@ func (a *App) Run(arguments []string) (err error) {
if err != nil {
if a.OnUsageError != nil {
err := a.OnUsageError(context, err, false)
HandleExitCoder(err)
a.handleExitCoder(context, err)
return err
}
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
Expand Down Expand Up @@ -240,8 +243,9 @@ func (a *App) Run(arguments []string) (err error) {
if a.Before != nil {
beforeErr := a.Before(context)
if beforeErr != nil {
fmt.Fprintf(a.Writer, "%v\n\n", beforeErr)
ShowAppHelp(context)
HandleExitCoder(beforeErr)
a.handleExitCoder(context, beforeErr)
err = beforeErr
return err
}
Expand All @@ -263,7 +267,7 @@ func (a *App) Run(arguments []string) (err error) {
// Run default Action
err = HandleAction(a.Action, context)

HandleExitCoder(err)
a.handleExitCoder(context, err)
return err
}

Expand Down Expand Up @@ -330,7 +334,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
if err != nil {
if a.OnUsageError != nil {
err = a.OnUsageError(context, err, true)
HandleExitCoder(err)
a.handleExitCoder(context, err)
return err
}
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
Expand All @@ -352,7 +356,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
defer func() {
afterErr := a.After(context)
if afterErr != nil {
HandleExitCoder(err)
a.handleExitCoder(context, err)
if err != nil {
err = NewMultiError(err, afterErr)
} else {
Expand All @@ -365,7 +369,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
if a.Before != nil {
beforeErr := a.Before(context)
if beforeErr != nil {
HandleExitCoder(beforeErr)
a.handleExitCoder(context, beforeErr)
err = beforeErr
return err
}
Expand All @@ -383,7 +387,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
// Run default Action
err = HandleAction(a.Action, context)

HandleExitCoder(err)
a.handleExitCoder(context, err)
return err
}

Expand Down Expand Up @@ -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.
type Author struct {
Name string // The Authors name
Expand Down
36 changes: 36 additions & 0 deletions app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
defer func() { refute(t, recover(), nil) }()

Expand Down
8 changes: 4 additions & 4 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func (c Command) Run(ctx *Context) (err error) {
if err != nil {
if c.OnUsageError != nil {
err := c.OnUsageError(context, err, false)
HandleExitCoder(err)
context.App.handleExitCoder(context, err)
return err
}
fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
Expand All @@ -184,7 +184,7 @@ func (c Command) Run(ctx *Context) (err error) {
defer func() {
afterErr := c.After(context)
if afterErr != nil {
HandleExitCoder(err)
context.App.handleExitCoder(context, err)
if err != nil {
err = NewMultiError(err, afterErr)
} else {
Expand All @@ -198,7 +198,7 @@ func (c Command) Run(ctx *Context) (err error) {
err = c.Before(context)
if err != nil {
ShowCommandHelp(context, c.Name)
HandleExitCoder(err)
context.App.handleExitCoder(context, err)
return err
}
}
Expand All @@ -210,7 +210,7 @@ func (c Command) Run(ctx *Context) (err error) {
err = HandleAction(c.Action, context)

if err != nil {
HandleExitCoder(err)
context.App.handleExitCoder(context, err)
}
return err
}
Expand Down
5 changes: 5 additions & 0 deletions funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ type CommandNotFoundFunc func(*Context, string)
// is displayed and the execution is interrupted.
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
// expected to be a single line.
type FlagStringFunc func(Flag) string
Expand All @@ -34,3 +38,4 @@ type FlagNamePrefixFunc func(fullName, placeholder string) string
// FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help
// with the environment variable details.
type FlagEnvHintFunc func(envVar, str string) string