mirror of
https://github.com/spf13/cobra
synced 2024-11-24 22:57:12 +00:00
feat: generalize ValidArgs; use it implicitly with any validator
This commit is contained in:
parent
dbf85f6104
commit
2cd7871821
5 changed files with 270 additions and 69 deletions
65
args.go
65
args.go
|
@ -7,6 +7,25 @@ import (
|
||||||
|
|
||||||
type PositionalArgs func(cmd *Command, args []string) error
|
type PositionalArgs func(cmd *Command, args []string) error
|
||||||
|
|
||||||
|
// validateArgs returns an error if there are any positional args that are not in
|
||||||
|
// the `ValidArgs` field of `Command`
|
||||||
|
func validateArgs(cmd *Command, args []string) error {
|
||||||
|
if len(cmd.ValidArgs) > 0 {
|
||||||
|
// Remove any description that may be included in ValidArgs.
|
||||||
|
// A description is following a tab character.
|
||||||
|
var validArgs []string
|
||||||
|
for _, v := range cmd.ValidArgs {
|
||||||
|
validArgs = append(validArgs, strings.Split(v, "\t")[0])
|
||||||
|
}
|
||||||
|
for _, v := range args {
|
||||||
|
if !stringInSlice(v, validArgs) {
|
||||||
|
return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Legacy arg validation has the following behaviour:
|
// Legacy arg validation has the following behaviour:
|
||||||
// - root commands with no subcommands can take arbitrary arguments
|
// - root commands with no subcommands can take arbitrary arguments
|
||||||
// - root commands with subcommands will do subcommand validity checking
|
// - root commands with subcommands will do subcommand validity checking
|
||||||
|
@ -32,25 +51,6 @@ func NoArgs(cmd *Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnlyValidArgs returns an error if any args are not in the list of ValidArgs.
|
|
||||||
func OnlyValidArgs(cmd *Command, args []string) error {
|
|
||||||
if len(cmd.ValidArgs) > 0 {
|
|
||||||
// Remove any description that may be included in ValidArgs.
|
|
||||||
// A description is following a tab character.
|
|
||||||
var validArgs []string
|
|
||||||
for _, v := range cmd.ValidArgs {
|
|
||||||
validArgs = append(validArgs, strings.Split(v, "\t")[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range args {
|
|
||||||
if !stringInSlice(v, validArgs) {
|
|
||||||
return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArbitraryArgs never returns an error.
|
// ArbitraryArgs never returns an error.
|
||||||
func ArbitraryArgs(cmd *Command, args []string) error {
|
func ArbitraryArgs(cmd *Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
|
@ -86,18 +86,6 @@ func ExactArgs(n int) PositionalArgs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExactValidArgs returns an error if
|
|
||||||
// there are not exactly N positional args OR
|
|
||||||
// there are any positional args that are not in the `ValidArgs` field of `Command`
|
|
||||||
func ExactValidArgs(n int) PositionalArgs {
|
|
||||||
return func(cmd *Command, args []string) error {
|
|
||||||
if err := ExactArgs(n)(cmd, args); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return OnlyValidArgs(cmd, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RangeArgs returns an error if the number of args is not within the expected range.
|
// RangeArgs returns an error if the number of args is not within the expected range.
|
||||||
func RangeArgs(min int, max int) PositionalArgs {
|
func RangeArgs(min int, max int) PositionalArgs {
|
||||||
return func(cmd *Command, args []string) error {
|
return func(cmd *Command, args []string) error {
|
||||||
|
@ -119,3 +107,18 @@ func MatchAll(pargs ...PositionalArgs) PositionalArgs {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExactValidArgs returns an error if there are not exactly N positional args OR
|
||||||
|
// there are any positional args that are not in the `ValidArgs` field of `Command`
|
||||||
|
//
|
||||||
|
// Deprecated: now `ExactArgs` honors `ValidArgs`, when defined and not empty
|
||||||
|
func ExactValidArgs(n int) PositionalArgs {
|
||||||
|
return ExactArgs(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlyValidArgs returns an error if any args are not in the list of `ValidArgs`.
|
||||||
|
//
|
||||||
|
// Deprecated: now `ArbitraryArgs` honors `ValidArgs`, when defined and not empty
|
||||||
|
func OnlyValidArgs(cmd *Command, args []string) error {
|
||||||
|
return ArbitraryArgs(cmd, args)
|
||||||
|
}
|
||||||
|
|
227
args_test.go
227
args_test.go
|
@ -31,6 +31,7 @@ func validWithInvalidArgs(err error, t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected an error")
|
t.Fatal("Expected an error")
|
||||||
}
|
}
|
||||||
|
|
||||||
got := err.Error()
|
got := err.Error()
|
||||||
expected := `invalid argument "a" for "c"`
|
expected := `invalid argument "a" for "c"`
|
||||||
if got != expected {
|
if got != expected {
|
||||||
|
@ -43,7 +44,7 @@ func noArgsWithArgs(err error, t *testing.T) {
|
||||||
t.Fatal("Expected an error")
|
t.Fatal("Expected an error")
|
||||||
}
|
}
|
||||||
got := err.Error()
|
got := err.Error()
|
||||||
expected := `unknown command "illegal" for "c"`
|
expected := `unknown command "one" for "c"`
|
||||||
if got != expected {
|
if got != expected {
|
||||||
t.Errorf("Expected: %q, got: %q", expected, got)
|
t.Errorf("Expected: %q, got: %q", expected, got)
|
||||||
}
|
}
|
||||||
|
@ -64,6 +65,7 @@ func maximumNArgsWithMoreArgs(err error, t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected an error")
|
t.Fatal("Expected an error")
|
||||||
}
|
}
|
||||||
|
|
||||||
got := err.Error()
|
got := err.Error()
|
||||||
expected := "accepts at most 2 arg(s), received 3"
|
expected := "accepts at most 2 arg(s), received 3"
|
||||||
if got != expected {
|
if got != expected {
|
||||||
|
@ -93,6 +95,8 @@ func rangeArgsWithInvalidCount(err error, t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NoArgs
|
||||||
|
|
||||||
func TestNoArgs(t *testing.T) {
|
func TestNoArgs(t *testing.T) {
|
||||||
c := getCommand(NoArgs, false)
|
c := getCommand(NoArgs, false)
|
||||||
output, err := executeCommand(c)
|
output, err := executeCommand(c)
|
||||||
|
@ -101,21 +105,17 @@ func TestNoArgs(t *testing.T) {
|
||||||
|
|
||||||
func TestNoArgsWithArgs(t *testing.T) {
|
func TestNoArgsWithArgs(t *testing.T) {
|
||||||
c := getCommand(NoArgs, false)
|
c := getCommand(NoArgs, false)
|
||||||
_, err := executeCommand(c, "illegal")
|
_, err := executeCommand(c, "one")
|
||||||
noArgsWithArgs(err, t)
|
noArgsWithArgs(err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOnlyValidArgs(t *testing.T) {
|
func TestNoArgsWithArgsWithValid(t *testing.T) {
|
||||||
c := getCommand(OnlyValidArgs, true)
|
c := getCommand(NoArgs, true)
|
||||||
output, err := executeCommand(c, "one", "two")
|
_, err := executeCommand(c, "one")
|
||||||
expectSuccess(output, err, t)
|
noArgsWithArgs(err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOnlyValidArgsWithInvalidArgs(t *testing.T) {
|
// ArbitraryArgs
|
||||||
c := getCommand(OnlyValidArgs, true)
|
|
||||||
_, err := executeCommand(c, "a")
|
|
||||||
validWithInvalidArgs(err, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArbitraryArgs(t *testing.T) {
|
func TestArbitraryArgs(t *testing.T) {
|
||||||
c := getCommand(ArbitraryArgs, false)
|
c := getCommand(ArbitraryArgs, false)
|
||||||
|
@ -123,72 +123,172 @@ func TestArbitraryArgs(t *testing.T) {
|
||||||
expectSuccess(output, err, t)
|
expectSuccess(output, err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestArbitraryArgsWithValid(t *testing.T) {
|
||||||
|
c := getCommand(ArbitraryArgs, true)
|
||||||
|
output, err := executeCommand(c, "one", "two")
|
||||||
|
expectSuccess(output, err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArbitraryArgsWithValidWithInvalidArgs(t *testing.T) {
|
||||||
|
c := getCommand(ArbitraryArgs, true)
|
||||||
|
_, err := executeCommand(c, "a")
|
||||||
|
validWithInvalidArgs(err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinimumNArgs
|
||||||
|
|
||||||
func TestMinimumNArgs(t *testing.T) {
|
func TestMinimumNArgs(t *testing.T) {
|
||||||
c := getCommand(MinimumNArgs(2), false)
|
c := getCommand(MinimumNArgs(2), false)
|
||||||
output, err := executeCommand(c, "a", "b", "c")
|
output, err := executeCommand(c, "a", "b", "c")
|
||||||
expectSuccess(output, err, t)
|
expectSuccess(output, err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMinimumNArgsWithValid(t *testing.T) {
|
||||||
|
c := getCommand(MinimumNArgs(2), true)
|
||||||
|
output, err := executeCommand(c, "one", "three")
|
||||||
|
expectSuccess(output, err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMinimumNArgsWithValidWithInvalidArgs(t *testing.T) {
|
||||||
|
c := getCommand(MinimumNArgs(2), true)
|
||||||
|
_, err := executeCommand(c, "a", "b")
|
||||||
|
validWithInvalidArgs(err, t)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMinimumNArgsWithLessArgs(t *testing.T) {
|
func TestMinimumNArgsWithLessArgs(t *testing.T) {
|
||||||
c := getCommand(MinimumNArgs(2), false)
|
c := getCommand(MinimumNArgs(2), false)
|
||||||
_, err := executeCommand(c, "a")
|
_, err := executeCommand(c, "a")
|
||||||
minimumNArgsWithLessArgs(err, t)
|
minimumNArgsWithLessArgs(err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMinimumNArgsWithLessArgsWithValid(t *testing.T) {
|
||||||
|
c := getCommand(MinimumNArgs(2), true)
|
||||||
|
_, err := executeCommand(c, "one")
|
||||||
|
minimumNArgsWithLessArgs(err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMinimumNArgsWithLessArgsWithValidWithInvalidArgs(t *testing.T) {
|
||||||
|
c := getCommand(MinimumNArgs(2), true)
|
||||||
|
_, err := executeCommand(c, "a")
|
||||||
|
validWithInvalidArgs(err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaximumNArgs
|
||||||
|
|
||||||
func TestMaximumNArgs(t *testing.T) {
|
func TestMaximumNArgs(t *testing.T) {
|
||||||
c := getCommand(MaximumNArgs(3), false)
|
c := getCommand(MaximumNArgs(3), false)
|
||||||
output, err := executeCommand(c, "a", "b")
|
output, err := executeCommand(c, "a", "b")
|
||||||
expectSuccess(output, err, t)
|
expectSuccess(output, err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMaximumNArgsWithValid(t *testing.T) {
|
||||||
|
c := getCommand(MaximumNArgs(2), true)
|
||||||
|
output, err := executeCommand(c, "one", "three")
|
||||||
|
expectSuccess(output, err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMaximumNArgsWithValidWithInvalidArgs(t *testing.T) {
|
||||||
|
c := getCommand(MaximumNArgs(2), true)
|
||||||
|
_, err := executeCommand(c, "a", "b")
|
||||||
|
validWithInvalidArgs(err, t)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMaximumNArgsWithMoreArgs(t *testing.T) {
|
func TestMaximumNArgsWithMoreArgs(t *testing.T) {
|
||||||
c := getCommand(MaximumNArgs(2), false)
|
c := getCommand(MaximumNArgs(2), false)
|
||||||
_, err := executeCommand(c, "a", "b", "c")
|
_, err := executeCommand(c, "a", "b", "c")
|
||||||
maximumNArgsWithMoreArgs(err, t)
|
maximumNArgsWithMoreArgs(err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMaximumNArgsWithMoreArgsWithValid(t *testing.T) {
|
||||||
|
c := getCommand(MaximumNArgs(2), true)
|
||||||
|
_, err := executeCommand(c, "one", "three", "two")
|
||||||
|
maximumNArgsWithMoreArgs(err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMaximumNArgsWithMoreArgsWithValidWithInvalidArgs(t *testing.T) {
|
||||||
|
c := getCommand(MaximumNArgs(2), true)
|
||||||
|
_, err := executeCommand(c, "a", "b", "c")
|
||||||
|
validWithInvalidArgs(err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExactArgs
|
||||||
|
|
||||||
func TestExactArgs(t *testing.T) {
|
func TestExactArgs(t *testing.T) {
|
||||||
c := getCommand(ExactArgs(3), false)
|
c := getCommand(ExactArgs(3), false)
|
||||||
output, err := executeCommand(c, "a", "b", "c")
|
output, err := executeCommand(c, "a", "b", "c")
|
||||||
expectSuccess(output, err, t)
|
expectSuccess(output, err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExactArgsWithValid(t *testing.T) {
|
||||||
|
c := getCommand(ExactArgs(3), true)
|
||||||
|
output, err := executeCommand(c, "three", "one", "two")
|
||||||
|
expectSuccess(output, err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExactArgsWithValidWithInvalidArgs(t *testing.T) {
|
||||||
|
c := getCommand(ExactArgs(3), true)
|
||||||
|
_, err := executeCommand(c, "three", "a", "two")
|
||||||
|
validWithInvalidArgs(err, t)
|
||||||
|
}
|
||||||
|
|
||||||
func TestExactArgsWithInvalidCount(t *testing.T) {
|
func TestExactArgsWithInvalidCount(t *testing.T) {
|
||||||
c := getCommand(ExactArgs(2), false)
|
c := getCommand(ExactArgs(2), false)
|
||||||
_, err := executeCommand(c, "a", "b", "c")
|
_, err := executeCommand(c, "a", "b", "c")
|
||||||
exactArgsWithInvalidCount(err, t)
|
exactArgsWithInvalidCount(err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExactValidArgs(t *testing.T) {
|
func TestExactArgsWithInvalidCountWithValid(t *testing.T) {
|
||||||
c := getCommand(ExactValidArgs(3), true)
|
c := getCommand(ExactArgs(2), true)
|
||||||
output, err := executeCommand(c, "three", "one", "two")
|
|
||||||
expectSuccess(output, err, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExactValidArgsWithInvalidCount(t *testing.T) {
|
|
||||||
c := getCommand(ExactValidArgs(2), false)
|
|
||||||
_, err := executeCommand(c, "three", "one", "two")
|
_, err := executeCommand(c, "three", "one", "two")
|
||||||
exactArgsWithInvalidCount(err, t)
|
exactArgsWithInvalidCount(err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExactValidArgsWithInvalidArgs(t *testing.T) {
|
func TestExactArgsWithInvalidCountWithValidWithInvalidArgs(t *testing.T) {
|
||||||
c := getCommand(ExactValidArgs(3), true)
|
c := getCommand(ExactArgs(2), true)
|
||||||
_, err := executeCommand(c, "three", "a", "two")
|
_, err := executeCommand(c, "three", "a", "two")
|
||||||
validWithInvalidArgs(err, t)
|
validWithInvalidArgs(err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RangeArgs
|
||||||
|
|
||||||
func TestRangeArgs(t *testing.T) {
|
func TestRangeArgs(t *testing.T) {
|
||||||
c := getCommand(RangeArgs(2, 4), false)
|
c := getCommand(RangeArgs(2, 4), false)
|
||||||
output, err := executeCommand(c, "a", "b", "c")
|
output, err := executeCommand(c, "a", "b", "c")
|
||||||
expectSuccess(output, err, t)
|
expectSuccess(output, err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRangeArgsWithValid(t *testing.T) {
|
||||||
|
c := getCommand(RangeArgs(2, 4), true)
|
||||||
|
output, err := executeCommand(c, "three", "one", "two")
|
||||||
|
expectSuccess(output, err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRangeArgsWithValidWithInvalidArgs(t *testing.T) {
|
||||||
|
c := getCommand(RangeArgs(2, 4), true)
|
||||||
|
_, err := executeCommand(c, "three", "a", "two")
|
||||||
|
validWithInvalidArgs(err, t)
|
||||||
|
}
|
||||||
|
|
||||||
func TestRangeArgsWithInvalidCount(t *testing.T) {
|
func TestRangeArgsWithInvalidCount(t *testing.T) {
|
||||||
c := getCommand(RangeArgs(2, 4), false)
|
c := getCommand(RangeArgs(2, 4), false)
|
||||||
_, err := executeCommand(c, "a")
|
_, err := executeCommand(c, "a")
|
||||||
rangeArgsWithInvalidCount(err, t)
|
rangeArgsWithInvalidCount(err, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRangeArgsWithInvalidCountWithValid(t *testing.T) {
|
||||||
|
c := getCommand(RangeArgs(2, 4), true)
|
||||||
|
_, err := executeCommand(c, "two")
|
||||||
|
rangeArgsWithInvalidCount(err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRangeArgsWithInvalidCountWithValidWithInvalidArgs(t *testing.T) {
|
||||||
|
c := getCommand(RangeArgs(2, 4), true)
|
||||||
|
_, err := executeCommand(c, "a")
|
||||||
|
validWithInvalidArgs(err, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes(No)Args
|
||||||
|
|
||||||
func TestRootTakesNoArgs(t *testing.T) {
|
func TestRootTakesNoArgs(t *testing.T) {
|
||||||
rootCmd := &Command{Use: "root", Run: emptyRun}
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
||||||
childCmd := &Command{Use: "child", Run: emptyRun}
|
childCmd := &Command{Use: "child", Run: emptyRun}
|
||||||
|
@ -293,6 +393,91 @@ func TestMatchAll(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEPRECATED
|
||||||
|
|
||||||
|
func TestOnlyValidArgs(t *testing.T) {
|
||||||
|
c := &Command{
|
||||||
|
Use: "c",
|
||||||
|
Args: OnlyValidArgs,
|
||||||
|
ValidArgs: []string{"one", "two"},
|
||||||
|
Run: emptyRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := executeCommand(c, "one", "two")
|
||||||
|
if output != "" {
|
||||||
|
t.Errorf("Unexpected output: %v", output)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOnlyValidArgsWithInvalidArgs(t *testing.T) {
|
||||||
|
c := &Command{
|
||||||
|
Use: "c",
|
||||||
|
Args: OnlyValidArgs,
|
||||||
|
ValidArgs: []string{"one", "two"},
|
||||||
|
Run: emptyRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := executeCommand(c, "three")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
got := err.Error()
|
||||||
|
expected := `invalid argument "three" for "c"`
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("Expected: %q, got: %q", expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExactValidArgs(t *testing.T) {
|
||||||
|
c := &Command{Use: "c", Args: ExactValidArgs(3), ValidArgs: []string{"a", "b", "c"}, Run: emptyRun}
|
||||||
|
output, err := executeCommand(c, "a", "b", "c")
|
||||||
|
if output != "" {
|
||||||
|
t.Errorf("Unexpected output: %v", output)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExactValidArgsWithInvalidCount(t *testing.T) {
|
||||||
|
c := &Command{Use: "c", Args: ExactValidArgs(2), Run: emptyRun}
|
||||||
|
_, err := executeCommand(c, "a", "b", "c")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
got := err.Error()
|
||||||
|
expected := "accepts 2 arg(s), received 3"
|
||||||
|
if got != expected {
|
||||||
|
t.Fatalf("Expected %q, got %q", expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExactValidArgsWithInvalidArgs(t *testing.T) {
|
||||||
|
c := &Command{
|
||||||
|
Use: "c",
|
||||||
|
Args: ExactValidArgs(1),
|
||||||
|
ValidArgs: []string{"one", "two"},
|
||||||
|
Run: emptyRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := executeCommand(c, "three")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
got := err.Error()
|
||||||
|
expected := `invalid argument "three" for "c"`
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("Expected: %q, got: %q", expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This test make sure we keep backwards-compatibility with respect
|
// This test make sure we keep backwards-compatibility with respect
|
||||||
// to the legacyArgs() function.
|
// to the legacyArgs() function.
|
||||||
// It makes sure the root command accepts arguments if it does not have
|
// It makes sure the root command accepts arguments if it does not have
|
||||||
|
|
|
@ -139,7 +139,7 @@ func TestBashCompletions(t *testing.T) {
|
||||||
timesCmd := &Command{
|
timesCmd := &Command{
|
||||||
Use: "times [# times] [string to echo]",
|
Use: "times [# times] [string to echo]",
|
||||||
SuggestFor: []string{"counts"},
|
SuggestFor: []string{"counts"},
|
||||||
Args: OnlyValidArgs,
|
Args: ArbitraryArgs,
|
||||||
ValidArgs: []string{"one", "two", "three", "four"},
|
ValidArgs: []string{"one", "two", "three", "four"},
|
||||||
Short: "Echo anything to the screen more times",
|
Short: "Echo anything to the screen more times",
|
||||||
Long: "a slightly useless command for testing.",
|
Long: "a slightly useless command for testing.",
|
||||||
|
|
|
@ -1011,10 +1011,15 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||||
return cmd, err
|
return cmd, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateArgs returns an error if any positional args are not in
|
||||||
|
// the `ValidArgs` field of `Command`
|
||||||
func (c *Command) ValidateArgs(args []string) error {
|
func (c *Command) ValidateArgs(args []string) error {
|
||||||
if c.Args == nil {
|
if c.Args == nil {
|
||||||
return ArbitraryArgs(c, args)
|
return ArbitraryArgs(c, args)
|
||||||
}
|
}
|
||||||
|
if err := validateArgs(c, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return c.Args(c, args)
|
return c.Args(c, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -302,15 +302,15 @@ rootCmd.MarkPersistentFlagRequired("region")
|
||||||
|
|
||||||
### Flag Groups
|
### Flag Groups
|
||||||
|
|
||||||
If you have different flags that must be provided together (e.g. if they provide the `--username` flag they MUST provide the `--password` flag as well) then
|
If you have different flags that must be provided together (e.g. if they provide the `--username` flag they MUST provide the `--password` flag as well) then
|
||||||
Cobra can enforce that requirement:
|
Cobra can enforce that requirement:
|
||||||
```go
|
```go
|
||||||
rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)")
|
rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)")
|
||||||
rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)")
|
rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)")
|
||||||
rootCmd.MarkFlagsRequiredTogether("username", "password")
|
rootCmd.MarkFlagsRequiredTogether("username", "password")
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also prevent different flags from being provided together if they represent mutually
|
You can also prevent different flags from being provided together if they represent mutually
|
||||||
exclusive options such as specifying an output format as either `--json` or `--yaml` but never both:
|
exclusive options such as specifying an output format as either `--json` or `--yaml` but never both:
|
||||||
```go
|
```go
|
||||||
rootCmd.Flags().BoolVar(&u, "json", false, "Output in JSON")
|
rootCmd.Flags().BoolVar(&u, "json", false, "Output in JSON")
|
||||||
|
@ -327,29 +327,37 @@ In both of these cases:
|
||||||
## Positional and Custom Arguments
|
## Positional and Custom Arguments
|
||||||
|
|
||||||
Validation of positional arguments can be specified using the `Args` field of `Command`.
|
Validation of positional arguments can be specified using the `Args` field of `Command`.
|
||||||
If `Args` is undefined or `nil`, it defaults to `ArbitraryArgs`.
|
|
||||||
|
|
||||||
The following validators are built in:
|
The following validators are built in:
|
||||||
|
|
||||||
- `NoArgs` - the command will report an error if there are any positional args.
|
- `NoArgs` - report an error if there are any positional args.
|
||||||
- `ArbitraryArgs` - the command will accept any args.
|
- `ArbitraryArgs` - accept any number of args.
|
||||||
- `OnlyValidArgs` - the command will report an error if there are any positional args that are not in the `ValidArgs` field of `Command`.
|
- `MinimumNArgs(int)` - report an error if less than N positional args are provided.
|
||||||
- `MinimumNArgs(int)` - the command will report an error if there are not at least N positional args.
|
- `MaximumNArgs(int)` - report an error if more than N positional args are provided.
|
||||||
- `MaximumNArgs(int)` - the command will report an error if there are more than N positional args.
|
- `ExactArgs(int)` - report an error if there are not exactly N positional args.
|
||||||
- `ExactArgs(int)` - the command will report an error if there are not exactly N positional args.
|
- `RangeArgs(min, max)` - report an error if the number of args is not between `min` and `max`.
|
||||||
- `ExactValidArgs(int)` - the command will report an error if there are not exactly N positional args OR if there are any positional args that are not in the `ValidArgs` field of `Command`
|
|
||||||
- `RangeArgs(min, max)` - the command will report an error if the number of args is not between the minimum and maximum number of expected args.
|
|
||||||
- `MatchAll(pargs ...PositionalArgs)` - enables combining existing checks with arbitrary other checks (e.g. you want to check the ExactArgs length along with other qualities).
|
- `MatchAll(pargs ...PositionalArgs)` - enables combining existing checks with arbitrary other checks (e.g. you want to check the ExactArgs length along with other qualities).
|
||||||
|
|
||||||
An example of setting the custom validator:
|
If `Args` is undefined or `nil`, it defaults to `ArbitraryArgs`.
|
||||||
|
|
||||||
|
Field `ValidArgs` of type `[]string` can be defined in `Command`, in order to report an error if there are any
|
||||||
|
positional args that are not in the list.
|
||||||
|
This validation is executed implicitly before the validator defined in `Args`.
|
||||||
|
|
||||||
|
> NOTE: `OnlyValidArgs` and `ExactValidArgs(int)` are now deprecated.
|
||||||
|
> `ArbitraryArgs` and `ExactArgs(int)` provide the same functionality now.
|
||||||
|
|
||||||
|
Moreover, it is possible to set any custom validator that satisfies `func(cmd *cobra.Command, args []string) error`.
|
||||||
|
For example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
var cmd = &cobra.Command{
|
var cmd = &cobra.Command{
|
||||||
Short: "hello",
|
Short: "hello",
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) < 1 {
|
// Optionally run one of the validators provided by cobra
|
||||||
return errors.New("requires a color argument")
|
if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
// Run the custom validation logic
|
||||||
if myapp.IsValidColor(args[0]) {
|
if myapp.IsValidColor(args[0]) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue