From 6e1736fb52c54fb82e04d1c9b23e575d652707d6 Mon Sep 17 00:00:00 2001 From: Nelz Date: Fri, 28 Jun 2019 14:47:35 -0700 Subject: [PATCH] add MatchAll: enable composing PositionalArgs (#896) --- README.md | 1 + args.go | 12 ++++++++++++ args_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/README.md b/README.md index e6a1205cb3..466bda3914 100644 --- a/README.md +++ b/README.md @@ -428,6 +428,7 @@ of `Command`. The following validators are built in: - `MaximumNArgs(int)` - report an error if more than N positional args are provided. - `ExactArgs(int)` - 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`. +- `MatchAll(pargs ...PositionalArgs)` - enables combining existing checks with arbitrary other checks (e.g. you want to check the ExactArgs length along with other qualities). If `Args` is undefined or `nil`, it defaults to `ArbitraryArgs`. diff --git a/args.go b/args.go index 1cf45a0a07..aad963bd10 100644 --- a/args.go +++ b/args.go @@ -96,6 +96,18 @@ func RangeArgs(min int, max int) PositionalArgs { } } +// MatchAll allows combining several PositionalArgs to work in concert. +func MatchAll(pargs ...PositionalArgs) PositionalArgs { + return func(cmd *Command, args []string) error { + for _, parg := range pargs { + if err := parg(cmd, args); err != nil { + return err + } + } + 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` // diff --git a/args_test.go b/args_test.go index c4bbdeb9ec..d97cf18cdc 100644 --- a/args_test.go +++ b/args_test.go @@ -1,6 +1,7 @@ package cobra import ( + "fmt" "strings" "testing" ) @@ -191,3 +192,51 @@ func TestChildTakesArgs(t *testing.T) { t.Fatalf("Unexpected error: %v", err) } } + +func TestMatchAll(t *testing.T) { + // Somewhat contrived example check that ensures there are exactly 3 + // arguments, and each argument is exactly 2 bytes long. + pargs := MatchAll( + ExactArgs(3), + func(cmd *Command, args []string) error { + for _, arg := range args { + if len([]byte(arg)) != 2 { + return fmt.Errorf("expected to be exactly 2 bytes long") + } + } + return nil + }, + ) + + testCases := map[string]struct { + args []string + fail bool + }{ + "happy path": { + []string{"aa", "bb", "cc"}, + false, + }, + "incorrect number of args": { + []string{"aa", "bb", "cc", "dd"}, + true, + }, + "incorrect number of bytes in one arg": { + []string{"aa", "bb", "abc"}, + true, + }, + } + + rootCmd := &Command{Use: "root", Args: pargs, Run: emptyRun} + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + _, err := executeCommand(rootCmd, tc.args...) + if err != nil && !tc.fail { + t.Errorf("unexpected: %v\n", err) + } + if err == nil && tc.fail { + t.Errorf("expected error") + } + }) + } +}