urfave-cli/command.go

246 lines
5.5 KiB
Go
Raw Normal View History

package cli
import (
"fmt"
"io/ioutil"
"sort"
"strings"
)
2013-11-01 14:31:37 +00:00
// Command is a subcommand for a cli.App.
type Command struct {
2013-11-01 14:31:37 +00:00
// The name of the command
Name string
// A list of aliases for the command
Aliases []string
2013-11-01 14:31:37 +00:00
// A short description of the usage of this command
Usage string
// Custom text to show on USAGE section of help
UsageText string
// A longer explanation of how the command works
2013-09-24 01:41:31 +00:00
Description string
2015-08-03 23:51:11 +00:00
// A short description of the arguments of this command
ArgsUsage string
2014-12-15 22:35:49 +00:00
// The category the command is part of
Category string
2014-04-10 17:14:13 +00:00
// The function to call when checking for bash command completions
BashComplete BashCompleteFunc
// An action to execute before any sub-subcommands are run, but after the context is ready
// If a non-nil error is returned, no sub-subcommands are run
Before BeforeFunc
2014-11-18 22:44:21 +00:00
// An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
After AfterFunc
2013-11-01 14:31:37 +00:00
// The function to call when this command is invoked
Action ActionFunc
// Execute this function if a usage error occurs.
OnUsageError OnUsageErrorFunc
// List of child commands
Subcommands []*Command
// List of flags to parse
Flags []Flag
2014-04-14 21:44:32 +00:00
// Treat all flags as normal arguments if true
SkipFlagParsing bool
2014-07-13 13:16:30 +00:00
// Boolean to hide built-in help command
HideHelp bool
// Boolean to hide this command from help or completion
Hidden bool
2015-08-13 05:14:26 +00:00
// Full name of command for help, defaults to full command name, including parent commands.
2015-08-13 04:43:14 +00:00
HelpName string
commandNamePath []string
}
// FullName returns the full name of the command.
// For subcommands this ensures that parent commands are part of the command path
func (c *Command) FullName() string {
if c.commandNamePath == nil {
return c.Name
}
return strings.Join(c.commandNamePath, " ")
}
2013-07-19 02:23:00 +00:00
// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c *Command) Run(ctx *Context) (err error) {
if len(c.Subcommands) > 0 {
return c.startApp(ctx)
}
if !c.HideHelp && HelpFlag != nil {
2014-07-13 13:16:30 +00:00
// append help to flags
c.appendFlag(HelpFlag)
2014-07-13 13:16:30 +00:00
}
2013-08-14 04:40:39 +00:00
if ctx.App.EnableBashCompletion {
c.appendFlag(BashCompletionFlag)
}
2013-07-20 15:21:20 +00:00
set := flagSet(c.Name, c.Flags)
set.SetOutput(ioutil.Discard)
if c.SkipFlagParsing {
err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
} else {
err = set.Parse(ctx.Args().Tail())
}
if err != nil {
if c.OnUsageError != nil {
err := c.OnUsageError(ctx, err, false)
HandleExitCoder(err)
return err
}
fmt.Fprintf(ctx.App.Writer, "Incorrect Usage: %s\n\n", err)
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
return err
}
2013-08-14 04:40:39 +00:00
2013-11-20 08:05:18 +00:00
nerr := normalizeFlags(c.Flags, set)
if nerr != nil {
fmt.Fprintln(ctx.App.Writer, nerr)
fmt.Fprintln(ctx.App.Writer)
2013-11-20 08:05:18 +00:00
ShowCommandHelp(ctx, c.Name)
return nerr
2013-11-20 08:05:18 +00:00
}
context := NewContext(ctx.App, set, ctx)
2014-04-10 17:14:13 +00:00
if checkCommandCompletions(context, c.Name) {
return nil
2014-04-10 17:14:13 +00:00
}
if checkCommandHelp(context, c.Name) {
return nil
}
if c.After != nil {
defer func() {
afterErr := c.After(context)
if afterErr != nil {
HandleExitCoder(err)
if err != nil {
err = newMultiError(err, afterErr)
} else {
err = afterErr
}
}
}()
}
if c.Before != nil {
err = c.Before(context)
if err != nil {
fmt.Fprintln(ctx.App.Writer, err)
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
HandleExitCoder(err)
return err
}
}
2014-03-29 19:02:05 +00:00
context.Command = c
err = c.Action(context)
if err != nil {
HandleExitCoder(err)
}
return err
2013-07-19 02:23:00 +00:00
}
2013-07-19 02:30:18 +00:00
// Names returns the names including short names and aliases.
func (c *Command) Names() []string {
return append([]string{c.Name}, c.Aliases...)
}
// HasName returns true if Command.Name matches given name
func (c *Command) HasName(name string) bool {
for _, n := range c.Names() {
if n == name {
return true
}
}
return false
2013-07-19 02:30:18 +00:00
}
func (c *Command) startApp(ctx *Context) error {
app := &App{
Metadata: ctx.App.Metadata,
Name: fmt.Sprintf("%s %s", ctx.App.Name, c.Name),
}
2015-08-13 04:58:25 +00:00
if c.HelpName == "" {
app.HelpName = c.HelpName
} else {
app.HelpName = app.Name
2015-08-13 04:58:25 +00:00
}
if c.Description != "" {
app.Usage = c.Description
} else {
app.Usage = c.Usage
}
// set CommandNotFound
app.CommandNotFound = ctx.App.CommandNotFound
// set the flags and commands
app.Commands = c.Subcommands
app.Flags = c.Flags
2014-07-13 13:16:30 +00:00
app.HideHelp = c.HideHelp
app.Version = ctx.App.Version
app.HideVersion = ctx.App.HideVersion
app.Compiled = ctx.App.Compiled
app.Writer = ctx.App.Writer
app.Categories = newCommandCategories()
for _, command := range c.Subcommands {
app.Categories.AddCommand(command.Category, command)
}
sort.Sort(app.Categories.(*commandCategories))
// bash completion
app.EnableBashCompletion = ctx.App.EnableBashCompletion
if c.BashComplete != nil {
app.BashComplete = c.BashComplete
}
// set the actions
app.Before = c.Before
2014-11-18 22:44:21 +00:00
app.After = c.After
if c.Action != nil {
app.Action = c.Action
} else {
app.Action = helpSubcommand.Action
}
for index, cc := range app.Commands {
app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
}
return app.RunAsSubcommand(ctx)
}
// VisibleFlags returns a slice of the Flags with Hidden=false
func (c *Command) VisibleFlags() []Flag {
return visibleFlags(c.Flags)
}
func (c *Command) appendFlag(fl Flag) {
if !hasFlag(c.Flags, fl) {
c.Flags = append(c.Flags, fl)
}
}
func hasCommand(commands []*Command, command *Command) bool {
for _, existing := range commands {
if command == existing {
return true
}
}
return false
}