Allow for legacy and newer Action func signatures

This commit is contained in:
Dan Buch 2016-04-27 11:34:01 -04:00
parent d48e22a9dd
commit d45f7c1fe2
No known key found for this signature in database
GPG Key ID: FAEF12936DD3E3EC
2 changed files with 51 additions and 8 deletions

51
app.go
View File

@ -6,6 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"sort" "sort"
"time" "time"
) )
@ -55,7 +56,10 @@ type App struct {
// It is run even if Action() panics // It is run even if Action() panics
After AfterFunc After AfterFunc
// The action to execute when no subcommands are specified // The action to execute when no subcommands are specified
Action ActionFunc Action interface{}
// TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
// of deprecation period has passed, maybe?
// Execute this function if the proper command cannot be found // Execute this function if the proper command cannot be found
CommandNotFound CommandNotFoundFunc CommandNotFound CommandNotFoundFunc
// Execute this function if an usage error occurs // Execute this function if an usage error occurs
@ -179,8 +183,7 @@ func (a *App) Run(arguments []string) (err error) {
if a.After != nil { if a.After != nil {
defer func() { defer func() {
afterErr := a.After(context) if afterErr := a.After(context); afterErr != nil {
if afterErr != nil {
if err != nil { if err != nil {
err = NewMultiError(err, afterErr) err = NewMultiError(err, afterErr)
} else { } else {
@ -210,9 +213,11 @@ func (a *App) Run(arguments []string) (err error) {
} }
// Run default Action // Run default Action
err = a.Action(context) err = HandleAction(a.Action, context)
HandleExitCoder(err)
if err != nil {
HandleExitCoder(err)
}
return err return err
} }
@ -318,7 +323,12 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
} }
// Run default Action // Run default Action
return a.Action(context) err = HandleAction(a.Action, context)
if err != nil {
HandleExitCoder(err)
}
return err
} }
// Returns the named command on App. Returns nil if the command does not exist // Returns the named command on App. Returns nil if the command does not exist
@ -368,3 +378,32 @@ func (a Author) String() string {
return fmt.Sprintf("%v %v", a.Name, e) return fmt.Sprintf("%v %v", a.Name, e)
} }
// HandleAction uses ✧✧✧reflection✧✧✧ to figure out if the given Action is an
// ActionFunc, LegacyActionFunc, or some other invalid thing. If it's an
// ActionFunc or LegacyActionFunc, the func is run!
func HandleAction(action interface{}, context *Context) error {
if reflect.TypeOf(action).Kind() != reflect.Func {
panic("given Action must be a func")
}
vals := reflect.ValueOf(action).Call([]reflect.Value{reflect.ValueOf(context)})
if len(vals) == 0 {
fmt.Fprintln(os.Stderr,
"DEPRECATED Action signature. Must be `cli.ActionFunc`")
return nil
}
if len(vals) > 1 {
fmt.Fprintln(os.Stderr,
"ERROR invalid Action signature. Must be `cli.ActionFunc`")
panic("given Action has invalid return signature")
}
if err, ok := reflect.ValueOf(vals[0]).Interface().(error); ok {
return err
}
return nil
}

View File

@ -34,7 +34,10 @@ type Command struct {
// It is run even if Action() panics // It is run even if Action() panics
After AfterFunc After AfterFunc
// The function to call when this command is invoked // The function to call when this command is invoked
Action ActionFunc Action interface{}
// TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
// of deprecation period has passed, maybe?
// Execute this function if a usage error occurs. // Execute this function if a usage error occurs.
OnUsageError OnUsageErrorFunc OnUsageError OnUsageErrorFunc
// List of child commands // List of child commands
@ -178,7 +181,8 @@ func (c Command) Run(ctx *Context) (err error) {
} }
context.Command = c context.Command = c
err = c.Action(context) err = HandleAction(c.Action, context)
if err != nil { if err != nil {
HandleExitCoder(err) HandleExitCoder(err)
} }