diff --git a/flag_mutex.go b/flag_mutex.go index 247bcb569b..5a1e87cfb5 100644 --- a/flag_mutex.go +++ b/flag_mutex.go @@ -17,32 +17,49 @@ type MutuallyExclusiveFlags struct { } func (grp MutuallyExclusiveFlags) check(_ *Command) error { - oneSet := false e := &mutuallyExclusiveGroup{} - for _, grpf := range grp.Flags { - for _, f := range grpf { - if f.IsSet() { - if oneSet { - e.flag2Name = f.Names()[0] - return e - } - e.flag1Name = f.Names()[0] - oneSet = true - break - } - if oneSet { - break - } + // Check for the use of a mutually-exclusive flag, starting at + // the first group. + name, i, ok := grp.findSetFlag(0) + if ok { + e.flag1Name = name + i++ + + // Check for the use of a flag in a mutually exclusive + // relationship with the one we just found. + if name2, _, ok := grp.findSetFlag(i); ok { + e.flag2Name = name2 + return e } } - if !oneSet && grp.Required { + if !ok && grp.Required { return &mutuallyExclusiveGroupRequiredFlag{flags: &grp} } + return nil } +// findSetFlag is used in [MutuallyExclusiveFlags.check] to find +// whether at least one flag inside a mutually exclusive flag group is +// set. If so, return the flag name, position at which it's set, and +// Boolean true (indicating that a flag was found.) Else, return all +// zero values. +func (grp MutuallyExclusiveFlags) findSetFlag(startIdx int) (string, int, bool) { + for i := startIdx; i < len(grp.Flags); i++ { + flags := grp.Flags[i] + + for _, flg := range flags { + if flg.IsSet() { + return flg.Names()[0], i, true + } + } + } + + return "", 0, false +} + func (grp MutuallyExclusiveFlags) propagateCategory() { for _, grpf := range grp.Flags { for _, f := range grpf { diff --git a/flag_mutex_test.go b/flag_mutex_test.go index 69ceec2328..b6c9a161c5 100644 --- a/flag_mutex_test.go +++ b/flag_mutex_test.go @@ -30,6 +30,9 @@ func newCommand() *Command { Aliases: []string{"ai"}, Sources: EnvVars("T_VAR"), }, + &BoolFlag{ + Name: "q", + }, }, }, }, @@ -73,6 +76,12 @@ func TestFlagMutuallyExclusiveFlags(t *testing.T) { errStr: "option i cannot be set along with option ai", required: true, }, + { + name: "required both set second member", + args: []string{"--i", "11", "--q"}, + errStr: "option i cannot be set along with option q", + required: true, + }, { name: "set env var", required: true,