Skip to content

Commit 5576bd3

Browse files
committed
feat: generalize ValidArgs; use it implicitly with any validator
1 parent 9657d44 commit 5576bd3

File tree

5 files changed

+133
-48
lines changed

5 files changed

+133
-48
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,13 +388,15 @@ The following validators are built in:
388388

389389
- `NoArgs` - the command will report an error if there are any positional args.
390390
- `ArbitraryArgs` - the command will accept any args.
391-
- `OnlyValidArgs` - the command will report an error if there are any positional args that are not in the `ValidArgs` field of `Command`.
392391
- `MinimumNArgs(int)` - the command will report an error if there are not at least N positional args.
393392
- `MaximumNArgs(int)` - the command will report an error if there are more than N positional args.
394393
- `ExactArgs(int)` - the command will report an error if there are not exactly N positional args.
395-
- `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`
396394
- `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.
397395

396+
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.
397+
398+
> NOTE: `OnlyValidArgs` and `ExactValidArgs(int)` are now deprecated. Instead, use `ArbitraryArgs` and `ExactArgs(int)`.
399+
398400
An example of setting the custom validator:
399401

400402
```go

args.go

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,6 @@ func NoArgs(cmd *Command, args []string) error {
3131
return nil
3232
}
3333

34-
// OnlyValidArgs returns an error if any args are not in the list of ValidArgs.
35-
func OnlyValidArgs(cmd *Command, args []string) error {
36-
if len(cmd.ValidArgs) > 0 {
37-
for _, v := range args {
38-
if !stringInSlice(v, cmd.ValidArgs) {
39-
return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
40-
}
41-
}
42-
}
43-
return nil
44-
}
45-
4634
// ArbitraryArgs never returns an error.
4735
func ArbitraryArgs(cmd *Command, args []string) error {
4836
return nil
@@ -78,18 +66,6 @@ func ExactArgs(n int) PositionalArgs {
7866
}
7967
}
8068

81-
// ExactValidArgs returns an error if
82-
// there are not exactly N positional args OR
83-
// there are any positional args that are not in the `ValidArgs` field of `Command`
84-
func ExactValidArgs(n int) PositionalArgs {
85-
return func(cmd *Command, args []string) error {
86-
if err := ExactArgs(n)(cmd, args); err != nil {
87-
return err
88-
}
89-
return OnlyValidArgs(cmd, args)
90-
}
91-
}
92-
9369
// RangeArgs returns an error if the number of args is not within the expected range.
9470
func RangeArgs(min int, max int) PositionalArgs {
9571
return func(cmd *Command, args []string) error {

args_test.go

Lines changed: 121 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func validWithInvalidArgs(err error, t *testing.T) {
3030
if err == nil {
3131
t.Fatal("Expected an error")
3232
}
33+
3334
got := err.Error()
3435
expected := `invalid argument "a" for "c"`
3536
if got != expected {
@@ -42,7 +43,7 @@ func noArgsWithArgs(err error, t *testing.T) {
4243
t.Fatal("Expected an error")
4344
}
4445
got := err.Error()
45-
expected := `unknown command "illegal" for "c"`
46+
expected := `unknown command "one" for "c"`
4647
if got != expected {
4748
t.Errorf("Expected: %q, got: %q", expected, got)
4849
}
@@ -63,6 +64,7 @@ func maximumNArgsWithMoreArgs(err error, t *testing.T) {
6364
if err == nil {
6465
t.Fatal("Expected an error")
6566
}
67+
6668
got := err.Error()
6769
expected := "accepts at most 2 arg(s), received 3"
6870
if got != expected {
@@ -92,6 +94,8 @@ func rangeArgsWithInvalidCount(err error, t *testing.T) {
9294
}
9395
}
9496

97+
// NoArgs
98+
9599
func TestNoArgs(t *testing.T) {
96100
c := getCommand(NoArgs, false)
97101
output, err := executeCommand(c)
@@ -100,94 +104,190 @@ func TestNoArgs(t *testing.T) {
100104

101105
func TestNoArgsWithArgs(t *testing.T) {
102106
c := getCommand(NoArgs, false)
103-
_, err := executeCommand(c, "illegal")
107+
_, err := executeCommand(c, "one")
104108
noArgsWithArgs(err, t)
105109
}
106110

107-
func TestOnlyValidArgs(t *testing.T) {
108-
c := getCommand(OnlyValidArgs, true)
109-
output, err := executeCommand(c, "one", "two")
110-
expectSuccess(output, err, t)
111+
func TestNoArgsWithArgsWithValid(t *testing.T) {
112+
c := getCommand(NoArgs, true)
113+
_, err := executeCommand(c, "one")
114+
noArgsWithArgs(err, t)
111115
}
112116

113-
func TestOnlyValidArgsWithInvalidArgs(t *testing.T) {
114-
c := getCommand(OnlyValidArgs, true)
115-
_, err := executeCommand(c, "a")
116-
validWithInvalidArgs(err, t)
117-
}
117+
// ArbitraryArgs
118118

119119
func TestArbitraryArgs(t *testing.T) {
120120
c := getCommand(ArbitraryArgs, false)
121121
output, err := executeCommand(c, "a", "b")
122122
expectSuccess(output, err, t)
123123
}
124124

125+
func TestArbitraryArgsWithValid(t *testing.T) {
126+
c := getCommand(ArbitraryArgs, true)
127+
output, err := executeCommand(c, "one", "two")
128+
expectSuccess(output, err, t)
129+
}
130+
131+
func TestArbitraryArgsWithValidWithInvalidArgs(t *testing.T) {
132+
c := getCommand(ArbitraryArgs, true)
133+
_, err := executeCommand(c, "a")
134+
validWithInvalidArgs(err, t)
135+
}
136+
137+
// MinimumNArgs
138+
125139
func TestMinimumNArgs(t *testing.T) {
126140
c := getCommand(MinimumNArgs(2), false)
127141
output, err := executeCommand(c, "a", "b", "c")
128142
expectSuccess(output, err, t)
129143
}
130144

145+
func TestMinimumNArgsWithValid(t *testing.T) {
146+
c := getCommand(MinimumNArgs(2), true)
147+
output, err := executeCommand(c, "one", "three")
148+
expectSuccess(output, err, t)
149+
}
150+
151+
func TestMinimumNArgsWithValidWithInvalidArgs(t *testing.T) {
152+
c := getCommand(MinimumNArgs(2), true)
153+
_, err := executeCommand(c, "a", "b")
154+
validWithInvalidArgs(err, t)
155+
}
156+
131157
func TestMinimumNArgsWithLessArgs(t *testing.T) {
132158
c := getCommand(MinimumNArgs(2), false)
133159
_, err := executeCommand(c, "a")
134160
minimumNArgsWithLessArgs(err, t)
135161
}
136162

163+
func TestMinimumNArgsWithLessArgsWithValid(t *testing.T) {
164+
c := getCommand(MinimumNArgs(2), true)
165+
_, err := executeCommand(c, "one")
166+
minimumNArgsWithLessArgs(err, t)
167+
}
168+
169+
func TestMinimumNArgsWithLessArgsWithValidWithInvalidArgs(t *testing.T) {
170+
c := getCommand(MinimumNArgs(2), true)
171+
_, err := executeCommand(c, "a")
172+
validWithInvalidArgs(err, t)
173+
}
174+
175+
// MaximumNArgs
176+
137177
func TestMaximumNArgs(t *testing.T) {
138178
c := getCommand(MaximumNArgs(3), false)
139179
output, err := executeCommand(c, "a", "b")
140180
expectSuccess(output, err, t)
141181
}
142182

183+
func TestMaximumNArgsWithValid(t *testing.T) {
184+
c := getCommand(MaximumNArgs(2), true)
185+
output, err := executeCommand(c, "one", "three")
186+
expectSuccess(output, err, t)
187+
}
188+
189+
func TestMaximumNArgsWithValidWithInvalidArgs(t *testing.T) {
190+
c := getCommand(MaximumNArgs(2), true)
191+
_, err := executeCommand(c, "a", "b")
192+
validWithInvalidArgs(err, t)
193+
}
194+
143195
func TestMaximumNArgsWithMoreArgs(t *testing.T) {
144196
c := getCommand(MaximumNArgs(2), false)
145197
_, err := executeCommand(c, "a", "b", "c")
146198
maximumNArgsWithMoreArgs(err, t)
147199
}
148200

201+
func TestMaximumNArgsWithMoreArgsWithValid(t *testing.T) {
202+
c := getCommand(MaximumNArgs(2), true)
203+
_, err := executeCommand(c, "one", "three", "two")
204+
maximumNArgsWithMoreArgs(err, t)
205+
}
206+
207+
func TestMaximumNArgsWithMoreArgsWithValidWithInvalidArgs(t *testing.T) {
208+
c := getCommand(MaximumNArgs(2), true)
209+
_, err := executeCommand(c, "a", "b", "c")
210+
validWithInvalidArgs(err, t)
211+
}
212+
213+
// ExactArgs
214+
149215
func TestExactArgs(t *testing.T) {
150216
c := getCommand(ExactArgs(3), false)
151217
output, err := executeCommand(c, "a", "b", "c")
152218
expectSuccess(output, err, t)
153219
}
154220

221+
func TestExactArgsWithValid(t *testing.T) {
222+
c := getCommand(ExactArgs(3), true)
223+
output, err := executeCommand(c, "three", "one", "two")
224+
expectSuccess(output, err, t)
225+
}
226+
227+
func TestExactArgsWithValidWithInvalidArgs(t *testing.T) {
228+
c := getCommand(ExactArgs(3), true)
229+
_, err := executeCommand(c, "three", "a", "two")
230+
validWithInvalidArgs(err, t)
231+
}
232+
155233
func TestExactArgsWithInvalidCount(t *testing.T) {
156234
c := getCommand(ExactArgs(2), false)
157235
_, err := executeCommand(c, "a", "b", "c")
158236
exactArgsWithInvalidCount(err, t)
159237
}
160238

161-
func TestExactValidArgs(t *testing.T) {
162-
c := getCommand(ExactValidArgs(3), true)
163-
output, err := executeCommand(c, "three", "one", "two")
164-
expectSuccess(output, err, t)
165-
}
166-
167-
func TestExactValidArgsWithInvalidCount(t *testing.T) {
168-
c := getCommand(ExactValidArgs(2), false)
239+
func TestExactArgsWithInvalidCountWithValid(t *testing.T) {
240+
c := getCommand(ExactArgs(2), true)
169241
_, err := executeCommand(c, "three", "one", "two")
170242
exactArgsWithInvalidCount(err, t)
171243
}
172244

173-
func TestExactValidArgsWithInvalidArgs(t *testing.T) {
174-
c := getCommand(ExactValidArgs(3), true)
245+
func TestExactArgsWithInvalidCountWithValidWithInvalidArgs(t *testing.T) {
246+
c := getCommand(ExactArgs(2), true)
175247
_, err := executeCommand(c, "three", "a", "two")
176248
validWithInvalidArgs(err, t)
177249
}
178250

251+
// RangeArgs
252+
179253
func TestRangeArgs(t *testing.T) {
180254
c := getCommand(RangeArgs(2, 4), false)
181255
output, err := executeCommand(c, "a", "b", "c")
182256
expectSuccess(output, err, t)
183257
}
184258

259+
func TestRangeArgsWithValid(t *testing.T) {
260+
c := getCommand(RangeArgs(2, 4), true)
261+
output, err := executeCommand(c, "three", "one", "two")
262+
expectSuccess(output, err, t)
263+
}
264+
265+
func TestRangeArgsWithValidWithInvalidArgs(t *testing.T) {
266+
c := getCommand(RangeArgs(2, 4), true)
267+
_, err := executeCommand(c, "three", "a", "two")
268+
validWithInvalidArgs(err, t)
269+
}
270+
185271
func TestRangeArgsWithInvalidCount(t *testing.T) {
186272
c := getCommand(RangeArgs(2, 4), false)
187273
_, err := executeCommand(c, "a")
188274
rangeArgsWithInvalidCount(err, t)
189275
}
190276

277+
func TestRangeArgsWithInvalidCountWithValid(t *testing.T) {
278+
c := getCommand(RangeArgs(2, 4), true)
279+
_, err := executeCommand(c, "two")
280+
rangeArgsWithInvalidCount(err, t)
281+
}
282+
283+
func TestRangeArgsWithInvalidCountWithValidWithInvalidArgs(t *testing.T) {
284+
c := getCommand(RangeArgs(2, 4), true)
285+
_, err := executeCommand(c, "a")
286+
validWithInvalidArgs(err, t)
287+
}
288+
289+
// Takes(No)Args
290+
191291
func TestRootTakesNoArgs(t *testing.T) {
192292
rootCmd := &Command{Use: "root", Run: emptyRun}
193293
childCmd := &Command{Use: "child", Run: emptyRun}

bash_completions_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func TestBashCompletions(t *testing.T) {
138138
timesCmd := &Command{
139139
Use: "times [# times] [string to echo]",
140140
SuggestFor: []string{"counts"},
141-
Args: OnlyValidArgs,
141+
Args: ArbitraryArgs,
142142
ValidArgs: []string{"one", "two", "three", "four"},
143143
Short: "Echo anything to the screen more times",
144144
Long: "a slightly useless command for testing.",

command.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,13 @@ func (c *Command) ValidateArgs(args []string) error {
875875
if c.Args == nil {
876876
return nil
877877
}
878+
if len(c.ValidArgs) > 0 {
879+
for _, v := range args {
880+
if !stringInSlice(v, c.ValidArgs) {
881+
return fmt.Errorf("invalid argument %q for %q%s", v, c.CommandPath(), c.findSuggestions(args[0]))
882+
}
883+
}
884+
}
878885
return c.Args(c, args)
879886
}
880887

0 commit comments

Comments
 (0)