Repository: spf13/cobra
Pull request #765 (2018-10-16 01:58:14)

Added ExactValidArgs
Added a convenience method that combines ValidArgs() and ExactArgs(int)

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/spf13/cobra?pullRequest=765) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/spf13/cobra?pullRequest=765) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/spf13/cobra?pullRequest=765) it.</sub>

Patch:
diff --git a/README.md b/README.md
index ce96133..5e73ffe 100644
--- a/README.md
+++ b/README.md
@@ -262,9 +262,6 @@ In a Cobra app, typically the main.go file is very bare. It serves, one purpose,
 package main
 
 import (
-  "fmt"
-  "os"
-
   "{pathToYourApp}/cmd"
 )
 
@@ -392,6 +389,7 @@ The following validators are built in:
 - `MinimumNArgs(int)` - the command will report an error if there are not at least N positional args.
 - `MaximumNArgs(int)` - the command will report an error if there are more than N positional args.
 - `ExactArgs(int)` - the command will report an error if there are not exactly N positional args.
+- `ExactValidArgs(int)` = the command will report and 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.
 
 An example of setting the custom validator:
diff --git a/args.go b/args.go
index a5d8a92..c4d820b 100644
--- a/args.go
+++ b/args.go
@@ -78,6 +78,18 @@ 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.
 func RangeArgs(min int, max int) PositionalArgs {
 	return func(cmd *Command, args []string) error {


Test patch:
diff --git a/args_test.go b/args_test.go
index d797b6f..c81b212 100644
--- a/args_test.go
+++ b/args_test.go
@@ -158,6 +158,52 @@ func TestExactArgsWithInvalidCount(t *testing.T) {
 	}
 }
 
+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)
+	}
+}
+
 func TestRangeArgs(t *testing.T) {
 	c := &Command{Use: "c", Args: RangeArgs(2, 4), Run: emptyRun}
 	output, err := executeCommand(c, "a", "b", "c")