package cli import ( "fmt" "io/ioutil" "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 // A longer explanation of how the command works Description string // A short description of the arguments of this command ArgsUsage string // The function to call when checking for bash command completions BashComplete func(context *Context) // 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 func(context *Context) error // An action to execute after any subcommands are run, but after the subcommand has finished // It is run even if Action() panics After func(context *Context) error // The function to call when this command is invoked Action func(context *Context) // List of child commands Subcommands []Command // List of flags to parse Flags []Flag // Treat all flags as normal arguments if true SkipFlagParsing bool // Boolean to hide built-in help command HideHelp bool // Name of command for help, defaults to full command name HelpName string commandNamePath []string } // 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, " ") } // Invokes the command given the context, parses ctx.Args() to generate command-specific flags func (c Command) Run(ctx *Context) error { if len(c.Subcommands) > 0 || c.Before != nil || c.After != nil { 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) firstFlagIndex := -1 terminatorIndex := -1 for index, arg := range ctx.Args() { if arg == "--" { terminatorIndex = index break } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { firstFlagIndex = index } } var err error if firstFlagIndex > -1 && !c.SkipFlagParsing { 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()) } if err != nil { fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.") 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 } context.Command = c c.Action(context) return nil } func (c Command) Names() []string { names := []string{c.Name} if c.ShortName != "" { names = append(names, c.ShortName) } return append(names, c.Aliases...) } // 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() // set the name and usage app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) app.HelpName = fmt.Sprintf("%s %s", ctx.App.Name, c.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 // 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 } var newCmds []Command for _, cc := range app.Commands { cc.commandNamePath = []string{c.Name, cc.Name} newCmds = append(newCmds, cc) } app.Commands = newCmds return app.RunAsSubcommand(ctx) }