Allow commands to explicitly state if they do, or do not take arbitrary arguments

This commit is contained in:
Eric Paris 2015-06-09 14:17:58 -04:00
parent 385fc87e43
commit 143ebcd4ee
2 changed files with 77 additions and 7 deletions

View file

@ -68,6 +68,7 @@ var cmdDeprecated = &Command{
Deprecated: "Please use echo instead", Deprecated: "Please use echo instead",
Run: func(cmd *Command, args []string) { Run: func(cmd *Command, args []string) {
}, },
TakesArgs: None,
} }
var cmdTimes = &Command{ var cmdTimes = &Command{
@ -80,6 +81,7 @@ var cmdTimes = &Command{
Run: func(cmd *Command, args []string) { Run: func(cmd *Command, args []string) {
tt = args tt = args
}, },
TakesArgs: Arbitrary,
} }
var cmdRootNoRun = &Command{ var cmdRootNoRun = &Command{
@ -95,6 +97,17 @@ var cmdRootSameName = &Command{
Use: "print", Use: "print",
Short: "Root with the same name as a subcommand", Short: "Root with the same name as a subcommand",
Long: "The root description for help", Long: "The root description for help",
TakesArgs: None,
}
var cmdRootTakesArgs = &Command{
Use: "root-with-args [random args]",
Short: "The root can run it's own function and takes args!",
Long: "The root description for help, and some args",
Run: func(cmd *Command, args []string) {
tr = args
},
TakesArgs: Arbitrary,
} }
var cmdRootWithRun = &Command{ var cmdRootWithRun = &Command{
@ -396,6 +409,42 @@ func TestGrandChildSameName(t *testing.T) {
} }
} }
func TestRootTakesNoArgs(t *testing.T) {
c := initializeWithSameName()
c.AddCommand(cmdPrint, cmdEcho)
result := simpleTester(c, "illegal")
expectedError := `unknown command "illegal" for "print"`
if !strings.Contains(result.Error.Error(), expectedError) {
t.Errorf("exptected %v, got %v", expectedError, result.Error.Error())
}
}
func TestRootTakesArgs(t *testing.T) {
c := cmdRootTakesArgs
result := simpleTester(c, "legal")
if result.Error != nil {
t.Errorf("expected no error, but got %v", result.Error)
}
}
func TestSubCmdTakesNoArgs(t *testing.T) {
result := fullSetupTest("deprecated illegal")
expectedError := `unknown command "illegal" for "cobra-test deprecated"`
if !strings.Contains(result.Error.Error(), expectedError) {
t.Errorf("exptected %v, got %v", expectedError, result.Error.Error())
}
}
func TestSubCmdTakesArgs(t *testing.T) {
noRRSetupTest("echo times one two")
if strings.Join(tt, " ") != "one two" {
t.Error("Command didn't parse correctly")
}
}
func TestFlagLong(t *testing.T) { func TestFlagLong(t *testing.T) {
noRRSetupTest("echo --intone=13 something here") noRRSetupTest("echo --intone=13 something here")

View file

@ -28,6 +28,14 @@ import (
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
) )
type Args int
const (
Legacy Args = iota
Arbitrary
None
)
// Command is just that, a command for your application. // Command is just that, a command for your application.
// eg. 'go run' ... 'run' is the command. Cobra requires // eg. 'go run' ... 'run' is the command. Cobra requires
// you to define the usage and description as part of your command // you to define the usage and description as part of your command
@ -47,6 +55,8 @@ type Command struct {
Example string Example string
// List of all valid non-flag arguments, used for bash completions *TODO* actually validate these // List of all valid non-flag arguments, used for bash completions *TODO* actually validate these
ValidArgs []string ValidArgs []string
// Does this command take arbitrary arguments
TakesArgs Args
// Custom functions used by the bash autocompletion generator // Custom functions used by the bash autocompletion generator
BashCompletionFunction string BashCompletionFunction string
// Is this command deprecated and should print this string when used? // Is this command deprecated and should print this string when used?
@ -416,6 +426,11 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
commandFound, a := innerfind(c, args) commandFound, a := innerfind(c, args)
argsWOflags := stripFlags(a, commandFound) 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 // no subcommand, always take args
if !commandFound.HasSubCommands() { if !commandFound.HasSubCommands() {
return commandFound, a, nil return commandFound, a, nil
@ -424,6 +439,12 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
if commandFound == c && len(argsWOflags) > 0 { if commandFound == c && len(argsWOflags) > 0 {
return commandFound, a, fmt.Errorf("unknown command %q for %q", argsWOflags[0], commandFound.CommandPath()) return commandFound, a, fmt.Errorf("unknown command %q for %q", argsWOflags[0], commandFound.CommandPath())
} }
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())
}
return commandFound, a, nil return commandFound, a, nil
} }