From 13f0c8c0f68ee8e25a9bb44dfdad819e1a7e5e2e Mon Sep 17 00:00:00 2001 From: Summer Mousa Date: Wed, 16 Apr 2014 11:18:00 -0500 Subject: [PATCH] More consistent implementation of recursive subcommands --- app.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ command.go | 39 ++++++++++++++++++++++++++++++++++++- help.go | 46 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) diff --git a/app.go b/app.go index a7c7122..f9342d7 100644 --- a/app.go +++ b/app.go @@ -129,6 +129,62 @@ func (a *App) Run(arguments []string) error { return nil } +// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags +func (a *App) RunAsSubcommand(c *Context) error { + // append help to commands + if a.Command(helpCommand.Name) == nil { + a.Commands = append(a.Commands, helpCommand) + } + + // append help flags + if a.EnableBashCompletion { + a.appendFlag(BashCompletionFlag) + } + a.appendFlag(BoolFlag{"help, h", "show help"}) + + // parse flags + set := flagSet(a.Name, a.Flags) + set.SetOutput(ioutil.Discard) + err := set.Parse(c.Args().Tail()) + nerr := normalizeFlags(a.Flags, set) + context := NewContext(a, set, set) + + if nerr != nil { + fmt.Println(nerr) + ShowSubcommandHelp(context) + fmt.Println("") + return nerr + } + + if err != nil { + fmt.Printf("Incorrect Usage.\n\n") + ShowSubcommandHelp(context) + fmt.Println("") + return err + } + + if checkCompletions(context) { + return nil + } + + if checkSubcommandHelp(context) { + return nil + } + + args := context.Args() + if args.Present() { + name := args.First() + c := a.Command(name) + if c != nil { + return c.Run(context) + } + } + + // Run default Action + a.Action(context) + return nil +} + // Returns the named command on App. Returns nil if the command does not exist func (a *App) Command(name string) *Command { for _, c := range a.Commands { diff --git a/command.go b/command.go index 60d29aa..2d5c662 100644 --- a/command.go +++ b/command.go @@ -14,7 +14,7 @@ type Command struct { ShortName string // A short description of the usage of this command Usage string - // A longer explaination of how the command works + // A longer explanation of how the command works Description string // The function to call when checking for bash command completions BashComplete func(context *Context) @@ -22,10 +22,16 @@ type Command struct { Action func(context *Context) // List of flags to parse Flags []Flag + // List of child commands + Subcommands []Command } // Invokes the command given the context, parses ctx.Args() to generate command-specific flags func (c Command) Run(ctx *Context) error { + if len(c.Subcommands) > 0 { + return c.startApp(ctx) + } + // append help to flags c.Flags = append( c.Flags, @@ -89,3 +95,34 @@ func (c Command) Run(ctx *Context) error { func (c Command) HasName(name string) bool { return c.Name == name || c.ShortName == name } + +func (c Command) startApp(ctx *Context) error { + app := NewApp() + + // set the name and usage + app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) + if c.Description != "" { + app.Usage = c.Description + } else { + app.Usage = c.Usage + } + + // set the flags and commands + app.Commands = c.Subcommands + app.Flags = c.Flags + + // bash completion + app.EnableBashCompletion = ctx.App.EnableBashCompletion + if c.BashComplete != nil { + app.BashComplete = c.BashComplete + } + + // set the action + if c.Action != nil { + app.Action = c.Action + } else { + app.Action = helpSubcommand.Action + } + + return app.RunAsSubcommand(ctx) +} diff --git a/help.go b/help.go index 64b4bab..db9ff02 100644 --- a/help.go +++ b/help.go @@ -44,6 +44,24 @@ OPTIONS: {{end}} ` +// The text template for the subcommand help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var SubcommandHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} + +USAGE: + {{.Name}} [global options] command [command options] [arguments...] + +COMMANDS: + {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} + {{end}} + +OPTIONS: + {{range .Flags}}{{.}} + {{end}} +` + var helpCommand = Command{ Name: "help", ShortName: "h", @@ -58,6 +76,20 @@ var helpCommand = Command{ }, } +var helpSubcommand = Command{ + Name: "help", + ShortName: "h", + Usage: "Shows a list of commands or help for one command", + Action: func(c *Context) { + args := c.Args() + if args.Present() { + ShowCommandHelp(c, args.First()) + } else { + ShowSubcommandHelp(c) + } + }, +} + // Prints help for the App var HelpPrinter = printHelp @@ -87,6 +119,11 @@ func ShowCommandHelp(c *Context, command string) { fmt.Printf("No help topic for '%v'\n", command) } +// Prints help for the given subcommand +func ShowSubcommandHelp(c *Context) { + HelpPrinter(SubcommandHelpTemplate, c.App) +} + // Prints the version number of the App func ShowVersion(c *Context) { fmt.Printf("%v version %v\n", c.App.Name, c.App.Version) @@ -145,6 +182,15 @@ func checkCommandHelp(c *Context, name string) bool { return false } +func checkSubcommandHelp(c *Context) bool { + if c.GlobalBool("h") || c.GlobalBool("help") { + ShowSubcommandHelp(c) + return true + } + + return false +} + func checkCompletions(c *Context) bool { if c.GlobalBool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { ShowCompletions(c)