Merge pull request #322 from blaubaer/custom-error-handling

Introduce custom usage error handling
This commit is contained in:
Jesse Szwedko 2016-02-06 12:58:47 -08:00
commit 3565e5dfe6
4 changed files with 121 additions and 10 deletions

14
app.go
View File

@ -44,6 +44,10 @@ type App struct {
Action func(context *Context) Action func(context *Context)
// Execute this function if the proper command cannot be found // Execute this function if the proper command cannot be found
CommandNotFound func(context *Context, command string) CommandNotFound func(context *Context, command string)
// Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
// This function is able to replace the original error messages.
// If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
OnUsageError func(context *Context, err error, isSubcommand bool) error
// Compilation date // Compilation date
Compiled time.Time Compiled time.Time
// List of all authors who contributed // List of all authors who contributed
@ -131,10 +135,15 @@ func (a *App) Run(arguments []string) (err error) {
} }
if err != nil { if err != nil {
if a.OnUsageError != nil {
err := a.OnUsageError(context, err, false)
return err
} else {
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
ShowAppHelp(context) ShowAppHelp(context)
return err return err
} }
}
if !a.HideHelp && checkHelp(context) { if !a.HideHelp && checkHelp(context) {
ShowAppHelp(context) ShowAppHelp(context)
@ -238,10 +247,15 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
} }
if err != nil { if err != nil {
if a.OnUsageError != nil {
err = a.OnUsageError(context, err, true)
return err
} else {
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
ShowSubcommandHelp(context) ShowSubcommandHelp(context)
return err return err
} }
}
if len(a.Commands) > 0 { if len(a.Commands) > 0 {
if checkSubcommandHelp(context) { if checkSubcommandHelp(context) {

View File

@ -9,6 +9,7 @@ import (
"os" "os"
"strings" "strings"
"testing" "testing"
"errors"
) )
func ExampleApp_Run() { func ExampleApp_Run() {
@ -983,3 +984,63 @@ func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) {
t.Errorf("expected text of error from After method, but got none in \"%v\"", err) t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
} }
} }
func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) {
app := NewApp()
app.Flags = []Flag{
IntFlag{Name: "flag"},
}
app.OnUsageError = func(c *Context, err error, isSubcommand bool) error {
if isSubcommand {
t.Errorf("Expect no subcommand")
}
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
}
return errors.New("intercepted: " + err.Error())
}
app.Commands = []Command{
Command{
Name: "bar",
},
}
err := app.Run([]string{"foo", "--flag=wrong"})
if err == nil {
t.Fatalf("expected to receive error from Run, got none")
}
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
}
}
func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) {
app := NewApp()
app.Flags = []Flag{
IntFlag{Name: "flag"},
}
app.OnUsageError = func(c *Context, err error, isSubcommand bool) error {
if isSubcommand {
t.Errorf("Expect subcommand")
}
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
}
return errors.New("intercepted: " + err.Error())
}
app.Commands = []Command{
Command{
Name: "bar",
},
}
err := app.Run([]string{"foo", "--flag=wrong", "bar"})
if err == nil {
t.Fatalf("expected to receive error from Run, got none")
}
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
}
}

View File

@ -30,6 +30,10 @@ type Command struct {
After func(context *Context) error After func(context *Context) error
// The function to call when this command is invoked // The function to call when this command is invoked
Action func(context *Context) Action func(context *Context)
// Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
// This function is able to replace the original error messages.
// If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
OnUsageError func(context *Context, err error) error
// List of child commands // List of child commands
Subcommands []Command Subcommands []Command
// List of flags to parse // List of flags to parse
@ -113,11 +117,16 @@ func (c Command) Run(ctx *Context) (err error) {
} }
if err != nil { if err != nil {
if c.OnUsageError != nil {
err := c.OnUsageError(ctx, err)
return err
} else {
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.") fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
fmt.Fprintln(ctx.App.Writer) fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name) ShowCommandHelp(ctx, c.Name)
return err return err
} }
}
nerr := normalizeFlags(c.Flags, set) nerr := normalizeFlags(c.Flags, set)
if nerr != nil { if nerr != nil {

View File

@ -68,3 +68,30 @@ func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
t.Errorf("expected text of error from After method, but got none in \"%v\"", err) t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
} }
} }
func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
app := NewApp()
app.Commands = []Command{
Command{
Name: "bar",
Flags: []Flag{
IntFlag{Name: "flag"},
},
OnUsageError: func(c *Context, err error) error {
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
}
return errors.New("intercepted: " + err.Error())
},
},
}
err := app.Run([]string{"foo", "bar", "--flag=wrong"})
if err == nil {
t.Fatalf("expected to receive error from Run, got none")
}
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
}
}