command.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package cli
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "strings"
  6. )
  7. // Command is a subcommand for a cli.App.
  8. type Command struct {
  9. // The name of the command
  10. Name string
  11. // short name of the command. Typically one character
  12. ShortName string
  13. // A short description of the usage of this command
  14. Usage string
  15. // A longer explanation of how the command works
  16. Description string
  17. // The function to call when checking for bash command completions
  18. BashComplete func(context *Context)
  19. // An action to execute before any sub-subcommands are run, but after the context is ready
  20. // If a non-nil error is returned, no sub-subcommands are run
  21. Before func(context *Context) error
  22. // The function to call when this command is invoked
  23. Action func(context *Context)
  24. // List of child commands
  25. Subcommands []Command
  26. // List of flags to parse
  27. Flags []Flag
  28. // Treat all flags as normal arguments if true
  29. SkipFlagParsing bool
  30. // Boolean to hide built-in help command
  31. HideHelp bool
  32. // Command argument requirements
  33. Requires *Requires
  34. }
  35. // Invokes the command given the context, parses ctx.Args() to generate command-specific flags
  36. func (c Command) Run(ctx *Context) error {
  37. if len(c.Subcommands) > 0 || c.Before != nil {
  38. return c.startApp(ctx)
  39. }
  40. if !c.HideHelp {
  41. // append help to flags
  42. c.Flags = append(
  43. c.Flags,
  44. HelpFlag,
  45. )
  46. }
  47. if ctx.App.EnableBashCompletion {
  48. c.Flags = append(c.Flags, BashCompletionFlag)
  49. }
  50. set := flagSet(c.Name, c.Flags)
  51. set.SetOutput(ioutil.Discard)
  52. firstFlagIndex := -1
  53. for index, arg := range ctx.Args() {
  54. if strings.HasPrefix(arg, "-") {
  55. firstFlagIndex = index
  56. break
  57. }
  58. }
  59. var err error
  60. if firstFlagIndex > -1 && !c.SkipFlagParsing {
  61. args := ctx.Args()
  62. regularArgs := args[1:firstFlagIndex]
  63. flagArgs := args[firstFlagIndex:]
  64. err = set.Parse(append(flagArgs, regularArgs...))
  65. } else {
  66. err = set.Parse(ctx.Args().Tail())
  67. }
  68. if err != nil {
  69. fmt.Printf("Incorrect Usage.\n\n")
  70. ShowCommandHelp(ctx, c.Name)
  71. fmt.Println("")
  72. return err
  73. }
  74. nerr := normalizeFlags(c.Flags, set)
  75. if nerr != nil {
  76. fmt.Println(nerr)
  77. fmt.Println("")
  78. ShowCommandHelp(ctx, c.Name)
  79. fmt.Println("")
  80. return nerr
  81. }
  82. context := NewContext(ctx.App, set, ctx.globalSet)
  83. if checkCommandCompletions(context, c.Name) {
  84. return nil
  85. }
  86. if checkCommandHelp(context, c.Name) {
  87. return nil
  88. }
  89. if err := context.Satisfies(c.Requires); err != nil {
  90. return err
  91. }
  92. context.Command = c
  93. c.Action(context)
  94. return nil
  95. }
  96. // Returns true if Command.Name or Command.ShortName matches given name
  97. func (c Command) HasName(name string) bool {
  98. return c.Name == name || c.ShortName == name
  99. }
  100. func (c Command) startApp(ctx *Context) error {
  101. app := NewApp()
  102. // set the name and usage
  103. app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
  104. if c.Description != "" {
  105. app.Usage = c.Description
  106. } else {
  107. app.Usage = c.Usage
  108. }
  109. // set the flags and commands
  110. app.Commands = c.Subcommands
  111. app.Flags = c.Flags
  112. app.HideHelp = c.HideHelp
  113. // bash completion
  114. app.EnableBashCompletion = ctx.App.EnableBashCompletion
  115. if c.BashComplete != nil {
  116. app.BashComplete = c.BashComplete
  117. }
  118. // set the actions
  119. app.Before = c.Before
  120. if c.Action != nil {
  121. app.Action = c.Action
  122. } else {
  123. app.Action = helpSubcommand.Action
  124. }
  125. return app.RunAsSubcommand(ctx)
  126. }