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" "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 // App is the main structure of a cli application. It is recomended that
// an app be created with the cli.NewApp() function // an app be created with the cli.NewApp() function
type App struct { 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 // 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 != "" { if a.Author != "" || a.Email != "" {
a.Authors = append(a.Authors, Author{Name: a.Author, Email: 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) fmt.Fprintln(a.Writer, nerr)
context := NewContext(a, set, nil) context := NewContext(a, set, nil)
ShowAppHelp(context) ShowAppHelp(context)
return nerr return DefaultExitCode, nerr
} }
context := NewContext(a, set, nil) 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, "Incorrect Usage.")
fmt.Fprintln(a.Writer) fmt.Fprintln(a.Writer)
ShowAppHelp(context) ShowAppHelp(context)
return err return DefaultExitCode, err
} }
if checkCompletions(context) { if checkCompletions(context) {
return nil return 0, nil
} }
if checkHelp(context) { if checkHelp(context) {
return nil return 0, nil
} }
if checkVersion(context) { if checkVersion(context) {
return nil return 0, nil
} }
if a.After != nil { if a.After != nil {
defer func() { defer func() {
afterErr := a.After(context) afterEc, afterErr := a.After(context)
if afterErr != nil { if afterErr != nil {
if err != nil { if err != nil {
err = NewMultiError(err, afterErr) err = NewMultiError(err, afterErr)
@ -141,13 +146,14 @@ func (a *App) Run(arguments []string) (err error) {
err = afterErr err = afterErr
} }
} }
ec = afterEc
}() }()
} }
if a.Before != nil { if a.Before != nil {
err := a.Before(context) ec, err = a.Before(context)
if err != nil { if err != nil {
return err return ec, err
} }
} }
@ -161,20 +167,19 @@ func (a *App) Run(arguments []string) (err error) {
} }
// Run default Action // Run default Action
a.Action(context) return a.Action(context), nil
return nil
} }
// Another entry point to the cli app, takes care of passing arguments and error handling // Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() { 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) 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 // 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 // append help to commands
if len(a.Commands) > 0 { if len(a.Commands) > 0 {
if a.Command(helpCommand.Name) == nil && !a.HideHelp { if a.Command(helpCommand.Name) == nil && !a.HideHelp {
@ -205,33 +210,33 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
} else { } else {
ShowCommandHelp(ctx, context.Args().First()) ShowCommandHelp(ctx, context.Args().First())
} }
return nerr return DefaultExitCode, nerr
} }
if err != nil { if err != nil {
fmt.Fprintln(a.Writer, "Incorrect Usage.") fmt.Fprintln(a.Writer, "Incorrect Usage.")
fmt.Fprintln(a.Writer) fmt.Fprintln(a.Writer)
ShowSubcommandHelp(context) ShowSubcommandHelp(context)
return err return DefaultExitCode, err
} }
if checkCompletions(context) { if checkCompletions(context) {
return nil return 0, nil
} }
if len(a.Commands) > 0 { if len(a.Commands) > 0 {
if checkSubcommandHelp(context) { if checkSubcommandHelp(context) {
return nil return 0, nil
} }
} else { } else {
if checkCommandHelp(ctx, context.Args().First()) { if checkCommandHelp(ctx, context.Args().First()) {
return nil return 0, nil
} }
} }
if a.After != nil { if a.After != nil {
defer func() { defer func() {
afterErr := a.After(context) afterEc, afterErr := a.After(context)
if afterErr != nil { if afterErr != nil {
if err != nil { if err != nil {
err = NewMultiError(err, afterErr) err = NewMultiError(err, afterErr)
@ -239,13 +244,14 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
err = afterErr err = afterErr
} }
} }
ec = afterEc
}() }()
} }
if a.Before != nil { if a.Before != nil {
err := a.Before(context) ec, err = a.Before(context)
if err != nil { if err != nil {
return err return ec, err
} }
} }
@ -259,9 +265,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
} }
// Run default Action // Run default Action
a.Action(context) return a.Action(context), nil
return nil
} }
// 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

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 // 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 { if len(c.Subcommands) > 0 || c.Before != nil || c.After != nil {
return c.startApp(ctx) 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, "Incorrect Usage.")
fmt.Fprintln(ctx.App.Writer) fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name) ShowCommandHelp(ctx, c.Name)
return err return DefaultExitCode, err
} }
nerr := normalizeFlags(c.Flags, set) 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, nerr)
fmt.Fprintln(ctx.App.Writer) fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name) ShowCommandHelp(ctx, c.Name)
return nerr return DefaultExitCode, nerr
} }
context := NewContext(ctx.App, set, ctx) context := NewContext(ctx.App, set, ctx)
if checkCommandCompletions(context, c.Name) { if checkCommandCompletions(context, c.Name) {
return nil return 0, nil
} }
if checkCommandHelp(context, c.Name) { if checkCommandHelp(context, c.Name) {
return nil return 0, nil
} }
context.Command = c context.Command = c
c.Action(context) return c.Action(context), nil
return nil
} }
func (c Command) Names() []string { func (c Command) Names() []string {
@ -148,7 +147,7 @@ func (c Command) HasName(name string) bool {
return false return false
} }
func (c Command) startApp(ctx *Context) error { func (c Command) startApp(ctx *Context) (int, error) {
app := NewApp() app := NewApp()
// set the name and usage // 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 // 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 // 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 // An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics // 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 // 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 // Execute this function if the proper command cannot be found
type CommandNotFoundFn func(*Context, string) type CommandNotFoundFn func(*Context, string)

View File

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