From 81fcf706ea1c2bdde52d04088a9c5b0bc1a97f7a Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 21 May 2016 21:29:45 -0400 Subject: [PATCH 1/2] Replace all "stringly typed" fields with slice equivalents and alter the Flag interface accordingly Closes #415 --- README.md | 34 +++-- altsrc/flag.go | 78 +++++------ altsrc/flag_test.go | 20 +-- altsrc/yaml_command_test.go | 8 +- app.go | 7 +- command.go | 3 +- context.go | 2 +- flag.go | 264 +++++++++++++++++++++--------------- flag_test.go | 132 ++++++++++-------- help.go | 8 +- help_test.go | 14 +- 11 files changed, 314 insertions(+), 256 deletions(-) diff --git a/README.md b/README.md index de73458..0a1f624 100644 --- a/README.md +++ b/README.md @@ -265,8 +265,9 @@ For example this: ```go cli.StringFlag{ - Name: "config, c", - Usage: "Load configuration from `FILE`", + Name: "config", + Aliases: []string{"c"}, + Usage: "Load configuration from `FILE`", } ``` @@ -285,9 +286,10 @@ You can set alternate (or short) names for flags by providing a comma-delimited ``` go app.Flags = []cli.Flag { cli.StringFlag{ - Name: "lang, l", - Value: "english", - Usage: "language for the greeting", + Name: "lang", + Aliases: []string{"l"}, + Value: "english", + Usage: "language for the greeting", }, } ``` @@ -296,28 +298,30 @@ That flag can then be set with `--lang spanish` or `-l spanish`. Note that givin #### Values from the Environment -You can also have the default value set from the environment via `EnvVar`. e.g. +You can also have the default value set from the environment via `EnvVars`. e.g. ``` go app.Flags = []cli.Flag { cli.StringFlag{ - Name: "lang, l", - Value: "english", - Usage: "language for the greeting", - EnvVar: "APP_LANG", + Name: "lang", + Aliases: []string{"l"}, + Value: "english", + Usage: "language for the greeting", + EnvVars: []string{"APP_LANG"}, }, } ``` -The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default. +If `EnvVars` contains more than one string, the first environment variable that resolves is used as the default. ``` go app.Flags = []cli.Flag { cli.StringFlag{ - Name: "lang, l", - Value: "english", - Usage: "language for the greeting", - EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG", + Name: "lang", + Aliases: []string{"l"}, + Value: "english", + Usage: "language for the greeting", + EnvVars: []string{"LEGACY_COMPAT_LANG", "APP_LANG", "LANG"}, }, } ``` diff --git a/altsrc/flag.go b/altsrc/flag.go index aea142e..6c68bf5 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "strconv" - "strings" "github.com/codegangsta/cli" ) @@ -78,15 +77,15 @@ func NewGenericFlag(flag cli.GenericFlag) *GenericFlag { // ApplyInputSourceValue applies a generic value to the flagSet if required func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { + if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.Generic(f.GenericFlag.Name) if err != nil { return err } if value != nil { - eachName(f.Name, func(name string) { - f.set.Set(f.Name, value.String()) - }) + for _, name := range f.Names() { + f.set.Set(name, value.String()) + } } } } @@ -116,19 +115,19 @@ func NewStringSliceFlag(flag cli.StringSliceFlag) *StringSliceFlag { // ApplyInputSourceValue applies a StringSlice value to the flagSet if required func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { + if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.StringSlice(f.StringSliceFlag.Name) if err != nil { return err } if value != nil { var sliceValue cli.StringSlice = *(cli.NewStringSlice(value...)) - eachName(f.Name, func(name string) { - underlyingFlag := f.set.Lookup(f.Name) + for _, name := range f.Names() { + underlyingFlag := f.set.Lookup(name) if underlyingFlag != nil { underlyingFlag.Value = &sliceValue } - }) + } } } } @@ -157,19 +156,19 @@ func NewIntSliceFlag(flag cli.IntSliceFlag) *IntSliceFlag { // ApplyInputSourceValue applies a IntSlice value if required func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { + if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.IntSlice(f.IntSliceFlag.Name) if err != nil { return err } if value != nil { var sliceValue cli.IntSlice = *(cli.NewIntSlice(value...)) - eachName(f.Name, func(name string) { - underlyingFlag := f.set.Lookup(f.Name) + for _, name := range f.Names() { + underlyingFlag := f.set.Lookup(name) if underlyingFlag != nil { underlyingFlag.Value = &sliceValue } - }) + } } } } @@ -198,15 +197,15 @@ func NewBoolFlag(flag cli.BoolFlag) *BoolFlag { // ApplyInputSourceValue applies a Bool value to the flagSet if required func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { + if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.Bool(f.BoolFlag.Name) if err != nil { return err } if value { - eachName(f.Name, func(name string) { - f.set.Set(f.Name, strconv.FormatBool(value)) - }) + for _, name := range f.Names() { + f.set.Set(name, strconv.FormatBool(value)) + } } } } @@ -235,15 +234,15 @@ func NewStringFlag(flag cli.StringFlag) *StringFlag { // ApplyInputSourceValue applies a String value to the flagSet if required func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { + if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.String(f.StringFlag.Name) if err != nil { return err } if value != "" { - eachName(f.Name, func(name string) { - f.set.Set(f.Name, value) - }) + for _, name := range f.Names() { + f.set.Set(name, value) + } } } } @@ -273,15 +272,15 @@ func NewIntFlag(flag cli.IntFlag) *IntFlag { // ApplyInputSourceValue applies a int value to the flagSet if required func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { + if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Int(f.IntFlag.Name) if err != nil { return err } if value > 0 { - eachName(f.Name, func(name string) { - f.set.Set(f.Name, strconv.FormatInt(int64(value), 10)) - }) + for _, name := range f.Names() { + f.set.Set(name, strconv.FormatInt(int64(value), 10)) + } } } } @@ -310,15 +309,15 @@ func NewDurationFlag(flag cli.DurationFlag) *DurationFlag { // ApplyInputSourceValue applies a Duration value to the flagSet if required func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { + if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Duration(f.DurationFlag.Name) if err != nil { return err } if value > 0 { - eachName(f.Name, func(name string) { - f.set.Set(f.Name, value.String()) - }) + for _, name := range f.Names() { + f.set.Set(name, value.String()) + } } } } @@ -348,16 +347,16 @@ func NewFloat64Flag(flag cli.Float64Flag) *Float64Flag { // ApplyInputSourceValue applies a Float64 value to the flagSet if required func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { + if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Float64(f.Float64Flag.Name) if err != nil { return err } if value > 0 { floatStr := float64ToString(value) - eachName(f.Name, func(name string) { - f.set.Set(f.Name, floatStr) - }) + for _, name := range f.Names() { + f.set.Set(name, floatStr) + } } } } @@ -372,9 +371,8 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) { f.Float64Flag.Apply(set) } -func isEnvVarSet(envVars string) bool { - for _, envVar := range strings.Split(envVars, ",") { - envVar = strings.TrimSpace(envVar) +func isEnvVarSet(envVars []string) bool { + for _, envVar := range envVars { if envVal := os.Getenv(envVar); envVal != "" { // TODO: Can't use this for bools as // set means that it was true or false based on @@ -391,11 +389,3 @@ func isEnvVarSet(envVars string) bool { func float64ToString(f float64) string { return fmt.Sprintf("%v", f) } - -func eachName(longName string, fn func(string)) { - parts := strings.Split(longName, ",") - for _, name := range parts { - name = strings.Trim(name, " ") - fn(name) - } -} diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index be7166f..0b188da 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -46,7 +46,11 @@ func TestGenericApplyInputSourceMethodContextSet(t *testing.T) { func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}, EnvVar: "TEST"}), + Flag: NewGenericFlag(cli.GenericFlag{ + Name: "test", + Value: &Parser{}, + EnvVars: []string{"TEST"}, + }), FlagName: "test", MapValue: &Parser{"efg", "hij"}, EnvVarName: "TEST", @@ -76,7 +80,7 @@ func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) { func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test", EnvVar: "TEST"}), + Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: []string{"hello", "world"}, EnvVarName: "TEST", @@ -106,7 +110,7 @@ func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) { func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test", EnvVar: "TEST"}), + Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: []int{1, 2}, EnvVarName: "TEST", @@ -136,7 +140,7 @@ func TestBoolApplyInputSourceMethodContextSet(t *testing.T) { func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewBoolFlag(cli.BoolFlag{Name: "test", EnvVar: "TEST"}), + Flag: NewBoolFlag(cli.BoolFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: false, EnvVarName: "TEST", @@ -166,7 +170,7 @@ func TestStringApplyInputSourceMethodContextSet(t *testing.T) { func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewStringFlag(cli.StringFlag{Name: "test", EnvVar: "TEST"}), + Flag: NewStringFlag(cli.StringFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: "hello", EnvVarName: "TEST", @@ -196,7 +200,7 @@ func TestIntApplyInputSourceMethodContextSet(t *testing.T) { func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "TEST"}), + Flag: NewIntFlag(cli.IntFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: 15, EnvVarName: "TEST", @@ -226,7 +230,7 @@ func TestDurationApplyInputSourceMethodContextSet(t *testing.T) { func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewDurationFlag(cli.DurationFlag{Name: "test", EnvVar: "TEST"}), + Flag: NewDurationFlag(cli.DurationFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: time.Duration(30 * time.Second), EnvVarName: "TEST", @@ -256,7 +260,7 @@ func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { func TestFloat64ApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewFloat64Flag(cli.Float64Flag{Name: "test", EnvVar: "TEST"}), + Flag: NewFloat64Flag(cli.Float64Flag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: 1.3, EnvVarName: "TEST", diff --git a/altsrc/yaml_command_test.go b/altsrc/yaml_command_test.go index 519bd81..d1a15d7 100644 --- a/altsrc/yaml_command_test.go +++ b/altsrc/yaml_command_test.go @@ -68,7 +68,7 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) { return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}), + NewIntFlag(cli.IntFlag{Name: "test", EnvVars: []string{"THE_TEST"}}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) @@ -103,7 +103,7 @@ func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) { return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "top.test", EnvVar: "THE_TEST"}), + NewIntFlag(cli.IntFlag{Name: "top.test", EnvVars: []string{"THE_TEST"}}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) @@ -268,7 +268,7 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}), + NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVars: []string{"THE_TEST"}}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) @@ -303,7 +303,7 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *tes return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVar: "THE_TEST"}), + NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVars: []string{"THE_TEST"}}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) diff --git a/app.go b/app.go index 066ea7a..622c7a1 100644 --- a/app.go +++ b/app.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "path/filepath" + "reflect" "sort" "time" ) @@ -121,7 +122,7 @@ func (a *App) Setup() { // append help to commands if a.Command(helpCommand.Name) == nil && !a.HideHelp { a.Commands = append(a.Commands, helpCommand) - if (HelpFlag != BoolFlag{}) { + if !reflect.DeepEqual(HelpFlag, BoolFlag{}) { a.appendFlag(HelpFlag) } } @@ -224,7 +225,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { if len(a.Commands) > 0 { if a.Command(helpCommand.Name) == nil && !a.HideHelp { a.Commands = append(a.Commands, helpCommand) - if (HelpFlag != BoolFlag{}) { + if !reflect.DeepEqual(HelpFlag, BoolFlag{}) { a.appendFlag(HelpFlag) } } @@ -379,7 +380,7 @@ func (a *App) VisibleFlags() []Flag { func (a *App) hasFlag(flag Flag) bool { for _, f := range a.Flags { - if flag == f { + if reflect.DeepEqual(flag, f) { return true } } diff --git a/command.go b/command.go index 7a77953..c2f39d0 100644 --- a/command.go +++ b/command.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "io/ioutil" + "reflect" "sort" "strings" ) @@ -69,7 +70,7 @@ func (c Command) Run(ctx *Context) (err error) { return c.startApp(ctx) } - if !c.HideHelp && (HelpFlag != BoolFlag{}) { + if !c.HideHelp && !reflect.DeepEqual(HelpFlag, BoolFlag{}) { // append help to flags c.Flags = append( c.Flags, diff --git a/context.go b/context.go index 45face1..3202c75 100644 --- a/context.go +++ b/context.go @@ -310,7 +310,7 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error { visited[f.Name] = true }) for _, f := range flags { - parts := strings.Split(f.GetName(), ",") + parts := f.Names() if len(parts) == 1 { continue } diff --git a/flag.go b/flag.go index 97e502c..ba55471 100644 --- a/flag.go +++ b/flag.go @@ -24,16 +24,18 @@ var BashCompletionFlag = BoolFlag{ // VersionFlag prints the version for the application var VersionFlag = BoolFlag{ - Name: "version, v", - Usage: "print the version", + Name: "version", + Aliases: []string{"v"}, + Usage: "print the version", } -// HelpFlag prints the help for all commands and subcommands -// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand -// unless HideHelp is set to true) +// HelpFlag prints the help for all commands and subcommands. +// Set to the zero value (BoolFlag{}) to disable the flag. The subcommand +// will still be added unless HideHelp is set to true. var HelpFlag = BoolFlag{ - Name: "help, h", - Usage: "show help", + Name: "help", + Aliases: []string{"h"}, + Usage: "show help", } // FlagStringer converts a flag definition to a string. This is used by help @@ -52,7 +54,7 @@ type Flag interface { fmt.Stringer // Apply Flag settings to the given flag set Apply(*flag.FlagSet) - GetName() string + Names() []string } func flagSet(name string, flags []Flag) *flag.FlagSet { @@ -64,14 +66,6 @@ func flagSet(name string, flags []Flag) *flag.FlagSet { return set } -func eachName(longName string, fn func(string)) { - parts := strings.Split(longName, ",") - for _, name := range parts { - name = strings.Trim(name, " ") - fn(name) - } -} - // Generic is a generic parseable type identified by a specific flag type Generic interface { Set(value string) error @@ -80,11 +74,12 @@ type Generic interface { // GenericFlag is the flag type for types implementing Generic type GenericFlag struct { - Name string - Value Generic - Usage string - EnvVar string - Hidden bool + Name string + Aliases []string + Value Generic + Usage string + EnvVars []string + Hidden bool } // String returns the string representation of the generic flag to display the @@ -98,8 +93,8 @@ func (f GenericFlag) String() string { // provided by the user for parsing by the flag func (f GenericFlag) Apply(set *flag.FlagSet) { val := f.Value - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) if envVal := os.Getenv(envVar); envVal != "" { val.Set(envVal) @@ -108,14 +103,14 @@ func (f GenericFlag) Apply(set *flag.FlagSet) { } } - eachName(f.Name, func(name string) { + for _, name := range f.Names() { set.Var(f.Value, name, f.Usage) - }) + } } -// GetName returns the name of a flag. -func (f GenericFlag) GetName() string { - return f.Name +// Names returns the names of a flag. +func (f GenericFlag) Names() []string { + return append([]string{f.Name}, f.Aliases...) } // StringSlice wraps a []string to satisfy flag.Value @@ -166,11 +161,12 @@ func (f *StringSlice) Value() []string { // StringSliceFlag is a string flag that can be specified multiple times on the // command-line type StringSliceFlag struct { - Name string - Value *StringSlice - Usage string - EnvVar string - Hidden bool + Name string + Aliases []string + Value *StringSlice + Usage string + EnvVars []string + Hidden bool } // String returns the usage @@ -180,8 +176,8 @@ func (f StringSliceFlag) String() string { // Apply populates the flag given the flag set and environment func (f StringSliceFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) if envVal := os.Getenv(envVar); envVal != "" { newVal := NewStringSlice() @@ -199,14 +195,14 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) { f.Value = NewStringSlice() } - eachName(f.Name, func(name string) { + for _, name := range f.Names() { set.Var(f.Value, name, f.Usage) - }) + } } -// GetName returns the name of a flag. -func (f StringSliceFlag) GetName() string { - return f.Name +// Names returns the name of a flag. +func (f StringSliceFlag) Names() []string { + return append([]string{f.Name}, f.Aliases...) } // IntSlice wraps an []int to satisfy flag.Value @@ -272,11 +268,12 @@ func (i *IntSlice) Value() []int { // IntSliceFlag is an int flag that can be specified multiple times on the // command-line type IntSliceFlag struct { - Name string - Value *IntSlice - Usage string - EnvVar string - Hidden bool + Name string + Aliases []string + Value *IntSlice + Usage string + EnvVars []string + Hidden bool } // String returns the usage @@ -286,8 +283,8 @@ func (f IntSliceFlag) String() string { // Apply populates the flag given the flag set and environment func (f IntSliceFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) if envVal := os.Getenv(envVar); envVal != "" { newVal := NewIntSlice() @@ -308,22 +305,23 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) { f.Value = NewIntSlice() } - eachName(f.Name, func(name string) { + for _, name := range f.Names() { set.Var(f.Value, name, f.Usage) - }) + } } -// GetName returns the name of the flag. -func (f IntSliceFlag) GetName() string { - return f.Name +// Names returns the name of the flag. +func (f IntSliceFlag) Names() []string { + return append([]string{f.Name}, f.Aliases...) } // BoolFlag is a switch that defaults to false type BoolFlag struct { Name string + Aliases []string Value bool Usage string - EnvVar string + EnvVars []string Destination *bool Hidden bool } @@ -335,8 +333,8 @@ func (f BoolFlag) String() string { // Apply populates the flag given the flag set and environment func (f BoolFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) if envVal := os.Getenv(envVar); envVal != "" { envValBool, err := strconv.ParseBool(envVal) @@ -348,26 +346,27 @@ func (f BoolFlag) Apply(set *flag.FlagSet) { } } - eachName(f.Name, func(name string) { + for _, name := range f.Names() { if f.Destination != nil { set.BoolVar(f.Destination, name, f.Value, f.Usage) return } set.Bool(name, f.Value, f.Usage) - }) + } } -// GetName returns the name of the flag. -func (f BoolFlag) GetName() string { - return f.Name +// Names returns the name of the flag. +func (f BoolFlag) Names() []string { + return append([]string{f.Name}, f.Aliases...) } // StringFlag represents a flag that takes as string value type StringFlag struct { Name string + Aliases []string Value string Usage string - EnvVar string + EnvVars []string Destination *string Hidden bool } @@ -379,8 +378,8 @@ func (f StringFlag) String() string { // Apply populates the flag given the flag set and environment func (f StringFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) if envVal := os.Getenv(envVar); envVal != "" { f.Value = envVal @@ -389,27 +388,28 @@ func (f StringFlag) Apply(set *flag.FlagSet) { } } - eachName(f.Name, func(name string) { + for _, name := range f.Names() { if f.Destination != nil { set.StringVar(f.Destination, name, f.Value, f.Usage) return } set.String(name, f.Value, f.Usage) - }) + } } -// GetName returns the name of the flag. -func (f StringFlag) GetName() string { - return f.Name +// Names returns the name of the flag. +func (f StringFlag) Names() []string { + return append([]string{f.Name}, f.Aliases...) } // IntFlag is a flag that takes an integer // Errors if the value provided cannot be parsed type IntFlag struct { Name string + Aliases []string Value int Usage string - EnvVar string + EnvVars []string Destination *int Hidden bool } @@ -421,8 +421,8 @@ func (f IntFlag) String() string { // Apply populates the flag given the flag set and environment func (f IntFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) if envVal := os.Getenv(envVar); envVal != "" { envValInt, err := strconv.ParseInt(envVal, 0, 64) @@ -434,27 +434,28 @@ func (f IntFlag) Apply(set *flag.FlagSet) { } } - eachName(f.Name, func(name string) { + for _, name := range f.Names() { if f.Destination != nil { set.IntVar(f.Destination, name, f.Value, f.Usage) return } set.Int(name, f.Value, f.Usage) - }) + } } -// GetName returns the name of the flag. -func (f IntFlag) GetName() string { - return f.Name +// Names returns the name of the flag. +func (f IntFlag) Names() []string { + return append([]string{f.Name}, f.Aliases...) } // DurationFlag is a flag that takes a duration specified in Go's duration // format: https://golang.org/pkg/time/#ParseDuration type DurationFlag struct { Name string + Aliases []string Value time.Duration Usage string - EnvVar string + EnvVars []string Destination *time.Duration Hidden bool } @@ -466,8 +467,8 @@ func (f DurationFlag) String() string { // Apply populates the flag given the flag set and environment func (f DurationFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) if envVal := os.Getenv(envVar); envVal != "" { envValDuration, err := time.ParseDuration(envVal) @@ -479,27 +480,28 @@ func (f DurationFlag) Apply(set *flag.FlagSet) { } } - eachName(f.Name, func(name string) { + for _, name := range f.Names() { if f.Destination != nil { set.DurationVar(f.Destination, name, f.Value, f.Usage) return } set.Duration(name, f.Value, f.Usage) - }) + } } -// GetName returns the name of the flag. -func (f DurationFlag) GetName() string { - return f.Name +// Names returns the name of the flag. +func (f DurationFlag) Names() []string { + return append([]string{f.Name}, f.Aliases...) } // Float64Flag is a flag that takes an float value // Errors if the value provided cannot be parsed type Float64Flag struct { Name string + Aliases []string Value float64 Usage string - EnvVar string + EnvVars []string Destination *float64 Hidden bool } @@ -511,8 +513,8 @@ func (f Float64Flag) String() string { // Apply populates the flag given the flag set and environment func (f Float64Flag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) if envVal := os.Getenv(envVar); envVal != "" { envValFloat, err := strconv.ParseFloat(envVal, 10) @@ -523,18 +525,18 @@ func (f Float64Flag) Apply(set *flag.FlagSet) { } } - eachName(f.Name, func(name string) { + for _, name := range f.Names() { if f.Destination != nil { set.Float64Var(f.Destination, name, f.Value, f.Usage) return } set.Float64(name, f.Value, f.Usage) - }) + } } -// GetName returns the name of the flag. -func (f Float64Flag) GetName() string { - return f.Name +// Names returns the name of the flag. +func (f Float64Flag) Names() []string { + return append([]string{f.Name}, f.Aliases...) } func visibleFlags(fl []Flag) []Flag { @@ -574,25 +576,27 @@ func unquoteUsage(usage string) (string, string) { return "", usage } -func prefixedNames(fullName, placeholder string) string { +func prefixedNames(names []string, placeholder string) string { var prefixed string - parts := strings.Split(fullName, ",") - for i, name := range parts { - name = strings.Trim(name, " ") + for i, name := range names { + if name == "" { + continue + } + prefixed += prefixFor(name) + name if placeholder != "" { prefixed += " " + placeholder } - if i < len(parts)-1 { + if i < len(names)-1 { prefixed += ", " } } return prefixed } -func withEnvHint(envVar, str string) string { +func withEnvHint(envVars []string, str string) string { envText := "" - if envVar != "" { + if envVars != nil && len(envVars) > 0 { prefix := "$" suffix := "" sep := ", $" @@ -601,21 +605,49 @@ func withEnvHint(envVar, str string) string { suffix = "%" sep = "%, %" } - envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(strings.Split(envVar, ","), sep), suffix) + envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(envVars, sep), suffix) } return str + envText } -func stringifyFlag(f Flag) string { +func flagStringSliceField(f Flag, name string) []string { + fv := flagValue(f) + field := fv.FieldByName(name) + + if field.IsValid() { + return field.Interface().([]string) + } + + return []string{} +} + +func flagStringField(f Flag, name string) string { + fv := flagValue(f) + field := fv.FieldByName(name) + + if field.IsValid() { + return field.String() + } + + return "" +} + +func flagValue(f Flag) reflect.Value { fv := reflect.ValueOf(f) + for fv.Kind() == reflect.Ptr { + fv = reflect.Indirect(fv) + } + return fv +} + +func stringifyFlag(f Flag) string { + fv := flagValue(f) switch f.(type) { case IntSliceFlag: - return withEnvHint(fv.FieldByName("EnvVar").String(), - stringifyIntSliceFlag(f.(IntSliceFlag))) + return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyIntSliceFlag(f.(IntSliceFlag))) case StringSliceFlag: - return withEnvHint(fv.FieldByName("EnvVar").String(), - stringifyStringSliceFlag(f.(StringSliceFlag))) + return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyStringSliceFlag(f.(StringSliceFlag))) } placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) @@ -643,8 +675,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 withEnvHint(flagStringSliceField(f, "EnvVars"), + fmt.Sprintf("%s\t%s", prefixedNames(f.Names(), placeholder), usageWithDefault)) } func stringifyIntSliceFlag(f IntSliceFlag) string { @@ -655,7 +687,7 @@ func stringifyIntSliceFlag(f IntSliceFlag) string { } } - return stringifySliceFlag(f.Usage, f.Name, defaultVals) + return stringifySliceFlag(f.Usage, append([]string{f.Name}, f.Aliases...), defaultVals) } func stringifyStringSliceFlag(f StringSliceFlag) string { @@ -668,10 +700,10 @@ func stringifyStringSliceFlag(f StringSliceFlag) string { } } - return stringifySliceFlag(f.Usage, f.Name, defaultVals) + return stringifySliceFlag(f.Usage, append([]string{f.Name}, f.Aliases...), defaultVals) } -func stringifySliceFlag(usage, name string, defaultVals []string) string { +func stringifySliceFlag(usage string, names, defaultVals []string) string { placeholder, usage := unquoteUsage(usage) if placeholder == "" { placeholder = defaultPlaceholder @@ -683,5 +715,15 @@ 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", prefixedNames(names, placeholder), usageWithDefault) +} + +func hasFlag(flags []Flag, fl Flag) bool { + for _, existing := range flags { + if fl == existing { + return true + } + } + + return false } diff --git a/flag_test.go b/flag_test.go index a3414cf..8ff64fb 100644 --- a/flag_test.go +++ b/flag_test.go @@ -31,21 +31,22 @@ func TestBoolFlagHelpOutput(t *testing.T) { var stringFlagTests = []struct { name string + aliases []string usage string value string expected string }{ - {"foo", "", "", "--foo value\t"}, - {"f", "", "", "-f value\t"}, - {"f", "The total `foo` desired", "all", "-f foo\tThe total foo desired (default: \"all\")"}, - {"test", "", "Something", "--test value\t(default: \"Something\")"}, - {"config,c", "Load configuration from `FILE`", "", "--config FILE, -c FILE\tLoad configuration from FILE"}, - {"config,c", "Load configuration from `CONFIG`", "config.json", "--config CONFIG, -c CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"}, + {"foo", nil, "", "", "--foo value\t"}, + {"f", nil, "", "", "-f value\t"}, + {"f", nil, "The total `foo` desired", "all", "-f foo\tThe total foo desired (default: \"all\")"}, + {"test", nil, "", "Something", "--test value\t(default: \"Something\")"}, + {"config", []string{"c"}, "Load configuration from `FILE`", "", "--config FILE, -c FILE\tLoad configuration from FILE"}, + {"config", []string{"c"}, "Load configuration from `CONFIG`", "config.json", "--config CONFIG, -c CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"}, } func TestStringFlagHelpOutput(t *testing.T) { for _, test := range stringFlagTests { - flag := StringFlag{Name: test.name, Usage: test.usage, Value: test.value} + flag := StringFlag{Name: test.name, Aliases: test.aliases, Usage: test.usage, Value: test.value} output := flag.String() if output != test.expected { @@ -58,7 +59,7 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_FOO", "derp") for _, test := range stringFlagTests { - flag := StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"} + flag := StringFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_FOO"}} output := flag.String() expectedSuffix := " [$APP_FOO]" @@ -73,19 +74,20 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { var stringSliceFlagTests = []struct { name string + aliases []string value *StringSlice expected string }{ - {"foo", NewStringSlice(""), "--foo value\t"}, - {"f", NewStringSlice(""), "-f value\t"}, - {"f", NewStringSlice("Lipstick"), "-f value\t(default: \"Lipstick\")"}, - {"test", NewStringSlice("Something"), "--test value\t(default: \"Something\")"}, - {"d, dee", NewStringSlice("Inka", "Dinka", "dooo"), "-d value, --dee value\t(default: \"Inka\", \"Dinka\", \"dooo\")"}, + {"foo", nil, NewStringSlice(""), "--foo value\t"}, + {"f", nil, NewStringSlice(""), "-f value\t"}, + {"f", nil, NewStringSlice("Lipstick"), "-f value\t(default: \"Lipstick\")"}, + {"test", nil, NewStringSlice("Something"), "--test value\t(default: \"Something\")"}, + {"dee", []string{"d"}, NewStringSlice("Inka", "Dinka", "dooo"), "--dee value, -d value\t(default: \"Inka\", \"Dinka\", \"dooo\")"}, } func TestStringSliceFlagHelpOutput(t *testing.T) { for _, test := range stringSliceFlagTests { - flag := StringSliceFlag{Name: test.name, Value: test.value} + flag := StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value} output := flag.String() if output != test.expected { @@ -98,7 +100,7 @@ func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_QWWX", "11,4") for _, test := range stringSliceFlagTests { - flag := StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"} + flag := StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_QWWX"}} output := flag.String() expectedSuffix := " [$APP_QWWX]" @@ -134,7 +136,7 @@ func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_BAR", "2") for _, test := range intFlagTests { - flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} + flag := IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := flag.String() expectedSuffix := " [$APP_BAR]" @@ -170,7 +172,7 @@ func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_BAR", "2h3m6s") for _, test := range durationFlagTests { - flag := DurationFlag{Name: test.name, EnvVar: "APP_BAR"} + flag := DurationFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := flag.String() expectedSuffix := " [$APP_BAR]" @@ -185,17 +187,18 @@ func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { var intSliceFlagTests = []struct { name string + aliases []string value *IntSlice expected string }{ - {"heads", NewIntSlice(), "--heads value\t"}, - {"H", NewIntSlice(), "-H value\t"}, - {"H, heads", NewIntSlice(9, 3), "-H value, --heads value\t(default: 9, 3)"}, + {"heads", nil, NewIntSlice(), "--heads value\t"}, + {"H", nil, NewIntSlice(), "-H value\t"}, + {"H", []string{"heads"}, NewIntSlice(9, 3), "-H value, --heads value\t(default: 9, 3)"}, } func TestIntSliceFlagHelpOutput(t *testing.T) { for _, test := range intSliceFlagTests { - flag := IntSliceFlag{Name: test.name, Value: test.value} + flag := IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value} output := flag.String() if output != test.expected { @@ -208,7 +211,7 @@ func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_SMURF", "42,3") for _, test := range intSliceFlagTests { - flag := IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} + flag := IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_SMURF"}} output := flag.String() expectedSuffix := " [$APP_SMURF]" @@ -244,7 +247,7 @@ func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_BAZ", "99.4") for _, test := range float64FlagTests { - flag := Float64Flag{Name: test.name, EnvVar: "APP_BAZ"} + flag := Float64Flag{Name: test.name, EnvVars: []string{"APP_BAZ"}} output := flag.String() expectedSuffix := " [$APP_BAZ]" @@ -281,7 +284,7 @@ func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_ZAP", "3") for _, test := range genericFlagTests { - flag := GenericFlag{Name: test.name, EnvVar: "APP_ZAP"} + flag := GenericFlag{Name: test.name, EnvVars: []string{"APP_ZAP"}} output := flag.String() expectedSuffix := " [$APP_ZAP]" @@ -297,7 +300,7 @@ func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { func TestParseMultiString(t *testing.T) { (&App{ Flags: []Flag{ - StringFlag{Name: "serve, s"}, + StringFlag{Name: "serve", Aliases: []string{"s"}}, }, Action: func(ctx *Context) error { if ctx.String("serve") != "10" { @@ -335,7 +338,7 @@ func TestParseMultiStringFromEnv(t *testing.T) { os.Setenv("APP_COUNT", "20") (&App{ Flags: []Flag{ - StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, + StringFlag{Name: "count", Aliases: []string{"c"}, EnvVars: []string{"APP_COUNT"}}, }, Action: func(ctx *Context) error { if ctx.String("count") != "20" { @@ -354,7 +357,7 @@ func TestParseMultiStringFromEnvCascade(t *testing.T) { os.Setenv("APP_COUNT", "20") (&App{ Flags: []Flag{ - StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"}, + StringFlag{Name: "count", Aliases: []string{"c"}, EnvVars: []string{"COMPAT_COUNT", "APP_COUNT"}}, }, Action: func(ctx *Context) error { if ctx.String("count") != "20" { @@ -371,7 +374,7 @@ func TestParseMultiStringFromEnvCascade(t *testing.T) { func TestParseMultiStringSlice(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "serve, s", Value: NewStringSlice()}, + StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice()}, }, Action: func(ctx *Context) error { expected := []string{"10", "20"} @@ -389,7 +392,7 @@ func TestParseMultiStringSlice(t *testing.T) { func TestParseMultiStringSliceWithDefaults(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "serve, s", Value: NewStringSlice("9", "2")}, + StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice("9", "2")}, }, Action: func(ctx *Context) error { expected := []string{"10", "20"} @@ -407,7 +410,7 @@ func TestParseMultiStringSliceWithDefaults(t *testing.T) { func TestParseMultiStringSliceWithDefaultsUnset(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "serve, s", Value: NewStringSlice("9", "2")}, + StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice("9", "2")}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"9", "2"}) { @@ -427,7 +430,7 @@ func TestParseMultiStringSliceFromEnv(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "intervals, i", Value: NewStringSlice(), EnvVar: "APP_INTERVALS"}, + StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice(), EnvVars: []string{"APP_INTERVALS"}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { @@ -447,7 +450,7 @@ func TestParseMultiStringSliceFromEnvWithDefaults(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "intervals, i", Value: NewStringSlice("1", "2", "5"), EnvVar: "APP_INTERVALS"}, + StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice("1", "2", "5"), EnvVars: []string{"APP_INTERVALS"}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { @@ -467,7 +470,7 @@ func TestParseMultiStringSliceFromEnvCascade(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "intervals, i", Value: NewStringSlice(), EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, + StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice(), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { @@ -487,7 +490,7 @@ func TestParseMultiStringSliceFromEnvCascadeWithDefaults(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "intervals, i", Value: NewStringSlice("1", "2", "5"), EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, + StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice("1", "2", "5"), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { @@ -504,7 +507,7 @@ func TestParseMultiStringSliceFromEnvCascadeWithDefaults(t *testing.T) { func TestParseMultiInt(t *testing.T) { a := App{ Flags: []Flag{ - IntFlag{Name: "serve, s"}, + IntFlag{Name: "serve", Aliases: []string{"s"}}, }, Action: func(ctx *Context) error { if ctx.Int("serve") != 10 { @@ -543,7 +546,7 @@ func TestParseMultiIntFromEnv(t *testing.T) { os.Setenv("APP_TIMEOUT_SECONDS", "10") a := App{ Flags: []Flag{ - IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, + IntFlag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"APP_TIMEOUT_SECONDS"}}, }, Action: func(ctx *Context) error { if ctx.Int("timeout") != 10 { @@ -563,7 +566,7 @@ func TestParseMultiIntFromEnvCascade(t *testing.T) { os.Setenv("APP_TIMEOUT_SECONDS", "10") a := App{ Flags: []Flag{ - IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, + IntFlag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"COMPAT_TIMEOUT_SECONDS", "APP_TIMEOUT_SECONDS"}}, }, Action: func(ctx *Context) error { if ctx.Int("timeout") != 10 { @@ -581,7 +584,7 @@ func TestParseMultiIntFromEnvCascade(t *testing.T) { func TestParseMultiIntSlice(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "serve, s", Value: NewIntSlice()}, + IntSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewIntSlice()}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { @@ -598,7 +601,7 @@ func TestParseMultiIntSlice(t *testing.T) { func TestParseMultiIntSliceWithDefaults(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "serve, s", Value: NewIntSlice(9, 2)}, + IntSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewIntSlice(9, 2)}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { @@ -615,7 +618,7 @@ func TestParseMultiIntSliceWithDefaults(t *testing.T) { func TestParseMultiIntSliceWithDefaultsUnset(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "serve, s", Value: NewIntSlice(9, 2)}, + IntSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewIntSlice(9, 2)}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{9, 2}) { @@ -635,7 +638,7 @@ func TestParseMultiIntSliceFromEnv(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "intervals, i", Value: NewIntSlice(), EnvVar: "APP_INTERVALS"}, + IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(), EnvVars: []string{"APP_INTERVALS"}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { @@ -655,7 +658,7 @@ func TestParseMultiIntSliceFromEnvWithDefaults(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "intervals, i", Value: NewIntSlice(1, 2, 5), EnvVar: "APP_INTERVALS"}, + IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(1, 2, 5), EnvVars: []string{"APP_INTERVALS"}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { @@ -675,7 +678,7 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "intervals, i", Value: NewIntSlice(), EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, + IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { @@ -692,7 +695,7 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { func TestParseMultiFloat64(t *testing.T) { a := App{ Flags: []Flag{ - Float64Flag{Name: "serve, s"}, + Float64Flag{Name: "serve", Aliases: []string{"s"}}, }, Action: func(ctx *Context) error { if ctx.Float64("serve") != 10.2 { @@ -731,7 +734,7 @@ func TestParseMultiFloat64FromEnv(t *testing.T) { os.Setenv("APP_TIMEOUT_SECONDS", "15.5") a := App{ Flags: []Flag{ - Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, + Float64Flag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"APP_TIMEOUT_SECONDS"}}, }, Action: func(ctx *Context) error { if ctx.Float64("timeout") != 15.5 { @@ -751,7 +754,7 @@ func TestParseMultiFloat64FromEnvCascade(t *testing.T) { os.Setenv("APP_TIMEOUT_SECONDS", "15.5") a := App{ Flags: []Flag{ - Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, + Float64Flag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"COMPAT_TIMEOUT_SECONDS", "APP_TIMEOUT_SECONDS"}}, }, Action: func(ctx *Context) error { if ctx.Float64("timeout") != 15.5 { @@ -769,7 +772,7 @@ func TestParseMultiFloat64FromEnvCascade(t *testing.T) { func TestParseMultiBool(t *testing.T) { a := App{ Flags: []Flag{ - BoolFlag{Name: "serve, s"}, + BoolFlag{Name: "serve", Aliases: []string{"s"}}, }, Action: func(ctx *Context) error { if ctx.Bool("serve") != true { @@ -808,7 +811,7 @@ func TestParseMultiBoolFromEnv(t *testing.T) { os.Setenv("APP_DEBUG", "1") a := App{ Flags: []Flag{ - BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, + BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"APP_DEBUG"}}, }, Action: func(ctx *Context) error { if ctx.Bool("debug") != true { @@ -828,7 +831,7 @@ func TestParseMultiBoolFromEnvCascade(t *testing.T) { os.Setenv("APP_DEBUG", "1") a := App{ Flags: []Flag{ - BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, + BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"COMPAT_DEBUG", "APP_DEBUG"}}, }, Action: func(ctx *Context) error { if ctx.Bool("debug") != true { @@ -846,7 +849,7 @@ func TestParseMultiBoolFromEnvCascade(t *testing.T) { func TestParseMultiBoolTrue(t *testing.T) { a := App{ Flags: []Flag{ - BoolFlag{Name: "implode, i", Value: true}, + BoolFlag{Name: "implode", Aliases: []string{"i"}, Value: true}, }, Action: func(ctx *Context) error { if ctx.Bool("implode") { @@ -888,9 +891,10 @@ func TestParseMultiBoolTrueFromEnv(t *testing.T) { a := App{ Flags: []Flag{ BoolFlag{ - Name: "debug, d", - Value: true, - EnvVar: "APP_DEBUG", + Name: "debug", + Aliases: []string{"d"}, + Value: true, + EnvVars: []string{"APP_DEBUG"}, }, }, Action: func(ctx *Context) error { @@ -912,9 +916,10 @@ func TestParseMultiBoolTrueFromEnvCascade(t *testing.T) { a := App{ Flags: []Flag{ BoolFlag{ - Name: "debug, d", - Value: true, - EnvVar: "COMPAT_DEBUG,APP_DEBUG", + Name: "debug", + Aliases: []string{"d"}, + Value: true, + EnvVars: []string{"COMPAT_DEBUG", "APP_DEBUG"}, }, }, Action: func(ctx *Context) error { @@ -951,7 +956,7 @@ func (p *Parser) String() string { func TestParseGeneric(t *testing.T) { a := App{ Flags: []Flag{ - GenericFlag{Name: "serve, s", Value: &Parser{}}, + GenericFlag{Name: "serve", Aliases: []string{"s"}, Value: &Parser{}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { @@ -971,7 +976,12 @@ func TestParseGenericFromEnv(t *testing.T) { os.Setenv("APP_SERVE", "20,30") a := App{ Flags: []Flag{ - GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"}, + GenericFlag{ + Name: "serve", + Aliases: []string{"s"}, + Value: &Parser{}, + EnvVars: []string{"APP_SERVE"}, + }, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) { @@ -991,7 +1001,11 @@ func TestParseGenericFromEnvCascade(t *testing.T) { os.Setenv("APP_FOO", "99,2000") a := App{ Flags: []Flag{ - GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"}, + GenericFlag{ + Name: "foos", + Value: &Parser{}, + EnvVars: []string{"COMPAT_FOO", "APP_FOO"}, + }, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) { diff --git a/help.go b/help.go index 893b1be..2675289 100644 --- a/help.go +++ b/help.go @@ -208,11 +208,11 @@ func printHelp(out io.Writer, templ string, data interface{}) { func checkVersion(c *Context) bool { found := false if VersionFlag.Name != "" { - eachName(VersionFlag.Name, func(name string) { + for _, name := range VersionFlag.Names() { if c.Bool(name) { found = true } - }) + } } return found } @@ -220,11 +220,11 @@ func checkVersion(c *Context) bool { func checkHelp(c *Context) bool { found := false if HelpFlag.Name != "" { - eachName(HelpFlag.Name, func(name string) { + for _, name := range HelpFlag.Names() { if c.Bool(name) { found = true } - }) + } } return found } diff --git a/help_test.go b/help_test.go index d19bc4c..9ca4c0d 100644 --- a/help_test.go +++ b/help_test.go @@ -60,13 +60,14 @@ func Test_Help_Custom_Flags(t *testing.T) { }() HelpFlag = BoolFlag{ - Name: "help, x", - Usage: "show help", + Name: "help", + Aliases: []string{"x"}, + Usage: "show help", } app := App{ Flags: []Flag{ - BoolFlag{Name: "foo, h"}, + BoolFlag{Name: "foo", Aliases: []string{"h"}}, }, Action: func(ctx *Context) error { if ctx.Bool("h") != true { @@ -90,13 +91,14 @@ func Test_Version_Custom_Flags(t *testing.T) { }() VersionFlag = BoolFlag{ - Name: "version, V", - Usage: "show version", + Name: "version", + Aliases: []string{"V"}, + Usage: "show version", } app := App{ Flags: []Flag{ - BoolFlag{Name: "foo, v"}, + BoolFlag{Name: "foo", Aliases: []string{"v"}}, }, Action: func(ctx *Context) error { if ctx.Bool("v") != true { From 334e66cb8f24be3190fded4ad5952a07244fbc8b Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 22 May 2016 10:59:42 -0400 Subject: [PATCH 2/2] Remove unused (so far) func --- flag.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/flag.go b/flag.go index ba55471..067e9ee 100644 --- a/flag.go +++ b/flag.go @@ -717,13 +717,3 @@ func stringifySliceFlag(usage string, names, defaultVals []string) string { usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) return fmt.Sprintf("%s\t%s", prefixedNames(names, placeholder), usageWithDefault) } - -func hasFlag(flags []Flag, fl Flag) bool { - for _, existing := range flags { - if fl == existing { - return true - } - } - - return false -}