Refactor TakesArgs to use an interface for arg validation.

Fix some typos in README and comments.
Move arg validation to after flag validation so that the help flag is run first.
Pass the same args to ValidateArgs as the Run methods receive.
Update README.

Signed-off-by: Daniel Nephin <dnephin@gmail.com>
This commit is contained in:
Daniel Nephin
2016-05-07 13:24:05 -04:00
committed by Albert Nigmatzianov
parent d89c499964
commit f20b4e9c32
4 changed files with 160 additions and 84 deletions

View File

@ -27,15 +27,6 @@ import (
flag "github.com/spf13/pflag"
)
type Args int
const (
Legacy Args = iota
Arbitrary
ValidOnly
None
)
// Command is just that, a command for your application.
// E.g. 'go run ...' - 'run' is the command. Cobra requires
// you to define the usage and description as part of your command
@ -68,8 +59,8 @@ type Command struct {
// but accepted if entered manually.
ArgAliases []string
// Does this command take arbitrary arguments
TakesArgs Args
// Expected arguments
Args PositionalArgs
// BashCompletionFunction is custom functions used by the bash autocompletion generator.
BashCompletionFunction string
@ -483,15 +474,6 @@ func argsMinusFirstX(args []string, x string) []string {
return args
}
func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
// 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) (*Command, []string, error) {
@ -533,39 +515,13 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
}
commandFound, a := innerfind(c, args)
argsWOflags := stripFlags(a, commandFound)
// "Legacy" has some 'odd' characteristics.
// - root commands with no subcommands can take arbitrary arguments
// - root commands with subcommands will do subcommand validity checking
// - subcommands will always accept arbitrary arguments
if commandFound.TakesArgs == Legacy {
// no subcommand, always take args
if !commandFound.HasSubCommands() {
return commandFound, a, nil
}
// root command with subcommands, do subcommand checking
if commandFound == c && len(argsWOflags) > 0 {
return commandFound, a, fmt.Errorf("unknown command %q for %q%s", argsWOflags[0], commandFound.CommandPath(), c.findSuggestions(argsWOflags))
}
return commandFound, a, nil
}
if commandFound.TakesArgs == None && len(argsWOflags) > 0 {
return commandFound, a, fmt.Errorf("unknown command %q for %q", argsWOflags[0], commandFound.CommandPath())
}
if commandFound.TakesArgs == ValidOnly && len(commandFound.ValidArgs) > 0 {
for _, v := range argsWOflags {
if !stringInSlice(v, commandFound.ValidArgs) {
return commandFound, a, fmt.Errorf("invalid argument %q for %q%s", v, commandFound.CommandPath(), c.findSuggestions(argsWOflags))
}
}
if commandFound.Args == nil {
return commandFound, a, legacyArgs(commandFound, stripFlags(a, commandFound))
}
return commandFound, a, nil
}
func (c *Command) findSuggestions(argsWOflags []string) string {
func (c *Command) findSuggestions(arg string) string {
if c.DisableSuggestions {
return ""
}
@ -573,7 +529,7 @@ func (c *Command) findSuggestions(argsWOflags []string) string {
c.SuggestionsMinimumDistance = 2
}
suggestionsString := ""
if suggestions := c.SuggestionsFor(argsWOflags[0]); len(suggestions) > 0 {
if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 {
suggestionsString += "\n\nDid you mean this?\n"
for _, s := range suggestions {
suggestionsString += fmt.Sprintf("\t%v\n", s)
@ -666,6 +622,10 @@ func (c *Command) execute(a []string) (err error) {
argWoFlags = a
}
if err := c.ValidateArgs(argWoFlags); err != nil {
return err
}
for p := c; p != nil; p = p.Parent() {
if p.PersistentPreRunE != nil {
if err := p.PersistentPreRunE(c, argWoFlags); err != nil {
@ -789,6 +749,13 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
return cmd, err
}
func (c *Command) ValidateArgs(args []string) error {
if c.Args == nil {
return nil
}
return c.Args(c, args)
}
// InitDefaultHelpFlag adds default help flag to c.
// It is called automatically by executing the c or by calling help and usage.
// If c already has help flag, it will do nothing.