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