Added exit code support

Now the exit code can be returned by BeforeFn, ActionFn and AfterFn.

The `os.Exit` function is not called by this packaged

This closes #66 and closes #164
This commit is contained in:
Tarcísio Gruppi 2015-07-28 20:05:14 +02:00
parent 9c0db3f4ac
commit 49c1229409
No known key found for this signature in database
GPG Key ID: 36F5005FCAF5C057
4 changed files with 43 additions and 38 deletions

54
app.go
View File

@ -8,6 +8,11 @@ import (
"time"
)
var (
// Set to 125 which is the highest number not used in most shells
DefaultExitCode int = 0
)
// App is the main structure of a cli application. It is recomended that
// an app be created with the cli.NewApp() function
type App struct {
@ -77,7 +82,7 @@ func NewApp() *App {
}
// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
func (a *App) Run(arguments []string) (ec int, err error) {
if a.Author != "" || a.Email != "" {
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
}
@ -108,7 +113,7 @@ func (a *App) Run(arguments []string) (err error) {
fmt.Fprintln(a.Writer, nerr)
context := NewContext(a, set, nil)
ShowAppHelp(context)
return nerr
return DefaultExitCode, nerr
}
context := NewContext(a, set, nil)
@ -116,24 +121,24 @@ func (a *App) Run(arguments []string) (err error) {
fmt.Fprintln(a.Writer, "Incorrect Usage.")
fmt.Fprintln(a.Writer)
ShowAppHelp(context)
return err
return DefaultExitCode, err
}
if checkCompletions(context) {
return nil
return 0, nil
}
if checkHelp(context) {
return nil
return 0, nil
}
if checkVersion(context) {
return nil
return 0, nil
}
if a.After != nil {
defer func() {
afterErr := a.After(context)
afterEc, afterErr := a.After(context)
if afterErr != nil {
if err != nil {
err = NewMultiError(err, afterErr)
@ -141,13 +146,14 @@ func (a *App) Run(arguments []string) (err error) {
err = afterErr
}
}
ec = afterEc
}()
}
if a.Before != nil {
err := a.Before(context)
ec, err = a.Before(context)
if err != nil {
return err
return ec, err
}
}
@ -161,20 +167,19 @@ func (a *App) Run(arguments []string) (err error) {
}
// Run default Action
a.Action(context)
return nil
return a.Action(context), nil
}
// Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() {
if err := a.Run(os.Args); err != nil {
if exitCode, err := a.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
os.Exit(exitCode)
}
}
// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
func (a *App) RunAsSubcommand(ctx *Context) (ec int, err error) {
// append help to commands
if len(a.Commands) > 0 {
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
@ -205,33 +210,33 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
} else {
ShowCommandHelp(ctx, context.Args().First())
}
return nerr
return DefaultExitCode, nerr
}
if err != nil {
fmt.Fprintln(a.Writer, "Incorrect Usage.")
fmt.Fprintln(a.Writer)
ShowSubcommandHelp(context)
return err
return DefaultExitCode, err
}
if checkCompletions(context) {
return nil
return 0, nil
}
if len(a.Commands) > 0 {
if checkSubcommandHelp(context) {
return nil
return 0, nil
}
} else {
if checkCommandHelp(ctx, context.Args().First()) {
return nil
return 0, nil
}
}
if a.After != nil {
defer func() {
afterErr := a.After(context)
afterEc, afterErr := a.After(context)
if afterErr != nil {
if err != nil {
err = NewMultiError(err, afterErr)
@ -239,13 +244,14 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
err = afterErr
}
}
ec = afterEc
}()
}
if a.Before != nil {
err := a.Before(context)
ec, err = a.Before(context)
if err != nil {
return err
return ec, err
}
}
@ -259,9 +265,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
}
// Run default Action
a.Action(context)
return nil
return a.Action(context), nil
}
// Returns the named command on App. Returns nil if the command does not exist

View File

@ -50,7 +50,7 @@ func (c Command) FullName() string {
}
// Invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c Command) Run(ctx *Context) error {
func (c Command) Run(ctx *Context) (int, error) {
if len(c.Subcommands) > 0 || c.Before != nil || c.After != nil {
return c.startApp(ctx)
}
@ -104,7 +104,7 @@ func (c Command) Run(ctx *Context) error {
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
return err
return DefaultExitCode, err
}
nerr := normalizeFlags(c.Flags, set)
@ -112,20 +112,19 @@ func (c Command) Run(ctx *Context) error {
fmt.Fprintln(ctx.App.Writer, nerr)
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
return nerr
return DefaultExitCode, nerr
}
context := NewContext(ctx.App, set, ctx)
if checkCommandCompletions(context, c.Name) {
return nil
return 0, nil
}
if checkCommandHelp(context, c.Name) {
return nil
return 0, nil
}
context.Command = c
c.Action(context)
return nil
return c.Action(context), nil
}
func (c Command) Names() []string {
@ -148,7 +147,7 @@ func (c Command) HasName(name string) bool {
return false
}
func (c Command) startApp(ctx *Context) error {
func (c Command) startApp(ctx *Context) (int, error) {
app := NewApp()
// set the name and usage

View File

@ -5,14 +5,14 @@ type BashCompleteFn func(*Context)
// An action to execute before any subcommands are run, but after the context is ready
// If a non-nil error is returned, no subcommands are run
type BeforeFn func(*Context) error
type BeforeFn func(*Context) (int, error)
// An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
type AfterFn func(*Context) error
type AfterFn func(*Context) (int, error)
// The action to execute when no subcommands are specified
type ActionFn func(*Context)
type ActionFn func(*Context) int
// Execute this function if the proper command cannot be found
type CommandNotFoundFn func(*Context, string)

View File

@ -72,13 +72,14 @@ var helpCommand = Command{
Name: "help",
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
Action: func(c *Context) {
Action: func(c *Context) int {
args := c.Args()
if args.Present() {
ShowCommandHelp(c, args.First())
} else {
ShowAppHelp(c)
}
return 0
},
}
@ -86,13 +87,14 @@ var helpSubcommand = Command{
Name: "help",
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
Action: func(c *Context) {
Action: func(c *Context) int {
args := c.Args()
if args.Present() {
ShowCommandHelp(c, args.First())
} else {
ShowSubcommandHelp(c)
}
return 0
},
}