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 ShortName string // A short description of the usage of this command Usage string // A longer explanation of how the command works Description 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 } // 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()) } // Define here so it closes over the above variables showErrAndHelp := func(err error) { fmt.Fprintln(ctx.App.Writer, err) fmt.Fprintln(ctx.App.Writer) ShowCommandHelp(ctx, c.Name) fmt.Fprintln(ctx.App.Writer) } if err != nil { showErrAndHelp(fmt.Errorf("Incorrect Usage.")) return err } nerr := normalizeFlags(c.Flags, set) if nerr != nil { showErrAndHelp(nerr) return nerr } cerr := checkRequiredFlags(c.Flags, set) if cerr != nil { showErrAndHelp(cerr) return cerr } context := NewContext(ctx.App, set, ctx.globalSet) if checkCommandCompletions(context, c.Name) { return nil } if checkCommandHelp(context, c.Name) { return nil } context.Command = c c.Action(context) return nil } // Returns true if Command.Name or Command.ShortName matches given name func (c Command) HasName(name string) bool { return c.Name == name || c.ShortName == name } 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) 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 // 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 } return app.RunAsSubcommand(ctx) }