Add support for help command

This commit is contained in:
spf13 2013-09-24 16:03:22 -04:00
parent 68f3c66d07
commit fb2146e9e5
3 changed files with 86 additions and 22 deletions

View File

@ -215,9 +215,9 @@ func TestChildCommandFlags(t *testing.T) {
}
// Testing with flag only existing on child
buf2 := new(bytes.Buffer)
buf.Reset()
c = initialize()
c.SetOutput(buf2)
c.SetOutput(buf)
cmdEcho.AddCommand(cmdTimes)
c.AddCommand(cmdPrint, cmdEcho)
c.SetArgs(strings.Split("echo -j 99 -i77 one two", " "))
@ -227,10 +227,9 @@ func TestChildCommandFlags(t *testing.T) {
t.Errorf("invalid flag should generate error")
}
if !strings.Contains(buf2.String(), "intone=123") {
if !strings.Contains(buf.String(), "intone=123") {
t.Errorf("Wrong error message displayed, \n %s", buf.String())
}
}
func TestPersistentFlags(t *testing.T) {
@ -268,3 +267,29 @@ func TestPersistentFlags(t *testing.T) {
t.Errorf("local flag not parsed correctly. Expected false, had %v", flagb2)
}
}
func TestHelpCommand(t *testing.T) {
buf := new(bytes.Buffer)
c := initialize()
cmdEcho.AddCommand(cmdTimes)
c.AddCommand(cmdPrint, cmdEcho)
c.SetArgs(strings.Split("help echo", " "))
c.SetOutput(buf)
c.Execute()
if !strings.Contains(buf.String(), cmdEcho.Long) {
t.Errorf("Wrong error message displayed, \n %s", buf.String())
}
buf.Reset()
c = initialize()
cmdEcho.AddCommand(cmdTimes)
c.AddCommand(cmdPrint, cmdEcho)
c.SetArgs(strings.Split("help echo times", " "))
c.SetOutput(buf)
c.Execute()
if !strings.Contains(buf.String(), cmdTimes.Long) {
t.Errorf("Wrong error message displayed, \n %s", buf.String())
}
}

View File

@ -53,13 +53,14 @@ type Command struct {
}
// find the target command given the args and command tree
// Meant to be run on the highest node. Only searches down.
func (c *Command) Find(args []string) (cmd *Command, a []string, err error) {
if c == nil {
return nil, nil, fmt.Errorf("Called find() on a nil Command")
}
validSubCommand := false
if len(args) > 1 && c.HasSubCommands() {
if len(args) > 0 && c.HasSubCommands() {
for _, cmd := range c.commands {
if cmd.Name() == args[0] {
validSubCommand = true
@ -74,7 +75,7 @@ func (c *Command) Find(args []string) (cmd *Command, a []string, err error) {
return nil, nil, nil
}
func (c *Command) Commander() *Commander {
func (c *Command) Root() *Command {
var findRoot func(*Command) *Command
findRoot = func(x *Command) *Command {
@ -84,7 +85,12 @@ func (c *Command) Commander() *Commander {
return x
}
}
cmdr := findRoot(c)
return findRoot(c)
}
func (c *Command) Commander() *Commander {
cmdr := c.Root()
if cmdr.cmdr != nil {
return cmdr.cmdr
} else {
@ -108,7 +114,12 @@ func (c *Command) execute(args []string) (err error) {
if e == nil {
err = cmd.ParseFlags(a)
if err != nil {
cmd.Usage()
// report flag parsing error
c.Println(strings.Split(err.Error(), "\n")[0])
erx := cmd.Usage()
if erx != nil {
return erx
}
return err
} else {
argWoFlags := cmd.Flags().Args()
@ -162,9 +173,6 @@ func (c *Command) Printf(format string, i ...interface{}) {
// Can be defined by user by overriding Commander.UsageFunc
func (c *Command) Usage() error {
err := c.Commander().UsageFunc(c)
if err != nil {
fmt.Println(err)
}
return err
}

View File

@ -28,10 +28,12 @@ type Commander struct {
Command
args []string
output *io.Writer // nil means stderr; use out() accessor
UsageFunc func(*Command) error // Usage can be defined by application
UsageTemplate string // Can be defined by Application
HelpTemplate string // Can be defined by Application
output *io.Writer // nil means stderr; use out() accessor
UsageFunc func(*Command) error // Usage can be defined by application
UsageTemplate string // Can be defined by Application
HelpTemplate string // Can be defined by Application
HelpFunc func(*Command, []string) // Help can be defined by application
HelpCommand *Command
}
// Provide the user with a new commander.
@ -39,10 +41,24 @@ func NewCommander() (c *Commander) {
c = new(Commander)
c.cmdr = c
c.UsageFunc = c.defaultUsage
c.HelpFunc = c.defaultHelp
c.initTemplates()
return
}
func (c *Commander) initHelp() {
if c.HelpCommand == nil {
c.HelpCommand = &Command{
Use: "help [command to learn about]",
Short: "Help about any command",
Long: `Help provides help for any command in the application.
Simply type ` + c.Name() + ` help [path to command] for full details.`,
Run: c.HelpFunc,
}
}
c.AddCommand(c.HelpCommand)
}
// Name for commander, should match application name
func (c *Commander) SetName(name string) {
c.name = name
@ -58,6 +74,9 @@ func (c *Commander) SetArgs(a []string) {
// and run through the command tree finding appropriate matches
// for commands and then corresponding flags.
func (c *Commander) Execute() (err error) {
// initialize help as the last point possible to allow for user
// overriding
c.initHelp()
if len(c.args) == 0 {
err = c.execute(os.Args[1:])
} else {
@ -75,12 +94,25 @@ func (c *Commander) out() io.Writer {
func (cmdr *Commander) defaultUsage(c *Command) error {
err := tmpl(cmdr.out(), cmdr.UsageTemplate, c)
if err != nil {
c.Println(err)
}
return err
}
func (cmdr *Commander) defaultHelp(c *Command, args []string) {
cmd, _, e := c.Root().Find(args)
if cmd == nil {
cmdr.Printf("Unknown help topic %#q. Run '%v help'.\n", args, cmdr.Name())
return
}
if e != nil {
cmdr.Printf("Unknown help topic %#q. Run '%v help'.\n", args, cmdr.Name())
} else {
err := tmpl(cmdr.out(), cmdr.HelpTemplate, cmd)
if err != nil {
c.Println(err)
}
}
}
//Print to out
func (c *Commander) PrintOut(i ...interface{}) {
fmt.Fprint(c.out(), i...)
@ -90,7 +122,6 @@ func (c *Commander) PrintOut(i ...interface{}) {
// If output is nil, os.Stderr is used.
func (c *Commander) SetOutput(output io.Writer) {
c.output = &output
//*c.output = output
}
func (c *Commander) initTemplates() {
@ -112,8 +143,8 @@ Additional help topics: {{if gt .Commands 0 }}{{range .Commands}}{{if not .Runna
Use "{{.Commander.Name}} help [command]" for more information about that command.
`
c.HelpTemplate = `{{if .Runnable}}Usage: {{.ProgramName}} {{.UsageLine}}
{{end}}{{.Long | trim}}
c.HelpTemplate = `{{.Name}}
{{.Long | trim}}
{{if .Runnable}}{{.Usage}}{{end}}
`
}