package cli import ( "bytes" "fmt" "log" "sort" "strings" ) // HelpFunc is the type of the function that is responsible for generating // the help output when the CLI must show the general help text. type HelpFunc func(map[string]CommandFactory) string // BasicHelpFunc generates some basic help output that is usually good enough // for most CLI applications. func BasicHelpFunc(app string) HelpFunc { return func(commands map[string]CommandFactory) string { var buf bytes.Buffer buf.WriteString(fmt.Sprintf( "Usage: %s [--version] [--help] []\n\n", app)) buf.WriteString("Available commands are:\n") // Get the list of keys so we can sort them, and also get the maximum // key length so they can be aligned properly. keys := make([]string, 0, len(commands)) maxKeyLen := 0 for key := range commands { if len(key) > maxKeyLen { maxKeyLen = len(key) } keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { commandFunc, ok := commands[key] if !ok { // This should never happen since we JUST built the list of // keys. panic("command not found: " + key) } command, err := commandFunc() if err != nil { log.Printf("[ERR] cli: Command '%s' failed to load: %s", key, err) continue } key = fmt.Sprintf("%s%s", key, strings.Repeat(" ", maxKeyLen-len(key))) buf.WriteString(fmt.Sprintf(" %s %s\n", key, command.Synopsis())) } return buf.String() } } // FilteredHelpFunc will filter the commands to only include the keys // in the include parameter. func FilteredHelpFunc(include []string, f HelpFunc) HelpFunc { return func(commands map[string]CommandFactory) string { set := make(map[string]struct{}) for _, k := range include { set[k] = struct{}{} } filtered := make(map[string]CommandFactory) for k, f := range commands { if _, ok := set[k]; ok { filtered[k] = f } } return f(filtered) } }