Allow multiple xor:"" groups to be defined.

Fixes #113.
This commit is contained in:
Alec Thomas
2021-06-21 18:58:44 +09:30
parent 8aa52739ea
commit 54558f65e8
5 changed files with 32 additions and 14 deletions
+1 -1
View File
@@ -446,7 +446,7 @@ Tag | Description
`mapsep:"X"` | Separator for maps (defaults to ";"). May be `none` to disable splitting.
`enum:"X,Y,..."` | Set of valid values allowed for this flag.
`group:"X"` | Logical group for a flag or command.
`xor:"X"` | Exclusive OR group for flags. Only one flag in the group can be used which is restricted within the same 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.
`prefix:"X"` | Prefix for all sub-flags.
`set:"K=V"` | Set a variable for expansion by child elements. Multiples can occur.
`embed:""` | If present, this field's children will be embedded in the parent. Useful for composition.
+5 -6
View File
@@ -871,13 +871,12 @@ func checkXorDuplicates(paths []*Path) error {
if !flag.Set {
continue
}
if flag.Xor == "" {
continue
for _, xor := range flag.Xor {
if seen[xor] != nil {
return fmt.Errorf("--%s and --%s can't be used together", seen[xor].Name, flag.Name)
}
seen[xor] = flag
}
if seen[flag.Xor] != nil {
return fmt.Errorf("--%s and --%s can't be used together", seen[flag.Xor].Name, flag.Name)
}
seen[flag.Xor] = flag
}
}
return nil
+16
View File
@@ -868,6 +868,22 @@ func TestXorChild(t *testing.T) {
require.Error(t, err, "--two and --three can't be used together")
}
func TestMultiXor(t *testing.T) {
var cli struct {
Hello bool `xor:"one,two"`
One bool `xor:"one"`
Two string `xor:"two"`
}
p := mustNew(t, &cli)
_, err := p.Parse([]string{"--hello", "--one"})
require.EqualError(t, err, "--hello and --one can't be used together")
p = mustNew(t, &cli)
_, err = p.Parse([]string{"--hello", "--two=foo"})
require.EqualError(t, err, "--hello and --two can't be used together")
}
func TestEnumSequence(t *testing.T) {
var cli struct {
State []string `enum:"a,b,c" default:"a"`
+1 -1
View File
@@ -377,7 +377,7 @@ type Positional = Value
type Flag struct {
*Value
Group *Group // Logical grouping when displaying. May also be used by configuration loaders to group options logically.
Xor string
Xor []string
PlaceHolder string
Env string
Short rune
+9 -6
View File
@@ -29,7 +29,7 @@ type Tag struct {
MapSep rune
Enum string
Group string
Xor string
Xor []string
Vars Vars
Prefix string // Optional prefix on anonymous structs. All sub-flags will have this prefix.
Embed bool
@@ -125,6 +125,10 @@ func newEmptyTag() *Tag {
return &Tag{items: map[string][]string{}}
}
func tagSplitFn(r rune) bool {
return r == ',' || r == ' '
}
func parseTag(fv reflect.Value, ft reflect.StructField) *Tag {
if ft.Tag.Get("kong") == "-" {
t := newEmptyTag()
@@ -164,7 +168,9 @@ func parseTag(fv reflect.Value, ft reflect.StructField) *Tag {
t.Sep, _ = t.GetSep("sep", ',')
t.MapSep, _ = t.GetSep("mapsep", ';')
t.Group = t.Get("group")
t.Xor = t.Get("xor")
for _, xor := range t.GetAll("xor") {
t.Xor = append(t.Xor, strings.FieldsFunc(xor, tagSplitFn)...)
}
t.Prefix = t.Get("prefix")
t.Embed = t.Has("embed")
negatable := t.Has("negatable")
@@ -172,12 +178,9 @@ func parseTag(fv reflect.Value, ft reflect.StructField) *Tag {
fail("negatable can only be set on booleans")
}
t.Negatable = negatable
splitFn := func(r rune) bool {
return r == ',' || r == ' '
}
aliases := t.Get("aliases")
if len(aliases) > 0 {
t.Aliases = append(t.Aliases, strings.FieldsFunc(aliases, splitFn)...)
t.Aliases = append(t.Aliases, strings.FieldsFunc(aliases, tagSplitFn)...)
}
t.Vars = Vars{}
for _, set := range t.GetAll("set") {