From 6cf7b5d5189d13b1a0c2c2a89d8104f6f5234924 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Thu, 1 Dec 2016 21:18:20 +0100 Subject: [PATCH] Enhanced the usage and help output Now each command has its own output destination, which can be changed using a SetOutput(writer) method. Added ability to display the options of each command. --- bee.go | 102 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 33 deletions(-) diff --git a/bee.go b/bee.go index 11e14fd..cda8ac4 100644 --- a/bee.go +++ b/bee.go @@ -52,6 +52,9 @@ type Command struct { // CustomFlags indicates that the command will do its own // flag parsing. CustomFlags bool + + // output out writer if set in SetOutput(w) + output *io.Writer } // Name returns the command's name: the first word in the usage line. @@ -64,10 +67,24 @@ func (c *Command) Name() string { return name } +// SetOutput sets the destination for usage and error messages. +// If output is nil, os.Stderr is used. +func (c *Command) SetOutput(output io.Writer) { + c.output = &output +} + +// Out returns the out writer of the current command. +// If cmd.output is nil, os.Stderr is used. +func (c *Command) Out() io.Writer { + if c.output != nil { + return *c.output + } + return NewColorWriter(os.Stderr) +} + // Usage puts out the usage for the command. func (c *Command) Usage() { - fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine) - fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(string(c.Long))) + tmpl(helpTemplate, c) os.Exit(2) } @@ -77,7 +94,15 @@ func (c *Command) Runnable() bool { return c.Run != nil } -var commands = []*Command{ +func (c *Command) Options() map[string]string { + options := make(map[string]string) + c.Flag.VisitAll(func(f *flag.Flag) { + options[f.Name] = f.Usage + }) + return options +} + +var availableCommands = []*Command{ cmdNew, cmdRun, cmdPack, @@ -103,6 +128,7 @@ func main() { log.SetFlags(0) args := flag.Args() + if len(args) < 1 { usage() } @@ -112,7 +138,7 @@ func main() { return } - for _, cmd := range commands { + for _, cmd := range availableCommands { if cmd.Name() == args[0] && cmd.Run != nil { cmd.Flag.Usage = func() { cmd.Usage() } if cmd.CustomFlags { @@ -137,46 +163,57 @@ func main() { } } - fmt.Fprintf(os.Stderr, "bee: unknown subcommand %q\nRun 'bee help' for usage.\n", args[0]) - os.Exit(2) + printErrorAndExit("Unknown subcommand") } -var usageTemplate = `Bee is a tool for managing beego framework. +var usageTemplate = `Bee is a Fast and Flexible tool for managing your Beego Web Application. -Usage: +{{"USAGE" | headline}} + {{"bee command [arguments]" | bold}} - bee command [arguments] - -The commands are: +{{"AVAILABLE COMMANDS" | headline}} {{range .}}{{if .Runnable}} - {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + {{.Name | printf "%-11s" | bold}} {{.Short}}{{end}}{{end}} -Use "bee help [command]" for more information about a command. +Use {{"bee help [command]" | bold}} for more information about a command. -Additional help topics: +{{"ADDITIONAL HELP TOPICS" | headline}} {{range .}}{{if not .Runnable}} {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} -Use "bee help [topic]" for more information about that topic. +Use {{"bee help [topic]" | bold}} for more information about that topic. ` -var helpTemplate = `{{if .Runnable}}usage: bee {{.UsageLine}} +var helpTemplate = `{{"USAGE" | headline}} + {{.UsageLine | printf "bee %s" | bold}} +{{if .Options}}{{endline}}{{"OPTIONS" | headline}}{{range $k,$v := .Options}} + {{$k | printf "-%-11s" | bold}} {{$v}}{{end}}{{endline}}{{end}} +{{"DESCRIPTION" | headline}} + {{.Long | trim}} +` -{{end}}{{.Long | trim}} +var errorTemplate = `bee: %s. +Use {{"bee help" | bold}} for more information. ` func usage() { - tmpl(os.Stdout, usageTemplate, commands) + tmpl(usageTemplate, availableCommands) os.Exit(2) } -func tmpl(w io.Writer, text string, data interface{}) { +func tmpl(text string, data interface{}) { + output := NewColorWriter(os.Stderr) + t := template.New("top") - t.Funcs(template.FuncMap{"trim": func(s template.HTML) template.HTML { - return template.HTML(strings.TrimSpace(string(s))) - }}) + t.Funcs(template.FuncMap{ + "trim": func(s template.HTML) template.HTML { return template.HTML(strings.TrimSpace(string(s))) }, + "bold": bold, + "headline": MagentaBold, + "endline": EndLine, + }) + template.Must(t.Parse(text)) - if err := t.Execute(w, data); err != nil { + if err := t.Execute(output, data); err != nil { panic(err) } } @@ -184,24 +221,23 @@ func tmpl(w io.Writer, text string, data interface{}) { func help(args []string) { if len(args) == 0 { usage() - // not exit 2: succeeded at 'go help'. - return } if len(args) != 1 { - fmt.Fprintf(os.Stdout, "usage: bee help command\n\nToo many arguments given.\n") - os.Exit(2) // failed at 'bee help' + printErrorAndExit("Too many arguments") } arg := args[0] - for _, cmd := range commands { + for _, cmd := range availableCommands { if cmd.Name() == arg { - tmpl(os.Stdout, helpTemplate, cmd) - // not exit 2: succeeded at 'go help cmd'. + tmpl(helpTemplate, cmd) return } } - - fmt.Fprintf(os.Stdout, "Unknown help topic %#q. Run 'bee help'.\n", arg) - os.Exit(2) // failed at 'bee help cmd' + printErrorAndExit("Unknown help topic") +} + +func printErrorAndExit(message string) { + tmpl(fmt.Sprintf(errorTemplate, message), nil) + os.Exit(2) }