Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow customization of prefixes and environment variable hints in flag help strings #661

Merged
merged 2 commits into from
Sep 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 14 additions & 6 deletions flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ var HelpFlag Flag = BoolFlag{
// to display a flag.
var FlagStringer FlagStringFunc = stringifyFlag

// FlagNamePrefixer converts a full flag name and its placeholder into the help
// message flag prefix. This is used by the default FlagStringer.
var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames

// FlagEnvHinter annotates flag help message with the environment variable
// details. This is used by the default FlagStringer.
var FlagEnvHinter FlagEnvHintFunc = withEnvHint

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

Expand Down Expand Up @@ -710,13 +718,13 @@ func stringifyFlag(f Flag) string {

switch f.(type) {
case IntSliceFlag:
return withEnvHint(fv.FieldByName("EnvVar").String(),
return FlagEnvHinter(fv.FieldByName("EnvVar").String(),
stringifyIntSliceFlag(f.(IntSliceFlag)))
case Int64SliceFlag:
return withEnvHint(fv.FieldByName("EnvVar").String(),
return FlagEnvHinter(fv.FieldByName("EnvVar").String(),
stringifyInt64SliceFlag(f.(Int64SliceFlag)))
case StringSliceFlag:
return withEnvHint(fv.FieldByName("EnvVar").String(),
return FlagEnvHinter(fv.FieldByName("EnvVar").String(),
stringifyStringSliceFlag(f.(StringSliceFlag)))
}

Expand Down Expand Up @@ -744,8 +752,8 @@ func stringifyFlag(f Flag) string {

usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString))

return withEnvHint(fv.FieldByName("EnvVar").String(),
fmt.Sprintf("%s\t%s", prefixedNames(fv.FieldByName("Name").String(), placeholder), usageWithDefault))
return FlagEnvHinter(fv.FieldByName("EnvVar").String(),
fmt.Sprintf("%s\t%s", FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder), usageWithDefault))
}

func stringifyIntSliceFlag(f IntSliceFlag) string {
Expand Down Expand Up @@ -795,5 +803,5 @@ func stringifySliceFlag(usage, name string, defaultVals []string) string {
}

usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
return fmt.Sprintf("%s\t%s", prefixedNames(name, placeholder), usageWithDefault)
return fmt.Sprintf("%s\t%s", FlagNamePrefixer(name, placeholder), usageWithDefault)
}
77 changes: 77 additions & 0 deletions flag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,83 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
}
}

var prefixStringFlagTests = []struct {
name string
usage string
value string
prefixer FlagNamePrefixFunc
expected string
}{
{"foo", "", "", func(a, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, "name: foo, ph: value\t"},
{"f", "", "", func(a, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, "name: f, ph: value\t"},
{"f", "The total `foo` desired", "all", func(a, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, "name: f, ph: foo\tThe total foo desired (default: \"all\")"},
{"test", "", "Something", func(a, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, "name: test, ph: value\t(default: \"Something\")"},
{"config,c", "Load configuration from `FILE`", "", func(a, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, "name: config,c, ph: FILE\tLoad configuration from FILE"},
{"config,c", "Load configuration from `CONFIG`", "config.json", func(a, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, "name: config,c, ph: CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"},
}

func TestFlagNamePrefixer(t *testing.T) {
defer func() {
FlagNamePrefixer = prefixedNames
}()

for _, test := range prefixStringFlagTests {
FlagNamePrefixer = test.prefixer
flag := StringFlag{Name: test.name, Usage: test.usage, Value: test.value}
output := flag.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}

var envHintFlagTests = []struct {
name string
env string
hinter FlagEnvHintFunc
expected string
}{
{"foo", "", func(a, b string) string {
return fmt.Sprintf("env: %s, str: %s", a, b)
}, "env: , str: --foo value\t"},
{"f", "", func(a, b string) string {
return fmt.Sprintf("env: %s, str: %s", a, b)
}, "env: , str: -f value\t"},
{"foo", "ENV_VAR", func(a, b string) string {
return fmt.Sprintf("env: %s, str: %s", a, b)
}, "env: ENV_VAR, str: --foo value\t"},
{"f", "ENV_VAR", func(a, b string) string {
return fmt.Sprintf("env: %s, str: %s", a, b)
}, "env: ENV_VAR, str: -f value\t"},
}

func TestFlagEnvHinter(t *testing.T) {
defer func() {
FlagEnvHinter = withEnvHint
}()

for _, test := range envHintFlagTests {
FlagEnvHinter = test.hinter
flag := StringFlag{Name: test.name, EnvVar: test.env}
output := flag.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}

var stringSliceFlagTests = []struct {
name string
value *StringSlice
Expand Down
8 changes: 8 additions & 0 deletions funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,11 @@ type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error
// FlagStringFunc is used by the help generation to display a flag, which is
// expected to be a single line.
type FlagStringFunc func(Flag) string

// FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix
// text for a flag's full name.
type FlagNamePrefixFunc func(fullName, placeholder string) string

// FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help
// with the environment variable details.
type FlagEnvHintFunc func(envVar, str string) string