Skip to content

Commit ad460ea

Browse files
authored
Add cobra unique args validator (#2397)
1 parent 746ef07 commit ad460ea

3 files changed

Lines changed: 51 additions & 0 deletions

File tree

args.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ func OnlyValidArgs(cmd *Command, args []string) error {
6565
return nil
6666
}
6767

68+
// NoDuplicateArgs returns an error if there are any duplicate positional args.
69+
func NoDuplicateArgs(cmd *Command, args []string) error {
70+
seen := make(map[string]struct{}, len(args))
71+
for _, arg := range args {
72+
if _, ok := seen[arg]; ok {
73+
return fmt.Errorf("duplicate argument %q for %q", arg, cmd.CommandPath())
74+
}
75+
seen[arg] = struct{}{}
76+
}
77+
78+
return nil
79+
}
80+
6881
// ArbitraryArgs never returns an error.
6982
func ArbitraryArgs(cmd *Command, args []string) error {
7083
return nil

args_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,17 @@ func rangeArgsWithInvalidCount(err error, t *testing.T) {
107107
}
108108
}
109109

110+
func noDuplicateArgsWithDuplicate(err error, t *testing.T, arg string) {
111+
if err == nil {
112+
t.Fatal("Expected an error")
113+
}
114+
got := err.Error()
115+
expected := `duplicate argument "` + arg + `" for "c"`
116+
if got != expected {
117+
t.Errorf("Expected: %q, got: %q", expected, got)
118+
}
119+
}
120+
110121
// NoArgs
111122

112123
func TestNoArgs(t *testing.T) {
@@ -153,6 +164,32 @@ func TestOnlyValidArgs_WithInvalidArgs(t *testing.T) {
153164
validOnlyWithInvalidArgs(err, t)
154165
}
155166

167+
// NoDuplicateArgs
168+
169+
func TestNoDuplicateArgs(t *testing.T) {
170+
c := getCommand(NoDuplicateArgs, false)
171+
output, err := executeCommand(c, "one", "two")
172+
expectSuccess(output, err, t)
173+
}
174+
175+
func TestNoDuplicateArgs_WithDuplicateArgs(t *testing.T) {
176+
c := getCommand(NoDuplicateArgs, false)
177+
_, err := executeCommand(c, "one", "one")
178+
noDuplicateArgsWithDuplicate(err, t, "one")
179+
}
180+
181+
func TestNoDuplicateArgs_WithValid_WithDuplicateArgs(t *testing.T) {
182+
c := getCommand(NoDuplicateArgs, true)
183+
_, err := executeCommand(c, "one", "one")
184+
noDuplicateArgsWithDuplicate(err, t, "one")
185+
}
186+
187+
func TestNoDuplicateArgs_WithValidOnly_WithInvalidArgs(t *testing.T) {
188+
c := getCommand(MatchAll(OnlyValidArgs, NoDuplicateArgs), true)
189+
_, err := executeCommand(c, "a", "a")
190+
validOnlyWithInvalidArgs(err, t)
191+
}
192+
156193
// ArbitraryArgs
157194

158195
func TestArbitraryArgs(t *testing.T) {

site/content/user_guide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ The following validators are built in:
447447
- `RangeArgs(min, max)` - report an error if the number of args is not between `min` and `max`.
448448
- Content of the arguments:
449449
- `OnlyValidArgs` - report an error if there are any positional args not specified in the `ValidArgs` field of `Command`, which can optionally be set to a list of valid values for positional args.
450+
- `NoDuplicateArgs` - report an error if any positional argument value is provided more than once.
450451

451452
If `Args` is undefined or `nil`, it defaults to `ArbitraryArgs`.
452453

0 commit comments

Comments
 (0)