github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/cli/command.go (about)

     1  package cli
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"sort"
     7  	"strings"
     8  )
     9  
    10  // Command is a subcommand for a cli.App.
    11  type Command struct {
    12  	// The name of the command
    13  	Name string
    14  	// short name of the command. Typically one character (deprecated, use `Aliases`)
    15  	ShortName string
    16  	// A list of aliases for the command
    17  	Aliases []string
    18  	// A short description of the usage of this command
    19  	Usage string
    20  	// Custom text to show on USAGE section of help
    21  	UsageText string
    22  	// A longer explanation of how the command works
    23  	Description string
    24  	// A short description of the arguments of this command
    25  	ArgsUsage string
    26  	// The category the command is part of
    27  	Category string
    28  	// The function to call when checking for bash command completions
    29  	BashComplete func(context *Context)
    30  	// An action to execute before any sub-subcommands are run, but after the context is ready
    31  	// If a non-nil error is returned, no sub-subcommands are run
    32  	Before func(context *Context) error
    33  	// An action to execute after any subcommands are run, but before the subcommand has finished
    34  	// It is run even if Action() panics
    35  	After func(context *Context) error
    36  	// The function to call when this command is invoked
    37  	Action func(context *Context)
    38  	// Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
    39  	// This function is able to replace the original error messages.
    40  	// If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
    41  	OnUsageError func(context *Context, err error) error
    42  	// List of child commands
    43  	Subcommands Commands
    44  	// List of flags to parse
    45  	Flags []Flag
    46  	// Treat all flags as normal arguments if true
    47  	SkipFlagParsing bool
    48  	// Boolean to hide built-in help command
    49  	HideHelp bool
    50  
    51  	// Full name of command for help, defaults to full command name, including parent commands.
    52  	HelpName        string
    53  	commandNamePath []string
    54  }
    55  
    56  // Returns the full name of the command.
    57  // For subcommands this ensures that parent commands are part of the command path
    58  func (c Command) FullName() string {
    59  	if c.commandNamePath == nil {
    60  		return c.Name
    61  	}
    62  	return strings.Join(c.commandNamePath, " ")
    63  }
    64  
    65  type Commands []Command
    66  
    67  // Invokes the command given the context, parses ctx.Args() to generate command-specific flags
    68  func (c Command) Run(ctx *Context) (err error) {
    69  	if len(c.Subcommands) > 0 {
    70  		return c.startApp(ctx)
    71  	}
    72  
    73  	if !c.HideHelp && (HelpFlag != BoolFlag{}) {
    74  		// append help to flags
    75  		c.Flags = append(
    76  			c.Flags,
    77  			HelpFlag,
    78  		)
    79  	}
    80  
    81  	if ctx.App.EnableBashCompletion {
    82  		c.Flags = append(c.Flags, BashCompletionFlag)
    83  	}
    84  
    85  	set := flagSet(c.Name, c.Flags)
    86  	set.SetOutput(ioutil.Discard)
    87  
    88  	if !c.SkipFlagParsing {
    89  		firstFlagIndex := -1
    90  		terminatorIndex := -1
    91  		for index, arg := range ctx.Args() {
    92  			if arg == "--" {
    93  				terminatorIndex = index
    94  				break
    95  			} else if arg == "-" {
    96  				// Do nothing. A dash alone is not really a flag.
    97  				continue
    98  			} else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 {
    99  				firstFlagIndex = index
   100  			}
   101  		}
   102  
   103  		if firstFlagIndex > -1 {
   104  			args := ctx.Args()
   105  			regularArgs := make([]string, len(args[1:firstFlagIndex]))
   106  			copy(regularArgs, args[1:firstFlagIndex])
   107  
   108  			var flagArgs []string
   109  			if terminatorIndex > -1 {
   110  				flagArgs = args[firstFlagIndex:terminatorIndex]
   111  				regularArgs = append(regularArgs, args[terminatorIndex:]...)
   112  			} else {
   113  				flagArgs = args[firstFlagIndex:]
   114  			}
   115  
   116  			err = set.Parse(append(flagArgs, regularArgs...))
   117  		} else {
   118  			err = set.Parse(ctx.Args().Tail())
   119  		}
   120  	} else {
   121  		if c.SkipFlagParsing {
   122  			err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
   123  		}
   124  	}
   125  
   126  	if err != nil {
   127  		if c.OnUsageError != nil {
   128  			err := c.OnUsageError(ctx, err)
   129  			return err
   130  		} else {
   131  			fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
   132  			fmt.Fprintln(ctx.App.Writer)
   133  			ShowCommandHelp(ctx, c.Name)
   134  			return err
   135  		}
   136  	}
   137  
   138  	nerr := normalizeFlags(c.Flags, set)
   139  	if nerr != nil {
   140  		fmt.Fprintln(ctx.App.Writer, nerr)
   141  		fmt.Fprintln(ctx.App.Writer)
   142  		ShowCommandHelp(ctx, c.Name)
   143  		return nerr
   144  	}
   145  	context := NewContext(ctx.App, set, ctx)
   146  
   147  	if checkCommandCompletions(context, c.Name) {
   148  		return nil
   149  	}
   150  
   151  	if checkCommandHelp(context, c.Name) {
   152  		return nil
   153  	}
   154  
   155  	if c.After != nil {
   156  		defer func() {
   157  			afterErr := c.After(context)
   158  			if afterErr != nil {
   159  				if err != nil {
   160  					err = NewMultiError(err, afterErr)
   161  				} else {
   162  					err = afterErr
   163  				}
   164  			}
   165  		}()
   166  	}
   167  
   168  	if c.Before != nil {
   169  		err := c.Before(context)
   170  		if err != nil {
   171  			fmt.Fprintln(ctx.App.Writer, err)
   172  			fmt.Fprintln(ctx.App.Writer)
   173  			ShowCommandHelp(ctx, c.Name)
   174  			return err
   175  		}
   176  	}
   177  
   178  	context.Command = c
   179  	c.Action(context)
   180  	return nil
   181  }
   182  
   183  func (c Command) Names() []string {
   184  	names := []string{c.Name}
   185  
   186  	if c.ShortName != "" {
   187  		names = append(names, c.ShortName)
   188  	}
   189  
   190  	return append(names, c.Aliases...)
   191  }
   192  
   193  // Returns true if Command.Name or Command.ShortName matches given name
   194  func (c Command) HasName(name string) bool {
   195  	for _, n := range c.Names() {
   196  		if n == name {
   197  			return true
   198  		}
   199  	}
   200  	return false
   201  }
   202  
   203  func (c Command) startApp(ctx *Context) error {
   204  	app := NewApp()
   205  
   206  	// set the name and usage
   207  	app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
   208  	if c.HelpName == "" {
   209  		app.HelpName = c.HelpName
   210  	} else {
   211  		app.HelpName = app.Name
   212  	}
   213  
   214  	if c.Description != "" {
   215  		app.Usage = c.Description
   216  	} else {
   217  		app.Usage = c.Usage
   218  	}
   219  
   220  	// set CommandNotFound
   221  	app.CommandNotFound = ctx.App.CommandNotFound
   222  
   223  	// set the flags and commands
   224  	app.Commands = c.Subcommands
   225  	app.Flags = c.Flags
   226  	app.HideHelp = c.HideHelp
   227  
   228  	app.Version = ctx.App.Version
   229  	app.HideVersion = ctx.App.HideVersion
   230  	app.Compiled = ctx.App.Compiled
   231  	app.Author = ctx.App.Author
   232  	app.Email = ctx.App.Email
   233  	app.Writer = ctx.App.Writer
   234  
   235  	app.categories = CommandCategories{}
   236  	for _, command := range c.Subcommands {
   237  		app.categories = app.categories.AddCommand(command.Category, command)
   238  	}
   239  
   240  	sort.Sort(app.categories)
   241  
   242  	// bash completion
   243  	app.EnableBashCompletion = ctx.App.EnableBashCompletion
   244  	if c.BashComplete != nil {
   245  		app.BashComplete = c.BashComplete
   246  	}
   247  
   248  	// set the actions
   249  	app.Before = c.Before
   250  	app.After = c.After
   251  	if c.Action != nil {
   252  		app.Action = c.Action
   253  	} else {
   254  		app.Action = helpSubcommand.Action
   255  	}
   256  
   257  	for index, cc := range app.Commands {
   258  		app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
   259  	}
   260  
   261  	return app.RunAsSubcommand(ctx)
   262  }