Skip to content
Open
Show file tree
Hide file tree
Changes from all 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