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 <jone.marius@vign.es> * Chore: Rewrite overlap err to avoid duplicated words --------- Co-authored-by: inful <jone.marius@vign.es>
This commit is contained in:
@@ -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. |
|
||||
|
||||
@@ -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] }
|
||||
|
||||
@@ -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:""`
|
||||
|
||||
Reference in New Issue
Block a user