285 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			285 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Go
		
	
	
	
package cli
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"io/ioutil"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
// Command is a subcommand for a cli.App.
 | 
						|
type Command struct {
 | 
						|
	// The name of the command
 | 
						|
	Name string
 | 
						|
	// short name of the command. Typically one character (deprecated, use `Aliases`)
 | 
						|
	ShortName string
 | 
						|
	// A list of aliases for the command
 | 
						|
	Aliases []string
 | 
						|
	// 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
 | 
						|
	Description string
 | 
						|
	// A short description of the arguments of this command
 | 
						|
	ArgsUsage string
 | 
						|
	// The category the command is part of
 | 
						|
	Category string
 | 
						|
	// 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
 | 
						|
	// An action to execute after any subcommands are run, but after the subcommand has finished
 | 
						|
	// It is run even if Action() panics
 | 
						|
	After AfterFunc
 | 
						|
	// The function to call when this command is invoked
 | 
						|
	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.
 | 
						|
	OnUsageError OnUsageErrorFunc
 | 
						|
	// List of child commands
 | 
						|
	Subcommands Commands
 | 
						|
	// List of flags to parse
 | 
						|
	Flags []Flag
 | 
						|
	// Treat all flags as normal arguments if true
 | 
						|
	SkipFlagParsing bool
 | 
						|
	// Skip argument reordering which attempts to move flags before arguments,
 | 
						|
	// but only works if all flags appear after all arguments. This behavior was
 | 
						|
	// removed n version 2 since it only works under specific conditions so we
 | 
						|
	// backport here by exposing it as an option for compatibility.
 | 
						|
	SkipArgReorder bool
 | 
						|
	// Boolean to hide built-in help command
 | 
						|
	HideHelp bool
 | 
						|
	// Boolean to hide this command from help or completion
 | 
						|
	Hidden bool
 | 
						|
 | 
						|
	// Full name of command for help, defaults to full command name, including parent commands.
 | 
						|
	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, " ")
 | 
						|
}
 | 
						|
 | 
						|
// Commands is a slice of Command
 | 
						|
type Commands []Command
 | 
						|
 | 
						|
// 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 != BoolFlag{}) {
 | 
						|
		// append help to flags
 | 
						|
		c.Flags = append(
 | 
						|
			c.Flags,
 | 
						|
			HelpFlag,
 | 
						|
		)
 | 
						|
	}
 | 
						|
 | 
						|
	if ctx.App.EnableBashCompletion {
 | 
						|
		c.Flags = append(c.Flags, BashCompletionFlag)
 | 
						|
	}
 | 
						|
 | 
						|
	set := flagSet(c.Name, c.Flags)
 | 
						|
	set.SetOutput(ioutil.Discard)
 | 
						|
 | 
						|
	if c.SkipFlagParsing {
 | 
						|
		err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
 | 
						|
	} else if !c.SkipArgReorder {
 | 
						|
		firstFlagIndex := -1
 | 
						|
		terminatorIndex := -1
 | 
						|
		for index, arg := range ctx.Args() {
 | 
						|
			if arg == "--" {
 | 
						|
				terminatorIndex = index
 | 
						|
				break
 | 
						|
			} else if arg == "-" {
 | 
						|
				// Do nothing. A dash alone is not really a flag.
 | 
						|
				continue
 | 
						|
			} else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 {
 | 
						|
				firstFlagIndex = index
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if firstFlagIndex > -1 {
 | 
						|
			args := ctx.Args()
 | 
						|
			regularArgs := make([]string, len(args[1:firstFlagIndex]))
 | 
						|
			copy(regularArgs, args[1:firstFlagIndex])
 | 
						|
 | 
						|
			var flagArgs []string
 | 
						|
			if terminatorIndex > -1 {
 | 
						|
				flagArgs = args[firstFlagIndex:terminatorIndex]
 | 
						|
				regularArgs = append(regularArgs, args[terminatorIndex:]...)
 | 
						|
			} else {
 | 
						|
				flagArgs = args[firstFlagIndex:]
 | 
						|
			}
 | 
						|
 | 
						|
			err = set.Parse(append(flagArgs, regularArgs...))
 | 
						|
		} else {
 | 
						|
			err = set.Parse(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.Fprintln(ctx.App.Writer, "Incorrect Usage:", err.Error())
 | 
						|
		fmt.Fprintln(ctx.App.Writer)
 | 
						|
		ShowCommandHelp(ctx, c.Name)
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	nerr := normalizeFlags(c.Flags, set)
 | 
						|
	if nerr != nil {
 | 
						|
		fmt.Fprintln(ctx.App.Writer, nerr)
 | 
						|
		fmt.Fprintln(ctx.App.Writer)
 | 
						|
		ShowCommandHelp(ctx, c.Name)
 | 
						|
		return nerr
 | 
						|
	}
 | 
						|
 | 
						|
	context := NewContext(ctx.App, set, ctx)
 | 
						|
 | 
						|
	if checkCommandCompletions(context, c.Name) {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	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
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	context.Command = c
 | 
						|
	err = HandleAction(c.Action, context)
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		HandleExitCoder(err)
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// Names returns the names including short names and aliases.
 | 
						|
func (c Command) Names() []string {
 | 
						|
	names := []string{c.Name}
 | 
						|
 | 
						|
	if c.ShortName != "" {
 | 
						|
		names = append(names, c.ShortName)
 | 
						|
	}
 | 
						|
 | 
						|
	return append(names, c.Aliases...)
 | 
						|
}
 | 
						|
 | 
						|
// HasName returns true if Command.Name or Command.ShortName matches given name
 | 
						|
func (c Command) HasName(name string) bool {
 | 
						|
	for _, n := range c.Names() {
 | 
						|
		if n == name {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func (c Command) startApp(ctx *Context) error {
 | 
						|
	app := NewApp()
 | 
						|
	app.Metadata = ctx.App.Metadata
 | 
						|
	// set the name and usage
 | 
						|
	app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
 | 
						|
	if c.HelpName == "" {
 | 
						|
		app.HelpName = c.HelpName
 | 
						|
	} else {
 | 
						|
		app.HelpName = app.Name
 | 
						|
	}
 | 
						|
 | 
						|
	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
 | 
						|
	app.HideHelp = c.HideHelp
 | 
						|
 | 
						|
	app.Version = ctx.App.Version
 | 
						|
	app.HideVersion = ctx.App.HideVersion
 | 
						|
	app.Compiled = ctx.App.Compiled
 | 
						|
	app.Author = ctx.App.Author
 | 
						|
	app.Email = ctx.App.Email
 | 
						|
	app.Writer = ctx.App.Writer
 | 
						|
 | 
						|
	app.categories = CommandCategories{}
 | 
						|
	for _, command := range c.Subcommands {
 | 
						|
		app.categories = app.categories.AddCommand(command.Category, command)
 | 
						|
	}
 | 
						|
 | 
						|
	sort.Sort(app.categories)
 | 
						|
 | 
						|
	// bash completion
 | 
						|
	app.EnableBashCompletion = ctx.App.EnableBashCompletion
 | 
						|
	if c.BashComplete != nil {
 | 
						|
		app.BashComplete = c.BashComplete
 | 
						|
	}
 | 
						|
 | 
						|
	// set the actions
 | 
						|
	app.Before = c.Before
 | 
						|
	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)
 | 
						|
}
 |