Allow commands to explicitly state if they do, or do not take arbitrary arguments
Check that arguments are in ValidArgs If a command defined cmd.ValidArgs check that the argument is actually in ValidArgs and fail if it is not.
This commit is contained in:
committed by
Albert Nigmatzianov
parent
715f41bd7a
commit
d89c499964
76
command.go
76
command.go
@ -27,6 +27,15 @@ 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
|
||||
@ -59,6 +68,8 @@ type Command struct {
|
||||
// but accepted if entered manually.
|
||||
ArgAliases []string
|
||||
|
||||
// Does this command take arbitrary arguments
|
||||
TakesArgs Args
|
||||
// BashCompletionFunction is custom functions used by the bash autocompletion generator.
|
||||
BashCompletionFunction string
|
||||
|
||||
@ -472,6 +483,15 @@ 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) {
|
||||
@ -515,31 +535,53 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
|
||||
commandFound, a := innerfind(c, args)
|
||||
argsWOflags := stripFlags(a, commandFound)
|
||||
|
||||
// no subcommand, always take args
|
||||
if !commandFound.HasSubCommands() {
|
||||
// "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
|
||||
}
|
||||
|
||||
// root command with subcommands, do subcommand checking
|
||||
if commandFound == c && len(argsWOflags) > 0 {
|
||||
suggestionsString := ""
|
||||
if !c.DisableSuggestions {
|
||||
if c.SuggestionsMinimumDistance <= 0 {
|
||||
c.SuggestionsMinimumDistance = 2
|
||||
}
|
||||
if suggestions := c.SuggestionsFor(argsWOflags[0]); len(suggestions) > 0 {
|
||||
suggestionsString += "\n\nDid you mean this?\n"
|
||||
for _, s := range suggestions {
|
||||
suggestionsString += fmt.Sprintf("\t%v\n", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
return commandFound, a, fmt.Errorf("unknown command %q for %q%s", argsWOflags[0], commandFound.CommandPath(), suggestionsString)
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
return commandFound, a, nil
|
||||
}
|
||||
|
||||
func (c *Command) findSuggestions(argsWOflags []string) string {
|
||||
if c.DisableSuggestions {
|
||||
return ""
|
||||
}
|
||||
if c.SuggestionsMinimumDistance <= 0 {
|
||||
c.SuggestionsMinimumDistance = 2
|
||||
}
|
||||
suggestionsString := ""
|
||||
if suggestions := c.SuggestionsFor(argsWOflags[0]); len(suggestions) > 0 {
|
||||
suggestionsString += "\n\nDid you mean this?\n"
|
||||
for _, s := range suggestions {
|
||||
suggestionsString += fmt.Sprintf("\t%v\n", s)
|
||||
}
|
||||
}
|
||||
return suggestionsString
|
||||
}
|
||||
|
||||
// SuggestionsFor provides suggestions for the typedName.
|
||||
func (c *Command) SuggestionsFor(typedName string) []string {
|
||||
suggestions := []string{}
|
||||
|
Reference in New Issue
Block a user