Skip to content

Commit 7d84b95

Browse files
tinytyranidinful
andauthored
Feature: Add check for overlapping xor and and groups (#443)
* Docs: Clean and group description * Feat: Add check for overlapping xor and and groups Co-authored-by: inful <[email protected]> * Chore: Rewrite overlap err to avoid duplicated words --------- Co-authored-by: inful <[email protected]>
1 parent b297ae9 commit 7d84b95

File tree

3 files changed

+44
-1
lines changed

3 files changed

+44
-1
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ Both can coexist with standard Tag parsing.
577577
| `enum:"X,Y,..."` | Set of valid values allowed for this flag. An enum field must be `required` or have a valid `default`. |
578578
| `group:"X"` | Logical group for a flag or command. |
579579
| `xor:"X,Y,..."` | Exclusive OR groups for flags. Only one flag in the group can be used which is restricted within the same command. When combined with `required`, at least one of the `xor` group will be required. |
580-
| `and:"X,Y,..."` | Exclusive AND groups for flags. All flags in the group must be used in the same command. When combined with `required`, all flags in the group will be required. |
580+
| `and:"X,Y,..."` | AND groups for flags. All flags in the group must be used in the same command. When combined with `required`, all flags in the group will be required. |
581581
| `prefix:"X"` | Prefix for all sub-flags. |
582582
| `envprefix:"X"` | Envar prefix for all sub-flags. |
583583
| `set:"K=V"` | Set a variable for expansion by child elements. Multiples can occur. |

Diff for: kong.go

+33
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,42 @@ func New(grammar interface{}, options ...Option) (*Kong, error) {
167167

168168
k.bindings.add(k.vars)
169169

170+
if err = checkOverlappingXorAnd(k); err != nil {
171+
return nil, err
172+
}
173+
170174
return k, nil
171175
}
172176

177+
func checkOverlappingXorAnd(k *Kong) error {
178+
xorGroups := map[string][]string{}
179+
andGroups := map[string][]string{}
180+
for _, flag := range k.Model.Node.Flags {
181+
for _, xor := range flag.Xor {
182+
xorGroups[xor] = append(xorGroups[xor], flag.Name)
183+
}
184+
for _, and := range flag.And {
185+
andGroups[and] = append(andGroups[and], flag.Name)
186+
}
187+
}
188+
for xor, xorSet := range xorGroups {
189+
for and, andSet := range andGroups {
190+
overlappingEntries := []string{}
191+
for _, xorTag := range xorSet {
192+
for _, andTag := range andSet {
193+
if xorTag == andTag {
194+
overlappingEntries = append(overlappingEntries, xorTag)
195+
}
196+
}
197+
}
198+
if len(overlappingEntries) > 1 {
199+
return fmt.Errorf("invalid xor and combination, %s and %s overlap with more than one: %s", xor, and, overlappingEntries)
200+
}
201+
}
202+
}
203+
return nil
204+
}
205+
173206
type varStack []Vars
174207

175208
func (v *varStack) head() Vars { return (*v)[len(*v)-1] }

Diff for: kong_test.go

+10
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,16 @@ func TestXorAnd(t *testing.T) {
10261026
assert.EqualError(t, err, "--hello and --one can't be used together, --hello and --two must be used together")
10271027
}
10281028

1029+
func TestOverLappingXorAnd(t *testing.T) {
1030+
var cli struct {
1031+
Hello bool `xor:"one" and:"two"`
1032+
One bool `xor:"one" and:"two"`
1033+
Two string `xor:"one" and:"two"`
1034+
}
1035+
_, err := kong.New(&cli)
1036+
assert.EqualError(t, err, "invalid xor and combination, one and two overlap with more than one: [hello one two]")
1037+
}
1038+
10291039
func TestXorRequired(t *testing.T) {
10301040
var cli struct {
10311041
One bool `xor:"one,two" required:""`

0 commit comments

Comments
 (0)