Skip to content

Commit

Permalink
Feature: Add check for overlapping xor and and groups (#443)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
tinytyranid and inful authored Sep 10, 2024
1 parent b297ae9 commit 7d84b95
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ Both can coexist with standard Tag parsing.
| `enum:"X,Y,..."` | Set of valid values allowed for this flag. An enum field must be `required` or have a valid `default`. |
| `group:"X"` | Logical group for a flag or command. |
| `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. |
| `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. |
| `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. |
| `prefix:"X"` | Prefix for all sub-flags. |
| `envprefix:"X"` | Envar prefix for all sub-flags. |
| `set:"K=V"` | Set a variable for expansion by child elements. Multiples can occur. |
Expand Down
33 changes: 33 additions & 0 deletions kong.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,42 @@ func New(grammar interface{}, options ...Option) (*Kong, error) {

k.bindings.add(k.vars)

if err = checkOverlappingXorAnd(k); err != nil {
return nil, err
}

return k, nil
}

func checkOverlappingXorAnd(k *Kong) error {
xorGroups := map[string][]string{}
andGroups := map[string][]string{}
for _, flag := range k.Model.Node.Flags {
for _, xor := range flag.Xor {
xorGroups[xor] = append(xorGroups[xor], flag.Name)
}
for _, and := range flag.And {
andGroups[and] = append(andGroups[and], flag.Name)
}
}
for xor, xorSet := range xorGroups {
for and, andSet := range andGroups {
overlappingEntries := []string{}
for _, xorTag := range xorSet {
for _, andTag := range andSet {
if xorTag == andTag {
overlappingEntries = append(overlappingEntries, xorTag)
}
}
}
if len(overlappingEntries) > 1 {
return fmt.Errorf("invalid xor and combination, %s and %s overlap with more than one: %s", xor, and, overlappingEntries)
}
}
}
return nil
}

type varStack []Vars

func (v *varStack) head() Vars { return (*v)[len(*v)-1] }
Expand Down
10 changes: 10 additions & 0 deletions kong_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,16 @@ func TestXorAnd(t *testing.T) {
assert.EqualError(t, err, "--hello and --one can't be used together, --hello and --two must be used together")
}

func TestOverLappingXorAnd(t *testing.T) {
var cli struct {
Hello bool `xor:"one" and:"two"`
One bool `xor:"one" and:"two"`
Two string `xor:"one" and:"two"`
}
_, err := kong.New(&cli)
assert.EqualError(t, err, "invalid xor and combination, one and two overlap with more than one: [hello one two]")
}

func TestXorRequired(t *testing.T) {
var cli struct {
One bool `xor:"one,two" required:""`
Expand Down

0 comments on commit 7d84b95

Please sign in to comment.