github.com/LukasHeimann/cloudfoundrycli/v8@v8.4.4/command/common/help_command.go (about)

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