Merge pull request #586 from minio/extra-info
Add support for custom help templates.
This commit is contained in:
commit
cf33a9befe
6
app.go
6
app.go
@ -85,6 +85,12 @@ type App struct {
|
||||
ErrWriter io.Writer
|
||||
// Other custom info
|
||||
Metadata map[string]interface{}
|
||||
// Carries a function which returns app specific info.
|
||||
ExtraInfo func() map[string]string
|
||||
// CustomAppHelpTemplate the text template for app help topic.
|
||||
// cli.go uses text/template to render templates. You can
|
||||
// render custom help text by setting this variable.
|
||||
CustomAppHelpTemplate string
|
||||
|
||||
didSetup bool
|
||||
}
|
||||
|
@ -59,6 +59,11 @@ type Command struct {
|
||||
// Full name of command for help, defaults to full command name, including parent commands.
|
||||
HelpName string
|
||||
commandNamePath []string
|
||||
|
||||
// CustomHelpTemplate the text template for the command help topic.
|
||||
// cli.go uses text/template to render templates. You can
|
||||
// render custom help text by setting this variable.
|
||||
CustomHelpTemplate string
|
||||
}
|
||||
|
||||
type CommandsByName []Command
|
||||
@ -250,6 +255,7 @@ func (c Command) startApp(ctx *Context) error {
|
||||
|
||||
// set CommandNotFound
|
||||
app.CommandNotFound = ctx.App.CommandNotFound
|
||||
app.CustomAppHelpTemplate = c.CustomHelpTemplate
|
||||
|
||||
// set the flags and commands
|
||||
app.Commands = c.Subcommands
|
||||
|
52
help.go
52
help.go
@ -112,17 +112,42 @@ var helpSubcommand = Command{
|
||||
// Prints help for the App or Command
|
||||
type helpPrinter func(w io.Writer, templ string, data interface{})
|
||||
|
||||
// Prints help for the App or Command with custom template function.
|
||||
type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{})
|
||||
|
||||
// HelpPrinter is a function that writes the help output. If not set a default
|
||||
// is used. The function signature is:
|
||||
// func(w io.Writer, templ string, data interface{})
|
||||
var HelpPrinter helpPrinter = printHelp
|
||||
|
||||
// HelpPrinterCustom is same as HelpPrinter but
|
||||
// takes a custom function for template function map.
|
||||
var HelpPrinterCustom helpPrinterCustom = printHelpCustom
|
||||
|
||||
// VersionPrinter prints the version for the App
|
||||
var VersionPrinter = printVersion
|
||||
|
||||
// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code.
|
||||
func ShowAppHelpAndExit(c *Context, exitCode int) {
|
||||
ShowAppHelp(c)
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
// ShowAppHelp is an action that displays the help.
|
||||
func ShowAppHelp(c *Context) error {
|
||||
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
|
||||
func ShowAppHelp(c *Context) (err error) {
|
||||
if c.App.CustomAppHelpTemplate == "" {
|
||||
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
|
||||
return
|
||||
}
|
||||
customAppData := func() map[string]interface{} {
|
||||
if c.App.ExtraInfo == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"ExtraInfo": c.App.ExtraInfo,
|
||||
}
|
||||
}
|
||||
HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData())
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -138,6 +163,12 @@ func DefaultAppComplete(c *Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// ShowCommandHelpAndExit - exits with code after showing help
|
||||
func ShowCommandHelpAndExit(c *Context, command string, code int) {
|
||||
ShowCommandHelp(c, command)
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// ShowCommandHelp prints help for the given command
|
||||
func ShowCommandHelp(ctx *Context, command string) error {
|
||||
// show the subcommand help for a command with subcommands
|
||||
@ -148,7 +179,11 @@ func ShowCommandHelp(ctx *Context, command string) error {
|
||||
|
||||
for _, c := range ctx.App.Commands {
|
||||
if c.HasName(command) {
|
||||
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
|
||||
if c.CustomHelpTemplate != "" {
|
||||
HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil)
|
||||
} else {
|
||||
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -191,10 +226,15 @@ func ShowCommandCompletions(ctx *Context, command string) {
|
||||
}
|
||||
}
|
||||
|
||||
func printHelp(out io.Writer, templ string, data interface{}) {
|
||||
func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) {
|
||||
funcMap := template.FuncMap{
|
||||
"join": strings.Join,
|
||||
}
|
||||
if customFunc != nil {
|
||||
for key, value := range customFunc {
|
||||
funcMap[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
|
||||
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
||||
@ -210,6 +250,10 @@ func printHelp(out io.Writer, templ string, data interface{}) {
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
func printHelp(out io.Writer, templ string, data interface{}) {
|
||||
printHelpCustom(out, templ, data, nil)
|
||||
}
|
||||
|
||||
func checkVersion(c *Context) bool {
|
||||
found := false
|
||||
if VersionFlag.GetName() != "" {
|
||||
|
120
help_test.go
120
help_test.go
@ -3,6 +3,8 @@ package cli
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@ -256,6 +258,49 @@ func TestShowSubcommandHelp_CommandAliases(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestShowCommandHelp_Customtemplate(t *testing.T) {
|
||||
app := &App{
|
||||
Commands: []Command{
|
||||
{
|
||||
Name: "frobbly",
|
||||
Action: func(ctx *Context) error {
|
||||
return nil
|
||||
},
|
||||
HelpName: "foo frobbly",
|
||||
CustomHelpTemplate: `NAME:
|
||||
{{.HelpName}} - {{.Usage}}
|
||||
|
||||
USAGE:
|
||||
{{.HelpName}} [FLAGS] TARGET [TARGET ...]
|
||||
|
||||
FLAGS:
|
||||
{{range .VisibleFlags}}{{.}}
|
||||
{{end}}
|
||||
EXAMPLES:
|
||||
1. Frobbly runs with this param locally.
|
||||
$ {{.HelpName}} wobbly
|
||||
`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
output := &bytes.Buffer{}
|
||||
app.Writer = output
|
||||
app.Run([]string{"foo", "help", "frobbly"})
|
||||
|
||||
if strings.Contains(output.String(), "2. Frobbly runs without this param locally.") {
|
||||
t.Errorf("expected output to exclude \"2. Frobbly runs without this param locally.\"; got: %q", output.String())
|
||||
}
|
||||
|
||||
if !strings.Contains(output.String(), "1. Frobbly runs with this param locally.") {
|
||||
t.Errorf("expected output to include \"1. Frobbly runs with this param locally.\"; got: %q", output.String())
|
||||
}
|
||||
|
||||
if !strings.Contains(output.String(), "$ foo frobbly wobbly") {
|
||||
t.Errorf("expected output to include \"$ foo frobbly wobbly\"; got: %q", output.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestShowAppHelp_HiddenCommand(t *testing.T) {
|
||||
app := &App{
|
||||
Commands: []Command{
|
||||
@ -287,3 +332,78 @@ func TestShowAppHelp_HiddenCommand(t *testing.T) {
|
||||
t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestShowAppHelp_CustomAppTemplate(t *testing.T) {
|
||||
app := &App{
|
||||
Commands: []Command{
|
||||
{
|
||||
Name: "frobbly",
|
||||
Action: func(ctx *Context) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "secretfrob",
|
||||
Hidden: true,
|
||||
Action: func(ctx *Context) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
ExtraInfo: func() map[string]string {
|
||||
platform := fmt.Sprintf("OS: %s | Arch: %s", runtime.GOOS, runtime.GOARCH)
|
||||
goruntime := fmt.Sprintf("Version: %s | CPUs: %d", runtime.Version(), runtime.NumCPU())
|
||||
return map[string]string{
|
||||
"PLATFORM": platform,
|
||||
"RUNTIME": goruntime,
|
||||
}
|
||||
},
|
||||
CustomAppHelpTemplate: `NAME:
|
||||
{{.Name}} - {{.Usage}}
|
||||
|
||||
USAGE:
|
||||
{{.Name}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}} [COMMAND FLAGS | -h]{{end}} [ARGUMENTS...]
|
||||
|
||||
COMMANDS:
|
||||
{{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
|
||||
{{end}}{{if .VisibleFlags}}
|
||||
GLOBAL FLAGS:
|
||||
{{range .VisibleFlags}}{{.}}
|
||||
{{end}}{{end}}
|
||||
VERSION:
|
||||
2.0.0
|
||||
{{"\n"}}{{range $key, $value := ExtraInfo}}
|
||||
{{$key}}:
|
||||
{{$value}}
|
||||
{{end}}`,
|
||||
}
|
||||
|
||||
output := &bytes.Buffer{}
|
||||
app.Writer = output
|
||||
app.Run([]string{"app", "--help"})
|
||||
|
||||
if strings.Contains(output.String(), "secretfrob") {
|
||||
t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String())
|
||||
}
|
||||
|
||||
if !strings.Contains(output.String(), "frobbly") {
|
||||
t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
|
||||
}
|
||||
|
||||
if !strings.Contains(output.String(), "PLATFORM:") ||
|
||||
!strings.Contains(output.String(), "OS:") ||
|
||||
!strings.Contains(output.String(), "Arch:") {
|
||||
t.Errorf("expected output to include \"PLATFORM:, OS: and Arch:\"; got: %q", output.String())
|
||||
}
|
||||
|
||||
if !strings.Contains(output.String(), "RUNTIME:") ||
|
||||
!strings.Contains(output.String(), "Version:") ||
|
||||
!strings.Contains(output.String(), "CPUs:") {
|
||||
t.Errorf("expected output to include \"RUNTIME:, Version: and CPUs:\"; got: %q", output.String())
|
||||
}
|
||||
|
||||
if !strings.Contains(output.String(), "VERSION:") ||
|
||||
!strings.Contains(output.String(), "2.0.0") {
|
||||
t.Errorf("expected output to include \"VERSION:, 2.0.0\"; got: %q", output.String())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user