github.com/elopio/cli@v6.21.2-0.20160902224010-ea909d1fdb2f+incompatible/commands/v2/help_command.go (about)

     1  package v2
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"sort"
     7  	"strings"
     8  
     9  	"code.cloudfoundry.org/cli/actors/v2actions"
    10  	"code.cloudfoundry.org/cli/cf"
    11  	"code.cloudfoundry.org/cli/commands"
    12  	"code.cloudfoundry.org/cli/commands/flags"
    13  	"code.cloudfoundry.org/cli/commands/v2/internal"
    14  	"code.cloudfoundry.org/cli/utils/config"
    15  	"code.cloudfoundry.org/cli/utils/sortutils"
    16  )
    17  
    18  //go:generate counterfeiter . HelpActor
    19  
    20  // HelpActor handles the business logic of the help command
    21  type HelpActor interface {
    22  	// CommandInfoByName returns back a help command information for the given
    23  	// command
    24  	CommandInfoByName(interface{}, string) (v2actions.CommandInfo, error)
    25  
    26  	// CommandInfos returns a list of all commands
    27  	CommandInfos(interface{}) map[string]v2actions.CommandInfo
    28  }
    29  
    30  type HelpCommand struct {
    31  	UI     commands.UI
    32  	Actor  HelpActor
    33  	Config commands.Config
    34  
    35  	OptionalArgs flags.CommandName `positional-args:"yes"`
    36  	AllCommands  bool              `short:"a" description:"All available CLI commands"`
    37  	usage        interface{}       `usage:"CF_NAME help [COMMAND]"`
    38  }
    39  
    40  func (cmd *HelpCommand) Setup(config commands.Config, ui commands.UI) error {
    41  	cmd.Actor = v2actions.NewActor()
    42  	cmd.Config = config
    43  	cmd.UI = ui
    44  
    45  	return nil
    46  }
    47  
    48  func (cmd HelpCommand) Execute(args []string) error {
    49  	var err error
    50  	if cmd.OptionalArgs.CommandName == "" {
    51  		cmd.displayFullHelp()
    52  	} else {
    53  		err = cmd.displayCommand()
    54  	}
    55  
    56  	return err
    57  }
    58  
    59  func (cmd HelpCommand) displayFullHelp() {
    60  	cmd.displayHelpPreamble()
    61  	if cmd.AllCommands {
    62  		cmd.displayAllCommands()
    63  		cmd.displayHelpFooter()
    64  	} else {
    65  		cmd.displayCommonCommands()
    66  	}
    67  }
    68  
    69  func (cmd HelpCommand) displayHelpPreamble() {
    70  	cmd.UI.DisplayHelpHeader("NAME:")
    71  	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} - {{.CommandDescription}}",
    72  		[]string{"CommandDescription"},
    73  		map[string]interface{}{
    74  			"CommandName":        cmd.Config.BinaryName(),
    75  			"CommandDescription": "A command line tool to interact with Cloud Foundry",
    76  		})
    77  	cmd.UI.DisplayNewline()
    78  
    79  	cmd.UI.DisplayHelpHeader("USAGE:")
    80  	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} {{.CommandUsage}}",
    81  		[]string{"CommandUsage"},
    82  		map[string]interface{}{
    83  			"CommandName":  cmd.Config.BinaryName(),
    84  			"CommandUsage": "[global options] command [arguments...] [command options]",
    85  		})
    86  	cmd.UI.DisplayNewline()
    87  
    88  	cmd.UI.DisplayHelpHeader("VERSION:")
    89  	cmd.UI.DisplayText("   {{.Version}}-{{.Time}}", map[string]interface{}{
    90  		"Version": cf.Version,
    91  		"Time":    cf.BuiltOnDate,
    92  	})
    93  	cmd.UI.DisplayNewline()
    94  }
    95  
    96  func (cmd HelpCommand) displayCommonCommands() {
    97  	cmdInfo := cmd.Actor.CommandInfos(Commands)
    98  
    99  	for _, category := range internal.CommonHelpCategoryList {
   100  		cmd.UI.DisplayHelpHeader(category.CategoryName)
   101  		table := [][]string{}
   102  
   103  		for _, row := range category.CommandList {
   104  			finalRow := []string{}
   105  
   106  			for _, command := range row {
   107  				separator := ""
   108  				if info, ok := cmdInfo[command]; ok {
   109  					if len(info.Alias) > 0 {
   110  						separator = ","
   111  					}
   112  					finalRow = append(finalRow, fmt.Sprintf("%s%s%s", info.Name, separator, info.Alias))
   113  				}
   114  			}
   115  
   116  			table = append(table, finalRow)
   117  		}
   118  
   119  		cmd.UI.DisplayTable("   ", table)
   120  		cmd.UI.DisplayNewline()
   121  	}
   122  
   123  	pluginCommands := cmd.getSortedPluginCommands()
   124  	cmd.UI.DisplayHelpHeader("Commands offered by installed plugins:")
   125  
   126  	size := int(math.Ceil(float64(len(pluginCommands)) / 3))
   127  	table := make([][]string, size)
   128  	for i := 0; i < size; i++ {
   129  		table[i] = make([]string, 3)
   130  		for j := 0; j < 3; j++ {
   131  			index := i + j*size
   132  			if index < len(pluginCommands) {
   133  				table[i][j] = pluginCommands[index].Name
   134  			}
   135  		}
   136  	}
   137  
   138  	cmd.UI.DisplayTable("   ", table)
   139  	cmd.UI.DisplayNewline()
   140  
   141  	cmd.UI.DisplayHelpHeader("Global options:")
   142  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                         {{.Description}}",
   143  		[]string{"Description"},
   144  		map[string]interface{}{
   145  			"ENVName":     "--help, -h",
   146  			"Description": "Show help",
   147  		})
   148  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                                 {{.Description}}",
   149  		[]string{"Description"},
   150  		map[string]interface{}{
   151  			"ENVName":     "-v",
   152  			"Description": "Print API request diagnostics to stdout",
   153  		})
   154  	cmd.UI.DisplayNewline()
   155  	cmd.UI.DisplayText("'cf help -a' lists all commands with short descriptions. See 'cf help <command>' to read about a specific command.")
   156  }
   157  
   158  func (cmd HelpCommand) displayAllCommands() {
   159  	pluginCommands := cmd.getSortedPluginCommands()
   160  	cmdInfo := cmd.Actor.CommandInfos(Commands)
   161  	longestCmd := internal.LongestCommandName(cmdInfo, pluginCommands)
   162  
   163  	for _, category := range internal.HelpCategoryList {
   164  		cmd.UI.DisplayHelpHeader(category.CategoryName)
   165  
   166  		for _, row := range category.CommandList {
   167  			for _, command := range row {
   168  				cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}}{{.Gap}}{{.CommandDescription}}",
   169  					[]string{"CommandDescription"},
   170  					map[string]interface{}{
   171  						"CommandName":        cmdInfo[command].Name,
   172  						"CommandDescription": cmdInfo[command].Description,
   173  						"Gap":                strings.Repeat(" ", longestCmd+1-len(command)),
   174  					})
   175  			}
   176  
   177  			cmd.UI.DisplayNewline()
   178  		}
   179  	}
   180  
   181  	cmd.UI.DisplayHelpHeader("INSTALLED PLUGIN COMMANDS:")
   182  	for _, pluginCommand := range pluginCommands {
   183  		cmd.UI.DisplayText("   {{.CommandName}}{{.Gap}}{{.CommandDescription}}", map[string]interface{}{
   184  			"CommandName":        pluginCommand.Name,
   185  			"CommandDescription": pluginCommand.HelpText,
   186  			"Gap":                strings.Repeat(" ", longestCmd+1-len(pluginCommand.Name)),
   187  		})
   188  	}
   189  	cmd.UI.DisplayNewline()
   190  }
   191  
   192  func (cmd HelpCommand) displayHelpFooter() {
   193  	cmd.UI.DisplayHelpHeader("ENVIRONMENT VARIABLES:")
   194  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                     {{.Description}}",
   195  		[]string{"Description"},
   196  		map[string]interface{}{
   197  			"ENVName":     "CF_COLOR=false",
   198  			"Description": "Do not colorize output",
   199  		})
   200  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}               {{.Description}}",
   201  		[]string{"Description"},
   202  		map[string]interface{}{
   203  			"ENVName":     "CF_HOME=path/to/dir/",
   204  			"Description": "Override path to default config directory",
   205  		})
   206  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}        {{.Description}}",
   207  		[]string{"Description"},
   208  		map[string]interface{}{
   209  			"ENVName":     "CF_PLUGIN_HOME=path/to/dir/",
   210  			"Description": "Override path to default plugin config directory",
   211  		})
   212  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}              {{.Description}}",
   213  		[]string{"Description"},
   214  		map[string]interface{}{
   215  			"ENVName":     "CF_STAGING_TIMEOUT=15",
   216  			"Description": "Max wait time for buildpack staging, in minutes",
   217  		})
   218  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}               {{.Description}}",
   219  		[]string{"Description"},
   220  		map[string]interface{}{
   221  			"ENVName":     "CF_STARTUP_TIMEOUT=5",
   222  			"Description": "Max wait time for app instance startup, in minutes",
   223  		})
   224  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                      {{.Description}}",
   225  		[]string{"Description"},
   226  		map[string]interface{}{
   227  			"ENVName":     "CF_TRACE=true",
   228  			"Description": "Print API request diagnostics to stdout",
   229  		})
   230  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}         {{.Description}}",
   231  		[]string{"Description"},
   232  		map[string]interface{}{
   233  			"ENVName":     "CF_TRACE=path/to/trace.log",
   234  			"Description": "Append API request diagnostics to a log file",
   235  		})
   236  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}} {{.Description}}",
   237  		[]string{"Description"},
   238  		map[string]interface{}{
   239  			"ENVName":     "https_proxy=proxy.example.com:8080",
   240  			"Description": "Enable HTTP proxying for API requests",
   241  		})
   242  	cmd.UI.DisplayNewline()
   243  
   244  	cmd.UI.DisplayHelpHeader("GLOBAL OPTIONS:")
   245  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                         {{.Description}}",
   246  		[]string{"Description"},
   247  		map[string]interface{}{
   248  			"ENVName":     "--help, -h",
   249  			"Description": "Show help",
   250  		})
   251  	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                                 {{.Description}}",
   252  		[]string{"Description"},
   253  		map[string]interface{}{
   254  			"ENVName":     "-v",
   255  			"Description": "Print API request diagnostics to stdout",
   256  		})
   257  }
   258  
   259  func (cmd HelpCommand) displayCommand() error {
   260  	cmdInfo, err := cmd.Actor.CommandInfoByName(Commands, cmd.OptionalArgs.CommandName)
   261  	if err != nil {
   262  		if err, ok := err.(v2actions.ErrorInvalidCommand); ok {
   263  			var found bool
   264  			if cmdInfo, found = cmd.findPlugin(); !found {
   265  				return err
   266  			}
   267  		} else {
   268  			return err
   269  		}
   270  	}
   271  
   272  	cmd.UI.DisplayText("NAME:")
   273  	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} - {{.CommandDescription}}",
   274  		[]string{"CommandDescription"},
   275  		map[string]interface{}{
   276  			"CommandName":        cmdInfo.Name,
   277  			"CommandDescription": cmdInfo.Description,
   278  		})
   279  
   280  	cmd.UI.DisplayNewline()
   281  	usageString := strings.Replace(cmdInfo.Usage, "CF_NAME", cmd.Config.BinaryName(), -1)
   282  	cmd.UI.DisplayText("USAGE:")
   283  	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandUsage}}",
   284  		[]string{"CommandUsage"},
   285  		map[string]interface{}{
   286  			"CommandUsage": usageString,
   287  		})
   288  
   289  	if cmdInfo.Alias != "" {
   290  		cmd.UI.DisplayNewline()
   291  		cmd.UI.DisplayText("ALIAS:")
   292  		cmd.UI.DisplayText("   {{.Alias}}",
   293  			map[string]interface{}{
   294  				"Alias": cmdInfo.Alias,
   295  			})
   296  	}
   297  
   298  	if len(cmdInfo.Flags) != 0 {
   299  		cmd.UI.DisplayNewline()
   300  		cmd.UI.DisplayText("OPTIONS:")
   301  		nameWidth := internal.LongestFlagWidth(cmdInfo.Flags) + 6
   302  		for _, flag := range cmdInfo.Flags {
   303  			var name string
   304  			if flag.Short != "" && flag.Long != "" {
   305  				name = fmt.Sprintf("--%s, -%s", flag.Long, flag.Short)
   306  			} else if flag.Short != "" {
   307  				name = "-" + flag.Short
   308  			} else {
   309  				name = "--" + flag.Long
   310  			}
   311  
   312  			cmd.UI.DisplayTextWithKeyTranslations("   {{.Flags}}{{.Spaces}}{{.Description}}",
   313  				[]string{"Description"},
   314  				map[string]interface{}{
   315  					"Flags":       name,
   316  					"Spaces":      strings.Repeat(" ", nameWidth-len(name)),
   317  					"Description": flag.Description,
   318  				})
   319  		}
   320  	}
   321  
   322  	if len(cmdInfo.RelatedCommands) > 0 {
   323  		cmd.UI.DisplayNewline()
   324  		cmd.UI.DisplayText("SEE ALSO:")
   325  		cmd.UI.DisplayText("   {{.RelatedCommands}}",
   326  			map[string]interface{}{
   327  				"RelatedCommands": strings.Join(cmdInfo.RelatedCommands, ", "),
   328  			})
   329  	}
   330  
   331  	return nil
   332  }
   333  
   334  func (cmd HelpCommand) findPlugin() (v2actions.CommandInfo, bool) {
   335  	for _, pluginConfig := range cmd.Config.PluginConfig() {
   336  		for _, command := range pluginConfig.Commands {
   337  			if command.Name == cmd.OptionalArgs.CommandName {
   338  				return internal.ConvertPluginToCommandInfo(command), true
   339  			}
   340  		}
   341  	}
   342  
   343  	return v2actions.CommandInfo{}, false
   344  }
   345  
   346  func (cmd HelpCommand) getSortedPluginCommands() config.PluginCommands {
   347  	plugins := cmd.Config.PluginConfig()
   348  
   349  	sortedPluginNames := sortutils.Alphabetic{}
   350  	for plugin, _ := range plugins {
   351  		sortedPluginNames = append(sortedPluginNames, plugin)
   352  	}
   353  	sort.Sort(sortedPluginNames)
   354  
   355  	pluginCommands := config.PluginCommands{}
   356  	for _, pluginName := range sortedPluginNames {
   357  		sortedCommands := plugins[pluginName].Commands
   358  		sort.Sort(sortedCommands)
   359  		pluginCommands = append(pluginCommands, sortedCommands...)
   360  	}
   361  
   362  	return pluginCommands
   363  }