Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions command_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ func (cmd *Command) setupDefaults(osArgs []string) {
tracef("setting category on mutually exclusive flags (cmd=%[1]q)", cmd.Name)
for _, grp := range cmd.MutuallyExclusiveFlags {
grp.propagateCategory()
grp.propagateStringer()
}

tracef("setting flag categories (cmd=%[1]q)", cmd.Name)
Expand Down Expand Up @@ -196,6 +197,7 @@ func (cmd *Command) setupSubcommand() {
tracef("setting category on mutually exclusive flags (cmd=%[1]q)", cmd.Name)
for _, grp := range cmd.MutuallyExclusiveFlags {
grp.propagateCategory()
grp.propagateStringer()
}

tracef("setting flag categories (cmd=%[1]q)", cmd.Name)
Expand Down
12 changes: 12 additions & 0 deletions flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,18 @@ type CategorizableFlag interface {
SetCategory(string)
}

// FlagStringerOverrider is an optional interface that allows an individual
// flag to be given a per-flag override of [FlagStringer]. FlagBase and
// BoolWithInverseFlag implement this. It's used by
// [MutuallyExclusiveFlags.Stringer] to customize how flags within a
// mutually exclusive group are displayed in help output.
type FlagStringerOverrider interface {
// SetStringer overrides the [FlagStringFunc] used by this flag's
// String method. Passing nil restores the default behavior of using
// the package-level [FlagStringer].
SetStringer(FlagStringFunc)
}

// LocalFlag is an interface to enable detection of flags which are local
// to current command
type LocalFlag interface {
Expand Down
15 changes: 14 additions & 1 deletion flag_bool_with_inverse.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ type BoolWithInverseFlag struct {
value Value // value representing this flag's value
pset bool
nset bool
stringer FlagStringFunc // optional per-flag override of FlagStringer
}

// SetStringer overrides the [FlagStringFunc] used by this flag's String
// method. Passing nil restores the default behavior of using the
// package-level [FlagStringer]. This is used e.g. by
// [MutuallyExclusiveFlags.Stringer].
func (bif *BoolWithInverseFlag) SetStringer(s FlagStringFunc) {
bif.stringer = s
}

func (bif *BoolWithInverseFlag) IsSet() bool {
Expand Down Expand Up @@ -171,7 +180,11 @@ func (bif *BoolWithInverseFlag) IsVisible() bool {
// Example for BoolFlag{Name: "env", Aliases: []string{"e"}}
// --[no-]env, -e (default: false)
func (bif *BoolWithInverseFlag) String() string {
out := FlagStringer(bif)
fs := FlagStringer
if bif.stringer != nil {
fs = bif.stringer
}
out := fs(bif)

i := strings.Index(out, "\t")

Expand Down
22 changes: 17 additions & 5 deletions flag_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@ type FlagBase[T any, C any, VC ValueCreator[T, C]] struct {
ValidateDefaults bool `json:"validateDefaults"` // whether to validate defaults or not

// unexported fields for internal use
count int // number of times the flag has been set
hasBeenSet bool // whether the flag has been set from env or file
applied bool // whether the flag has been applied to a flag set already
creator VC // value creator for this flag type
value Value // value representing this flag's value
count int // number of times the flag has been set
hasBeenSet bool // whether the flag has been set from env or file
applied bool // whether the flag has been applied to a flag set already
creator VC // value creator for this flag type
value Value // value representing this flag's value
stringer FlagStringFunc // optional per-flag override of FlagStringer
}

// GetValue returns the flags value as string representation and an empty
Expand Down Expand Up @@ -232,9 +233,20 @@ func (f *FlagBase[T, C, V]) IsDefaultVisible() bool {

// String returns a readable representation of this value (for usage defaults)
func (f *FlagBase[T, C, V]) String() string {
if f.stringer != nil {
return f.stringer(f)
}
return FlagStringer(f)
}

// SetStringer overrides the [FlagStringFunc] used by this flag's String
// method. Passing nil restores the default behavior of using the
// package-level [FlagStringer]. This is used e.g. by
// [MutuallyExclusiveFlags.Stringer].
func (f *FlagBase[T, C, V]) SetStringer(s FlagStringFunc) {
f.stringer = s
}

// IsSet returns whether or not the flag has been set through env or file
func (f *FlagBase[T, C, V]) IsSet() bool {
return f.hasBeenSet
Expand Down
20 changes: 20 additions & 0 deletions flag_mutex.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ type MutuallyExclusiveFlags struct {

// Category to apply to all flags within group
Category string

// Stringer overrides how each flag within this group is displayed in
// help output. If nil, flags use [FlagStringer] as usual.
Stringer FlagStringFunc `json:"-"`
}

func (grp MutuallyExclusiveFlags) check(_ *Command) error {
Expand Down Expand Up @@ -69,3 +73,19 @@ func (grp MutuallyExclusiveFlags) propagateCategory() {
}
}
}

// propagateStringer applies [MutuallyExclusiveFlags.Stringer], if set, to
// every flag within the group that supports a [FlagStringerOverrider].
func (grp MutuallyExclusiveFlags) propagateStringer() {
if grp.Stringer == nil {
return
}

for _, grpf := range grp.Flags {
for _, f := range grpf {
if sf, ok := f.(FlagStringerOverrider); ok {
sf.SetStringer(grp.Stringer)
}
}
}
}
40 changes: 40 additions & 0 deletions flag_mutex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,43 @@ func TestFlagMutuallyExclusiveFlags(t *testing.T) {
})
}
}

func TestMutuallyExclusiveFlags_PropagateStringer(t *testing.T) {
customStringer := func(f Flag) string {
return "custom:" + f.Names()[0]
}

grp := MutuallyExclusiveFlags{
Stringer: customStringer,
Flags: [][]Flag{
{
&StringFlag{Name: "foo"},
&BoolWithInverseFlag{Name: "bar"},
},
{
&Int64Flag{Name: "baz"},
},
},
}

grp.propagateStringer()

assert.Equal(t, "custom:foo", grp.Flags[0][0].String())
assert.Contains(t, grp.Flags[0][1].String(), "custom:bar")
assert.Equal(t, "custom:baz", grp.Flags[1][0].String())
}

func TestMutuallyExclusiveFlags_PropagateStringerNil(t *testing.T) {
grp := MutuallyExclusiveFlags{
Flags: [][]Flag{
{
&StringFlag{Name: "foo"},
},
},
}

// should not panic and should leave flags using the default FlagStringer
grp.propagateStringer()

assert.NotEqual(t, "", grp.Flags[0][0].String())
}
26 changes: 26 additions & 0 deletions godoc-current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,11 @@ func (bif *BoolWithInverseFlag) Set(name, val string) error

func (bif *BoolWithInverseFlag) SetCategory(c string)

func (bif *BoolWithInverseFlag) SetStringer(s FlagStringFunc)
SetStringer overrides the FlagStringFunc used by this flag's String method.
Passing nil restores the default behavior of using the package-level
FlagStringer. This is used e.g. by MutuallyExclusiveFlags.Stringer.

func (bif *BoolWithInverseFlag) String() string
String implements the standard Stringer interface.

Expand Down Expand Up @@ -1078,6 +1083,11 @@ func (f *FlagBase[T, C, V]) Set(_ string, val string) error

func (f *FlagBase[T, C, V]) SetCategory(c string)

func (f *FlagBase[T, C, V]) SetStringer(s FlagStringFunc)
SetStringer overrides the FlagStringFunc used by this flag's String method.
Passing nil restores the default behavior of using the package-level
FlagStringer. This is used e.g. by MutuallyExclusiveFlags.Stringer.

func (f *FlagBase[T, C, V]) String() string
String returns a readable representation of this value (for usage defaults)

Expand Down Expand Up @@ -1127,6 +1137,18 @@ var FlagStringer FlagStringFunc = stringifyFlag
FlagStringer converts a flag definition to a string. This is used by help to
display a flag.

type FlagStringerOverrider interface {
// SetStringer overrides the [FlagStringFunc] used by this flag's
// String method. Passing nil restores the default behavior of using
// the package-level [FlagStringer].
SetStringer(FlagStringFunc)
}
FlagStringerOverrider is an optional interface that allows an
individual flag to be given a per-flag override of FlagStringer.
FlagBase and BoolWithInverseFlag implement this. It's used by
MutuallyExclusiveFlags.Stringer to customize how flags within a mutually
exclusive group are displayed in help output.

type FlagsByName []Flag
FlagsByName is a slice of Flag.

Expand Down Expand Up @@ -1315,6 +1337,10 @@ type MutuallyExclusiveFlags struct {

// Category to apply to all flags within group
Category string

// Stringer overrides how each flag within this group is displayed in
// help output. If nil, flags use [FlagStringer] as usual.
Stringer FlagStringFunc `json:"-"`
}
MutuallyExclusiveFlags defines a mutually exclusive flag group Multiple
option paths can be provided out of which only one can be defined on cmdline
Expand Down
26 changes: 26 additions & 0 deletions testdata/godoc-v3.x.txt
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,11 @@ func (bif *BoolWithInverseFlag) Set(name, val string) error

func (bif *BoolWithInverseFlag) SetCategory(c string)

func (bif *BoolWithInverseFlag) SetStringer(s FlagStringFunc)
SetStringer overrides the FlagStringFunc used by this flag's String method.
Passing nil restores the default behavior of using the package-level
FlagStringer. This is used e.g. by MutuallyExclusiveFlags.Stringer.

func (bif *BoolWithInverseFlag) String() string
String implements the standard Stringer interface.

Expand Down Expand Up @@ -1078,6 +1083,11 @@ func (f *FlagBase[T, C, V]) Set(_ string, val string) error

func (f *FlagBase[T, C, V]) SetCategory(c string)

func (f *FlagBase[T, C, V]) SetStringer(s FlagStringFunc)
SetStringer overrides the FlagStringFunc used by this flag's String method.
Passing nil restores the default behavior of using the package-level
FlagStringer. This is used e.g. by MutuallyExclusiveFlags.Stringer.

func (f *FlagBase[T, C, V]) String() string
String returns a readable representation of this value (for usage defaults)

Expand Down Expand Up @@ -1127,6 +1137,18 @@ var FlagStringer FlagStringFunc = stringifyFlag
FlagStringer converts a flag definition to a string. This is used by help to
display a flag.

type FlagStringerOverrider interface {
// SetStringer overrides the [FlagStringFunc] used by this flag's
// String method. Passing nil restores the default behavior of using
// the package-level [FlagStringer].
SetStringer(FlagStringFunc)
}
FlagStringerOverrider is an optional interface that allows an
individual flag to be given a per-flag override of FlagStringer.
FlagBase and BoolWithInverseFlag implement this. It's used by
MutuallyExclusiveFlags.Stringer to customize how flags within a mutually
exclusive group are displayed in help output.

type FlagsByName []Flag
FlagsByName is a slice of Flag.

Expand Down Expand Up @@ -1315,6 +1337,10 @@ type MutuallyExclusiveFlags struct {

// Category to apply to all flags within group
Category string

// Stringer overrides how each flag within this group is displayed in
// help output. If nil, flags use [FlagStringer] as usual.
Stringer FlagStringFunc `json:"-"`
}
MutuallyExclusiveFlags defines a mutually exclusive flag group Multiple
option paths can be provided out of which only one can be defined on cmdline
Expand Down