Skip to content

Commit

Permalink
feat: generalize ValidArgs; use it implicitly with any validator
Browse files Browse the repository at this point in the history
  • Loading branch information
umarcor committed Mar 19, 2019
1 parent 9657d44 commit 5576bd3
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 48 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,13 +388,15 @@ The following validators are built in:

- `NoArgs` - the command will report an error if there are any positional args.
- `ArbitraryArgs` - the command will accept any 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)` - 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 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.

If field `ValidArgs` of type `[]string` is defined in `Command`, apart from checking the number of positional arguments according to the validators above, the command will report an error if there are any positional args that are not in the list.

> NOTE: `OnlyValidArgs` and `ExactValidArgs(int)` are now deprecated. Instead, use `ArbitraryArgs` and `ExactArgs(int)`.
An example of setting the custom validator:

```go
Expand Down
24 changes: 0 additions & 24 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,6 @@ func NoArgs(cmd *Command, args []string) error {
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 {
for _, v := range args {
if !stringInSlice(v, cmd.ValidArgs) {
return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
}
}
}
return nil
}

// ArbitraryArgs never returns an error.
func ArbitraryArgs(cmd *Command, args []string) error {
return nil
Expand Down Expand Up @@ -78,18 +66,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.
func RangeArgs(min int, max int) PositionalArgs {
return func(cmd *Command, args []string) error {
Expand Down
142 changes: 121 additions & 21 deletions args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func validWithInvalidArgs(err error, t *testing.T) {
if err == nil {
t.Fatal("Expected an error")
}

got := err.Error()
expected := `invalid argument "a" for "c"`
if got != expected {
Expand All @@ -42,7 +43,7 @@ func noArgsWithArgs(err error, t *testing.T) {
t.Fatal("Expected an error")
}
got := err.Error()
expected := `unknown command "illegal" for "c"`
expected := `unknown command "one" for "c"`
if got != expected {
t.Errorf("Expected: %q, got: %q", expected, got)
}
Expand All @@ -63,6 +64,7 @@ func maximumNArgsWithMoreArgs(err error, t *testing.T) {
if err == nil {
t.Fatal("Expected an error")
}

got := err.Error()
expected := "accepts at most 2 arg(s), received 3"
if got != expected {
Expand Down Expand Up @@ -92,6 +94,8 @@ func rangeArgsWithInvalidCount(err error, t *testing.T) {
}
}

// NoArgs

func TestNoArgs(t *testing.T) {
c := getCommand(NoArgs, false)
output, err := executeCommand(c)
Expand All @@ -100,94 +104,190 @@ func TestNoArgs(t *testing.T) {

func TestNoArgsWithArgs(t *testing.T) {
c := getCommand(NoArgs, false)
_, err := executeCommand(c, "illegal")
_, err := executeCommand(c, "one")
noArgsWithArgs(err, t)
}

func TestOnlyValidArgs(t *testing.T) {
c := getCommand(OnlyValidArgs, true)
output, err := executeCommand(c, "one", "two")
expectSuccess(output, err, t)
func TestNoArgsWithArgsWithValid(t *testing.T) {
c := getCommand(NoArgs, true)
_, err := executeCommand(c, "one")
noArgsWithArgs(err, t)
}

func TestOnlyValidArgsWithInvalidArgs(t *testing.T) {
c := getCommand(OnlyValidArgs, true)
_, err := executeCommand(c, "a")
validWithInvalidArgs(err, t)
}
// ArbitraryArgs

func TestArbitraryArgs(t *testing.T) {
c := getCommand(ArbitraryArgs, false)
output, err := executeCommand(c, "a", "b")
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) {
c := getCommand(MinimumNArgs(2), false)
output, err := executeCommand(c, "a", "b", "c")
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) {
c := getCommand(MinimumNArgs(2), false)
_, err := executeCommand(c, "a")
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) {
c := getCommand(MaximumNArgs(3), false)
output, err := executeCommand(c, "a", "b")
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) {
c := getCommand(MaximumNArgs(2), false)
_, err := executeCommand(c, "a", "b", "c")
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) {
c := getCommand(ExactArgs(3), false)
output, err := executeCommand(c, "a", "b", "c")
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) {
c := getCommand(ExactArgs(2), false)
_, err := executeCommand(c, "a", "b", "c")
exactArgsWithInvalidCount(err, t)
}

func TestExactValidArgs(t *testing.T) {
c := getCommand(ExactValidArgs(3), true)
output, err := executeCommand(c, "three", "one", "two")
expectSuccess(output, err, t)
}

func TestExactValidArgsWithInvalidCount(t *testing.T) {
c := getCommand(ExactValidArgs(2), false)
func TestExactArgsWithInvalidCountWithValid(t *testing.T) {
c := getCommand(ExactArgs(2), true)
_, err := executeCommand(c, "three", "one", "two")
exactArgsWithInvalidCount(err, t)
}

func TestExactValidArgsWithInvalidArgs(t *testing.T) {
c := getCommand(ExactValidArgs(3), true)
func TestExactArgsWithInvalidCountWithValidWithInvalidArgs(t *testing.T) {
c := getCommand(ExactArgs(2), true)
_, err := executeCommand(c, "three", "a", "two")
validWithInvalidArgs(err, t)
}

// RangeArgs

func TestRangeArgs(t *testing.T) {
c := getCommand(RangeArgs(2, 4), false)
output, err := executeCommand(c, "a", "b", "c")
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) {
c := getCommand(RangeArgs(2, 4), false)
_, err := executeCommand(c, "a")
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) {
rootCmd := &Command{Use: "root", Run: emptyRun}
childCmd := &Command{Use: "child", Run: emptyRun}
Expand Down
2 changes: 1 addition & 1 deletion bash_completions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func TestBashCompletions(t *testing.T) {
timesCmd := &Command{
Use: "times [# times] [string to echo]",
SuggestFor: []string{"counts"},
Args: OnlyValidArgs,
Args: ArbitraryArgs,
ValidArgs: []string{"one", "two", "three", "four"},
Short: "Echo anything to the screen more times",
Long: "a slightly useless command for testing.",
Expand Down
7 changes: 7 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,13 @@ func (c *Command) ValidateArgs(args []string) error {
if c.Args == nil {
return nil
}
if len(c.ValidArgs) > 0 {
for _, v := range args {
if !stringInSlice(v, c.ValidArgs) {
return fmt.Errorf("invalid argument %q for %q%s", v, c.CommandPath(), c.findSuggestions(args[0]))
}
}
}
return c.Args(c, args)
}

Expand Down

0 comments on commit 5576bd3

Please sign in to comment.