From 11d45572f9727acfbc93daa8565f379d396125d6 Mon Sep 17 00:00:00 2001 From: rliebz Date: Sat, 26 Aug 2017 07:42:25 -0400 Subject: [PATCH 1/5] Export funcs to configure flag prefix/env hints This will allow users to customize the prefix section or env hint section of the flag entries in the help menu without having to reimplement the rest of the logic required in defining FlagStringer. --- flag.go | 20 ++++++++++++++------ funcs.go | 8 ++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/flag.go b/flag.go index 877ff35..b17f5b9 100644 --- a/flag.go +++ b/flag.go @@ -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 @@ -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))) } @@ -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 { @@ -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) } diff --git a/funcs.go b/funcs.go index cba5e6c..3ad3c6d 100644 --- a/funcs.go +++ b/funcs.go @@ -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 From cbbe4c1a2c34e52c8ad0937c01c9c15ef407a6d5 Mon Sep 17 00:00:00 2001 From: Robert Liebowitz Date: Mon, 18 Sep 2017 00:44:42 -0400 Subject: [PATCH 2/5] Add tests for custom flag prefix/env hints --- flag_test.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/flag_test.go b/flag_test.go index 1ccb639..bc840b5 100644 --- a/flag_test.go +++ b/flag_test.go @@ -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 From 67ee172e6da2cdad8e48af107eef0fbfd1e85eec Mon Sep 17 00:00:00 2001 From: Sebastian Sprenger Date: Fri, 6 Oct 2017 07:28:18 +0200 Subject: [PATCH 3/5] fix misspelling issue --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index db94191..012b9b5 100644 --- a/context.go +++ b/context.go @@ -73,7 +73,7 @@ func (c *Context) IsSet(name string) bool { // change in version 2 to add `IsSet` to the Flag interface to push the // responsibility closer to where the information required to determine // whether a flag is set by non-standard means such as environment - // variables is avaliable. + // variables is available. // // See https://github.com/urfave/cli/issues/294 for additional discussion flags := c.Command.Flags From c3cc74dac756e33c2919ab998481809e8720e068 Mon Sep 17 00:00:00 2001 From: Sebastian Sprenger Date: Fri, 6 Oct 2017 07:28:43 +0200 Subject: [PATCH 4/5] fix ineffective assigns --- app_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/app_test.go b/app_test.go index e14ddaf..54e0951 100644 --- a/app_test.go +++ b/app_test.go @@ -497,7 +497,6 @@ func TestApp_Float64Flag(t *testing.T) { } func TestApp_ParseSliceFlags(t *testing.T) { - var parsedOption, firstArg string var parsedIntSlice []int var parsedStringSlice []string @@ -511,8 +510,6 @@ func TestApp_ParseSliceFlags(t *testing.T) { Action: func(c *Context) error { parsedIntSlice = c.IntSlice("p") parsedStringSlice = c.StringSlice("ip") - parsedOption = c.String("option") - firstArg = c.Args().First() return nil }, } From c202606a17a763fcc1b320cac6cf584662e31364 Mon Sep 17 00:00:00 2001 From: Sebastian Sprenger Date: Fri, 6 Oct 2017 07:29:13 +0200 Subject: [PATCH 5/5] fix golint issues --- altsrc/map_input_source.go | 14 +++++++------- altsrc/toml_file_loader.go | 4 ++-- altsrc/yaml_file_loader.go | 4 ++-- app.go | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index b3169e0..a111eee 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -22,15 +22,15 @@ func nestedVal(name string, tree map[interface{}]interface{}) (interface{}, bool if sections := strings.Split(name, "."); len(sections) > 1 { node := tree for _, section := range sections[:len(sections)-1] { - if child, ok := node[section]; !ok { + child, ok := node[section] + if !ok { return nil, false - } else { - if ctype, ok := child.(map[interface{}]interface{}); !ok { - return nil, false - } else { - node = ctype - } } + ctype, ok := child.(map[interface{}]interface{}) + if !ok { + return nil, false + } + node = ctype } if val, ok := node[sections[len(sections)-1]]; ok { return val, true diff --git a/altsrc/toml_file_loader.go b/altsrc/toml_file_loader.go index 37870fc..87bb9d9 100644 --- a/altsrc/toml_file_loader.go +++ b/altsrc/toml_file_loader.go @@ -66,9 +66,9 @@ func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) { return ret, nil } -func (self *tomlMap) UnmarshalTOML(i interface{}) error { +func (tm *tomlMap) UnmarshalTOML(i interface{}) error { if tmp, err := unmarshalMap(i); err == nil { - self.Map = tmp + tm.Map = tmp } else { return err } diff --git a/altsrc/yaml_file_loader.go b/altsrc/yaml_file_loader.go index dd808d5..202469f 100644 --- a/altsrc/yaml_file_loader.go +++ b/altsrc/yaml_file_loader.go @@ -86,7 +86,7 @@ func loadDataFrom(filePath string) ([]byte, error) { return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath) } return ioutil.ReadFile(filePath) - } else { - return nil, fmt.Errorf("unable to determine how to load from path %s", filePath) } + + return nil, fmt.Errorf("unable to determine how to load from path %s", filePath) } diff --git a/app.go b/app.go index 51fc45d..5991226 100644 --- a/app.go +++ b/app.go @@ -491,7 +491,7 @@ func HandleAction(action interface{}, context *Context) (err error) { } else if a, ok := action.(func(*Context)); ok { // deprecated function signature a(context) return nil - } else { - return errInvalidActionType } + + return errInvalidActionType }