Merge pull request #419 from codegangsta/remove-stringlies

Replace all "stringly typed" fields with slice equivalents
This commit is contained in:
Jesse Szwedko 2016-05-22 08:12:24 -07:00
commit b35c8a92d2
11 changed files with 304 additions and 256 deletions

View File

@ -265,8 +265,9 @@ For example this:
```go ```go
cli.StringFlag{ cli.StringFlag{
Name: "config, c", Name: "config",
Usage: "Load configuration from `FILE`", 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 ``` go
app.Flags = []cli.Flag { app.Flags = []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "lang, l", Name: "lang",
Value: "english", Aliases: []string{"l"},
Usage: "language for the greeting", 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 #### 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 ``` go
app.Flags = []cli.Flag { app.Flags = []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "lang, l", Name: "lang",
Value: "english", Aliases: []string{"l"},
Usage: "language for the greeting", Value: "english",
EnvVar: "APP_LANG", 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 ``` go
app.Flags = []cli.Flag { app.Flags = []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "lang, l", Name: "lang",
Value: "english", Aliases: []string{"l"},
Usage: "language for the greeting", Value: "english",
EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG", Usage: "language for the greeting",
EnvVars: []string{"LEGACY_COMPAT_LANG", "APP_LANG", "LANG"},
}, },
} }
``` ```

View File

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"os" "os"
"strconv" "strconv"
"strings"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )
@ -78,15 +77,15 @@ func NewGenericFlag(flag cli.GenericFlag) *GenericFlag {
// ApplyInputSourceValue applies a generic value to the flagSet if required // ApplyInputSourceValue applies a generic value to the flagSet if required
func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
if f.set != nil { 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) value, err := isc.Generic(f.GenericFlag.Name)
if err != nil { if err != nil {
return err return err
} }
if value != nil { if value != nil {
eachName(f.Name, func(name string) { for _, name := range f.Names() {
f.set.Set(f.Name, value.String()) 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 // ApplyInputSourceValue applies a StringSlice value to the flagSet if required
func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
if f.set != nil { 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) value, err := isc.StringSlice(f.StringSliceFlag.Name)
if err != nil { if err != nil {
return err return err
} }
if value != nil { if value != nil {
var sliceValue cli.StringSlice = *(cli.NewStringSlice(value...)) var sliceValue cli.StringSlice = *(cli.NewStringSlice(value...))
eachName(f.Name, func(name string) { for _, name := range f.Names() {
underlyingFlag := f.set.Lookup(f.Name) underlyingFlag := f.set.Lookup(name)
if underlyingFlag != nil { if underlyingFlag != nil {
underlyingFlag.Value = &sliceValue underlyingFlag.Value = &sliceValue
} }
}) }
} }
} }
} }
@ -157,19 +156,19 @@ func NewIntSliceFlag(flag cli.IntSliceFlag) *IntSliceFlag {
// ApplyInputSourceValue applies a IntSlice value if required // ApplyInputSourceValue applies a IntSlice value if required
func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
if f.set != nil { 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) value, err := isc.IntSlice(f.IntSliceFlag.Name)
if err != nil { if err != nil {
return err return err
} }
if value != nil { if value != nil {
var sliceValue cli.IntSlice = *(cli.NewIntSlice(value...)) var sliceValue cli.IntSlice = *(cli.NewIntSlice(value...))
eachName(f.Name, func(name string) { for _, name := range f.Names() {
underlyingFlag := f.set.Lookup(f.Name) underlyingFlag := f.set.Lookup(name)
if underlyingFlag != nil { if underlyingFlag != nil {
underlyingFlag.Value = &sliceValue underlyingFlag.Value = &sliceValue
} }
}) }
} }
} }
} }
@ -198,15 +197,15 @@ func NewBoolFlag(flag cli.BoolFlag) *BoolFlag {
// ApplyInputSourceValue applies a Bool value to the flagSet if required // ApplyInputSourceValue applies a Bool value to the flagSet if required
func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
if f.set != nil { 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) value, err := isc.Bool(f.BoolFlag.Name)
if err != nil { if err != nil {
return err return err
} }
if value { if value {
eachName(f.Name, func(name string) { for _, name := range f.Names() {
f.set.Set(f.Name, strconv.FormatBool(value)) 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 // ApplyInputSourceValue applies a String value to the flagSet if required
func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
if f.set != nil { 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) value, err := isc.String(f.StringFlag.Name)
if err != nil { if err != nil {
return err return err
} }
if value != "" { if value != "" {
eachName(f.Name, func(name string) { for _, name := range f.Names() {
f.set.Set(f.Name, value) 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 // ApplyInputSourceValue applies a int value to the flagSet if required
func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
if f.set != nil { 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) value, err := isc.Int(f.IntFlag.Name)
if err != nil { if err != nil {
return err return err
} }
if value > 0 { if value > 0 {
eachName(f.Name, func(name string) { for _, name := range f.Names() {
f.set.Set(f.Name, strconv.FormatInt(int64(value), 10)) 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 // ApplyInputSourceValue applies a Duration value to the flagSet if required
func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
if f.set != nil { 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) value, err := isc.Duration(f.DurationFlag.Name)
if err != nil { if err != nil {
return err return err
} }
if value > 0 { if value > 0 {
eachName(f.Name, func(name string) { for _, name := range f.Names() {
f.set.Set(f.Name, value.String()) 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 // ApplyInputSourceValue applies a Float64 value to the flagSet if required
func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
if f.set != nil { 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) value, err := isc.Float64(f.Float64Flag.Name)
if err != nil { if err != nil {
return err return err
} }
if value > 0 { if value > 0 {
floatStr := float64ToString(value) floatStr := float64ToString(value)
eachName(f.Name, func(name string) { for _, name := range f.Names() {
f.set.Set(f.Name, floatStr) f.set.Set(name, floatStr)
}) }
} }
} }
} }
@ -372,9 +371,8 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) {
f.Float64Flag.Apply(set) f.Float64Flag.Apply(set)
} }
func isEnvVarSet(envVars string) bool { func isEnvVarSet(envVars []string) bool {
for _, envVar := range strings.Split(envVars, ",") { for _, envVar := range envVars {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { if envVal := os.Getenv(envVar); envVal != "" {
// TODO: Can't use this for bools as // TODO: Can't use this for bools as
// set means that it was true or false based on // set means that it was true or false based on
@ -391,11 +389,3 @@ func isEnvVarSet(envVars string) bool {
func float64ToString(f float64) string { func float64ToString(f float64) string {
return fmt.Sprintf("%v", f) 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)
}
}

View File

@ -46,7 +46,11 @@ func TestGenericApplyInputSourceMethodContextSet(t *testing.T) {
func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) {
c := runTest(t, testApplyInputSource{ 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", FlagName: "test",
MapValue: &Parser{"efg", "hij"}, MapValue: &Parser{"efg", "hij"},
EnvVarName: "TEST", EnvVarName: "TEST",
@ -76,7 +80,7 @@ func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) {
func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) {
c := runTest(t, testApplyInputSource{ c := runTest(t, testApplyInputSource{
Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test", EnvVar: "TEST"}), Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test", EnvVars: []string{"TEST"}}),
FlagName: "test", FlagName: "test",
MapValue: []string{"hello", "world"}, MapValue: []string{"hello", "world"},
EnvVarName: "TEST", EnvVarName: "TEST",
@ -106,7 +110,7 @@ func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) {
func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) {
c := runTest(t, testApplyInputSource{ c := runTest(t, testApplyInputSource{
Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test", EnvVar: "TEST"}), Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test", EnvVars: []string{"TEST"}}),
FlagName: "test", FlagName: "test",
MapValue: []int{1, 2}, MapValue: []int{1, 2},
EnvVarName: "TEST", EnvVarName: "TEST",
@ -136,7 +140,7 @@ func TestBoolApplyInputSourceMethodContextSet(t *testing.T) {
func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) {
c := runTest(t, testApplyInputSource{ c := runTest(t, testApplyInputSource{
Flag: NewBoolFlag(cli.BoolFlag{Name: "test", EnvVar: "TEST"}), Flag: NewBoolFlag(cli.BoolFlag{Name: "test", EnvVars: []string{"TEST"}}),
FlagName: "test", FlagName: "test",
MapValue: false, MapValue: false,
EnvVarName: "TEST", EnvVarName: "TEST",
@ -166,7 +170,7 @@ func TestStringApplyInputSourceMethodContextSet(t *testing.T) {
func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) {
c := runTest(t, testApplyInputSource{ c := runTest(t, testApplyInputSource{
Flag: NewStringFlag(cli.StringFlag{Name: "test", EnvVar: "TEST"}), Flag: NewStringFlag(cli.StringFlag{Name: "test", EnvVars: []string{"TEST"}}),
FlagName: "test", FlagName: "test",
MapValue: "hello", MapValue: "hello",
EnvVarName: "TEST", EnvVarName: "TEST",
@ -196,7 +200,7 @@ func TestIntApplyInputSourceMethodContextSet(t *testing.T) {
func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) {
c := runTest(t, testApplyInputSource{ c := runTest(t, testApplyInputSource{
Flag: NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "TEST"}), Flag: NewIntFlag(cli.IntFlag{Name: "test", EnvVars: []string{"TEST"}}),
FlagName: "test", FlagName: "test",
MapValue: 15, MapValue: 15,
EnvVarName: "TEST", EnvVarName: "TEST",
@ -226,7 +230,7 @@ func TestDurationApplyInputSourceMethodContextSet(t *testing.T) {
func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) {
c := runTest(t, testApplyInputSource{ c := runTest(t, testApplyInputSource{
Flag: NewDurationFlag(cli.DurationFlag{Name: "test", EnvVar: "TEST"}), Flag: NewDurationFlag(cli.DurationFlag{Name: "test", EnvVars: []string{"TEST"}}),
FlagName: "test", FlagName: "test",
MapValue: time.Duration(30 * time.Second), MapValue: time.Duration(30 * time.Second),
EnvVarName: "TEST", EnvVarName: "TEST",
@ -256,7 +260,7 @@ func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) {
func TestFloat64ApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestFloat64ApplyInputSourceMethodEnvVarSet(t *testing.T) {
c := runTest(t, testApplyInputSource{ c := runTest(t, testApplyInputSource{
Flag: NewFloat64Flag(cli.Float64Flag{Name: "test", EnvVar: "TEST"}), Flag: NewFloat64Flag(cli.Float64Flag{Name: "test", EnvVars: []string{"TEST"}}),
FlagName: "test", FlagName: "test",
MapValue: 1.3, MapValue: 1.3,
EnvVarName: "TEST", EnvVarName: "TEST",

View File

@ -68,7 +68,7 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) {
return nil return nil
}, },
Flags: []cli.Flag{ Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}), NewIntFlag(cli.IntFlag{Name: "test", EnvVars: []string{"THE_TEST"}}),
cli.StringFlag{Name: "load"}}, cli.StringFlag{Name: "load"}},
} }
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
@ -103,7 +103,7 @@ func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) {
return nil return nil
}, },
Flags: []cli.Flag{ 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"}}, cli.StringFlag{Name: "load"}},
} }
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
@ -268,7 +268,7 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T
return nil return nil
}, },
Flags: []cli.Flag{ 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"}}, cli.StringFlag{Name: "load"}},
} }
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
@ -303,7 +303,7 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *tes
return nil return nil
}, },
Flags: []cli.Flag{ 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"}}, cli.StringFlag{Name: "load"}},
} }
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))

7
app.go
View File

@ -6,6 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"sort" "sort"
"time" "time"
) )
@ -121,7 +122,7 @@ func (a *App) Setup() {
// append help to commands // append help to commands
if a.Command(helpCommand.Name) == nil && !a.HideHelp { if a.Command(helpCommand.Name) == nil && !a.HideHelp {
a.Commands = append(a.Commands, helpCommand) a.Commands = append(a.Commands, helpCommand)
if (HelpFlag != BoolFlag{}) { if !reflect.DeepEqual(HelpFlag, BoolFlag{}) {
a.appendFlag(HelpFlag) a.appendFlag(HelpFlag)
} }
} }
@ -224,7 +225,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
if len(a.Commands) > 0 { if len(a.Commands) > 0 {
if a.Command(helpCommand.Name) == nil && !a.HideHelp { if a.Command(helpCommand.Name) == nil && !a.HideHelp {
a.Commands = append(a.Commands, helpCommand) a.Commands = append(a.Commands, helpCommand)
if (HelpFlag != BoolFlag{}) { if !reflect.DeepEqual(HelpFlag, BoolFlag{}) {
a.appendFlag(HelpFlag) a.appendFlag(HelpFlag)
} }
} }
@ -379,7 +380,7 @@ func (a *App) VisibleFlags() []Flag {
func (a *App) hasFlag(flag Flag) bool { func (a *App) hasFlag(flag Flag) bool {
for _, f := range a.Flags { for _, f := range a.Flags {
if flag == f { if reflect.DeepEqual(flag, f) {
return true return true
} }
} }

View File

@ -3,6 +3,7 @@ package cli
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"reflect"
"sort" "sort"
"strings" "strings"
) )
@ -69,7 +70,7 @@ func (c Command) Run(ctx *Context) (err error) {
return c.startApp(ctx) return c.startApp(ctx)
} }
if !c.HideHelp && (HelpFlag != BoolFlag{}) { if !c.HideHelp && !reflect.DeepEqual(HelpFlag, BoolFlag{}) {
// append help to flags // append help to flags
c.Flags = append( c.Flags = append(
c.Flags, c.Flags,

View File

@ -310,7 +310,7 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
visited[f.Name] = true visited[f.Name] = true
}) })
for _, f := range flags { for _, f := range flags {
parts := strings.Split(f.GetName(), ",") parts := f.Names()
if len(parts) == 1 { if len(parts) == 1 {
continue continue
} }

254
flag.go
View File

@ -24,16 +24,18 @@ var BashCompletionFlag = BoolFlag{
// VersionFlag prints the version for the application // VersionFlag prints the version for the application
var VersionFlag = BoolFlag{ var VersionFlag = BoolFlag{
Name: "version, v", Name: "version",
Usage: "print the version", Aliases: []string{"v"},
Usage: "print the version",
} }
// HelpFlag prints the help for all commands and subcommands // HelpFlag prints the help for all commands and subcommands.
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand // Set to the zero value (BoolFlag{}) to disable the flag. The subcommand
// unless HideHelp is set to true) // will still be added unless HideHelp is set to true.
var HelpFlag = BoolFlag{ var HelpFlag = BoolFlag{
Name: "help, h", Name: "help",
Usage: "show help", Aliases: []string{"h"},
Usage: "show help",
} }
// FlagStringer converts a flag definition to a string. This is used by help // FlagStringer converts a flag definition to a string. This is used by help
@ -52,7 +54,7 @@ type Flag interface {
fmt.Stringer fmt.Stringer
// Apply Flag settings to the given flag set // Apply Flag settings to the given flag set
Apply(*flag.FlagSet) Apply(*flag.FlagSet)
GetName() string Names() []string
} }
func flagSet(name string, flags []Flag) *flag.FlagSet { func flagSet(name string, flags []Flag) *flag.FlagSet {
@ -64,14 +66,6 @@ func flagSet(name string, flags []Flag) *flag.FlagSet {
return set 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 // Generic is a generic parseable type identified by a specific flag
type Generic interface { type Generic interface {
Set(value string) error Set(value string) error
@ -80,11 +74,12 @@ type Generic interface {
// GenericFlag is the flag type for types implementing Generic // GenericFlag is the flag type for types implementing Generic
type GenericFlag struct { type GenericFlag struct {
Name string Name string
Value Generic Aliases []string
Usage string Value Generic
EnvVar string Usage string
Hidden bool EnvVars []string
Hidden bool
} }
// String returns the string representation of the generic flag to display the // 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 // provided by the user for parsing by the flag
func (f GenericFlag) Apply(set *flag.FlagSet) { func (f GenericFlag) Apply(set *flag.FlagSet) {
val := f.Value val := f.Value
if f.EnvVar != "" { if f.EnvVars != nil {
for _, envVar := range strings.Split(f.EnvVar, ",") { for _, envVar := range f.EnvVars {
envVar = strings.TrimSpace(envVar) envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { if envVal := os.Getenv(envVar); envVal != "" {
val.Set(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) set.Var(f.Value, name, f.Usage)
}) }
} }
// GetName returns the name of a flag. // Names returns the names of a flag.
func (f GenericFlag) GetName() string { func (f GenericFlag) Names() []string {
return f.Name return append([]string{f.Name}, f.Aliases...)
} }
// StringSlice wraps a []string to satisfy flag.Value // 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 // StringSliceFlag is a string flag that can be specified multiple times on the
// command-line // command-line
type StringSliceFlag struct { type StringSliceFlag struct {
Name string Name string
Value *StringSlice Aliases []string
Usage string Value *StringSlice
EnvVar string Usage string
Hidden bool EnvVars []string
Hidden bool
} }
// String returns the usage // String returns the usage
@ -180,8 +176,8 @@ func (f StringSliceFlag) String() string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f StringSliceFlag) Apply(set *flag.FlagSet) { func (f StringSliceFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { if f.EnvVars != nil {
for _, envVar := range strings.Split(f.EnvVar, ",") { for _, envVar := range f.EnvVars {
envVar = strings.TrimSpace(envVar) envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { if envVal := os.Getenv(envVar); envVal != "" {
newVal := NewStringSlice() newVal := NewStringSlice()
@ -199,14 +195,14 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) {
f.Value = NewStringSlice() f.Value = NewStringSlice()
} }
eachName(f.Name, func(name string) { for _, name := range f.Names() {
set.Var(f.Value, name, f.Usage) set.Var(f.Value, name, f.Usage)
}) }
} }
// GetName returns the name of a flag. // Names returns the name of a flag.
func (f StringSliceFlag) GetName() string { func (f StringSliceFlag) Names() []string {
return f.Name return append([]string{f.Name}, f.Aliases...)
} }
// IntSlice wraps an []int to satisfy flag.Value // 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 // IntSliceFlag is an int flag that can be specified multiple times on the
// command-line // command-line
type IntSliceFlag struct { type IntSliceFlag struct {
Name string Name string
Value *IntSlice Aliases []string
Usage string Value *IntSlice
EnvVar string Usage string
Hidden bool EnvVars []string
Hidden bool
} }
// String returns the usage // String returns the usage
@ -286,8 +283,8 @@ func (f IntSliceFlag) String() string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f IntSliceFlag) Apply(set *flag.FlagSet) { func (f IntSliceFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { if f.EnvVars != nil {
for _, envVar := range strings.Split(f.EnvVar, ",") { for _, envVar := range f.EnvVars {
envVar = strings.TrimSpace(envVar) envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { if envVal := os.Getenv(envVar); envVal != "" {
newVal := NewIntSlice() newVal := NewIntSlice()
@ -308,22 +305,23 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) {
f.Value = NewIntSlice() f.Value = NewIntSlice()
} }
eachName(f.Name, func(name string) { for _, name := range f.Names() {
set.Var(f.Value, name, f.Usage) set.Var(f.Value, name, f.Usage)
}) }
} }
// GetName returns the name of the flag. // Names returns the name of the flag.
func (f IntSliceFlag) GetName() string { func (f IntSliceFlag) Names() []string {
return f.Name return append([]string{f.Name}, f.Aliases...)
} }
// BoolFlag is a switch that defaults to false // BoolFlag is a switch that defaults to false
type BoolFlag struct { type BoolFlag struct {
Name string Name string
Aliases []string
Value bool Value bool
Usage string Usage string
EnvVar string EnvVars []string
Destination *bool Destination *bool
Hidden bool Hidden bool
} }
@ -335,8 +333,8 @@ func (f BoolFlag) String() string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f BoolFlag) Apply(set *flag.FlagSet) { func (f BoolFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { if f.EnvVars != nil {
for _, envVar := range strings.Split(f.EnvVar, ",") { for _, envVar := range f.EnvVars {
envVar = strings.TrimSpace(envVar) envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { if envVal := os.Getenv(envVar); envVal != "" {
envValBool, err := strconv.ParseBool(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 { if f.Destination != nil {
set.BoolVar(f.Destination, name, f.Value, f.Usage) set.BoolVar(f.Destination, name, f.Value, f.Usage)
return return
} }
set.Bool(name, f.Value, f.Usage) set.Bool(name, f.Value, f.Usage)
}) }
} }
// GetName returns the name of the flag. // Names returns the name of the flag.
func (f BoolFlag) GetName() string { func (f BoolFlag) Names() []string {
return f.Name return append([]string{f.Name}, f.Aliases...)
} }
// StringFlag represents a flag that takes as string value // StringFlag represents a flag that takes as string value
type StringFlag struct { type StringFlag struct {
Name string Name string
Aliases []string
Value string Value string
Usage string Usage string
EnvVar string EnvVars []string
Destination *string Destination *string
Hidden bool Hidden bool
} }
@ -379,8 +378,8 @@ func (f StringFlag) String() string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f StringFlag) Apply(set *flag.FlagSet) { func (f StringFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { if f.EnvVars != nil {
for _, envVar := range strings.Split(f.EnvVar, ",") { for _, envVar := range f.EnvVars {
envVar = strings.TrimSpace(envVar) envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { if envVal := os.Getenv(envVar); envVal != "" {
f.Value = 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 { if f.Destination != nil {
set.StringVar(f.Destination, name, f.Value, f.Usage) set.StringVar(f.Destination, name, f.Value, f.Usage)
return return
} }
set.String(name, f.Value, f.Usage) set.String(name, f.Value, f.Usage)
}) }
} }
// GetName returns the name of the flag. // Names returns the name of the flag.
func (f StringFlag) GetName() string { func (f StringFlag) Names() []string {
return f.Name return append([]string{f.Name}, f.Aliases...)
} }
// IntFlag is a flag that takes an integer // IntFlag is a flag that takes an integer
// Errors if the value provided cannot be parsed // Errors if the value provided cannot be parsed
type IntFlag struct { type IntFlag struct {
Name string Name string
Aliases []string
Value int Value int
Usage string Usage string
EnvVar string EnvVars []string
Destination *int Destination *int
Hidden bool Hidden bool
} }
@ -421,8 +421,8 @@ func (f IntFlag) String() string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f IntFlag) Apply(set *flag.FlagSet) { func (f IntFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { if f.EnvVars != nil {
for _, envVar := range strings.Split(f.EnvVar, ",") { for _, envVar := range f.EnvVars {
envVar = strings.TrimSpace(envVar) envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { if envVal := os.Getenv(envVar); envVal != "" {
envValInt, err := strconv.ParseInt(envVal, 0, 64) 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 { if f.Destination != nil {
set.IntVar(f.Destination, name, f.Value, f.Usage) set.IntVar(f.Destination, name, f.Value, f.Usage)
return return
} }
set.Int(name, f.Value, f.Usage) set.Int(name, f.Value, f.Usage)
}) }
} }
// GetName returns the name of the flag. // Names returns the name of the flag.
func (f IntFlag) GetName() string { func (f IntFlag) Names() []string {
return f.Name return append([]string{f.Name}, f.Aliases...)
} }
// DurationFlag is a flag that takes a duration specified in Go's duration // DurationFlag is a flag that takes a duration specified in Go's duration
// format: https://golang.org/pkg/time/#ParseDuration // format: https://golang.org/pkg/time/#ParseDuration
type DurationFlag struct { type DurationFlag struct {
Name string Name string
Aliases []string
Value time.Duration Value time.Duration
Usage string Usage string
EnvVar string EnvVars []string
Destination *time.Duration Destination *time.Duration
Hidden bool Hidden bool
} }
@ -466,8 +467,8 @@ func (f DurationFlag) String() string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f DurationFlag) Apply(set *flag.FlagSet) { func (f DurationFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { if f.EnvVars != nil {
for _, envVar := range strings.Split(f.EnvVar, ",") { for _, envVar := range f.EnvVars {
envVar = strings.TrimSpace(envVar) envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { if envVal := os.Getenv(envVar); envVal != "" {
envValDuration, err := time.ParseDuration(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 { if f.Destination != nil {
set.DurationVar(f.Destination, name, f.Value, f.Usage) set.DurationVar(f.Destination, name, f.Value, f.Usage)
return return
} }
set.Duration(name, f.Value, f.Usage) set.Duration(name, f.Value, f.Usage)
}) }
} }
// GetName returns the name of the flag. // Names returns the name of the flag.
func (f DurationFlag) GetName() string { func (f DurationFlag) Names() []string {
return f.Name return append([]string{f.Name}, f.Aliases...)
} }
// Float64Flag is a flag that takes an float value // Float64Flag is a flag that takes an float value
// Errors if the value provided cannot be parsed // Errors if the value provided cannot be parsed
type Float64Flag struct { type Float64Flag struct {
Name string Name string
Aliases []string
Value float64 Value float64
Usage string Usage string
EnvVar string EnvVars []string
Destination *float64 Destination *float64
Hidden bool Hidden bool
} }
@ -511,8 +513,8 @@ func (f Float64Flag) String() string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f Float64Flag) Apply(set *flag.FlagSet) { func (f Float64Flag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { if f.EnvVars != nil {
for _, envVar := range strings.Split(f.EnvVar, ",") { for _, envVar := range f.EnvVars {
envVar = strings.TrimSpace(envVar) envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { if envVal := os.Getenv(envVar); envVal != "" {
envValFloat, err := strconv.ParseFloat(envVal, 10) 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 { if f.Destination != nil {
set.Float64Var(f.Destination, name, f.Value, f.Usage) set.Float64Var(f.Destination, name, f.Value, f.Usage)
return return
} }
set.Float64(name, f.Value, f.Usage) set.Float64(name, f.Value, f.Usage)
}) }
} }
// GetName returns the name of the flag. // Names returns the name of the flag.
func (f Float64Flag) GetName() string { func (f Float64Flag) Names() []string {
return f.Name return append([]string{f.Name}, f.Aliases...)
} }
func visibleFlags(fl []Flag) []Flag { func visibleFlags(fl []Flag) []Flag {
@ -574,25 +576,27 @@ func unquoteUsage(usage string) (string, string) {
return "", usage return "", usage
} }
func prefixedNames(fullName, placeholder string) string { func prefixedNames(names []string, placeholder string) string {
var prefixed string var prefixed string
parts := strings.Split(fullName, ",") for i, name := range names {
for i, name := range parts { if name == "" {
name = strings.Trim(name, " ") continue
}
prefixed += prefixFor(name) + name prefixed += prefixFor(name) + name
if placeholder != "" { if placeholder != "" {
prefixed += " " + placeholder prefixed += " " + placeholder
} }
if i < len(parts)-1 { if i < len(names)-1 {
prefixed += ", " prefixed += ", "
} }
} }
return prefixed return prefixed
} }
func withEnvHint(envVar, str string) string { func withEnvHint(envVars []string, str string) string {
envText := "" envText := ""
if envVar != "" { if envVars != nil && len(envVars) > 0 {
prefix := "$" prefix := "$"
suffix := "" suffix := ""
sep := ", $" sep := ", $"
@ -601,21 +605,49 @@ func withEnvHint(envVar, str string) string {
suffix = "%" suffix = "%"
sep = "%, %" 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 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) 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) { switch f.(type) {
case IntSliceFlag: case IntSliceFlag:
return withEnvHint(fv.FieldByName("EnvVar").String(), return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyIntSliceFlag(f.(IntSliceFlag)))
stringifyIntSliceFlag(f.(IntSliceFlag)))
case StringSliceFlag: case StringSliceFlag:
return withEnvHint(fv.FieldByName("EnvVar").String(), return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyStringSliceFlag(f.(StringSliceFlag)))
stringifyStringSliceFlag(f.(StringSliceFlag)))
} }
placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) 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)) usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString))
return withEnvHint(fv.FieldByName("EnvVar").String(), return withEnvHint(flagStringSliceField(f, "EnvVars"),
fmt.Sprintf("%s\t%s", prefixedNames(fv.FieldByName("Name").String(), placeholder), usageWithDefault)) fmt.Sprintf("%s\t%s", prefixedNames(f.Names(), placeholder), usageWithDefault))
} }
func stringifyIntSliceFlag(f IntSliceFlag) string { 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 { 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) placeholder, usage := unquoteUsage(usage)
if placeholder == "" { if placeholder == "" {
placeholder = defaultPlaceholder placeholder = defaultPlaceholder
@ -683,5 +715,5 @@ func stringifySliceFlag(usage, name string, defaultVals []string) string {
} }
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) 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)
} }

View File

@ -31,21 +31,22 @@ func TestBoolFlagHelpOutput(t *testing.T) {
var stringFlagTests = []struct { var stringFlagTests = []struct {
name string name string
aliases []string
usage string usage string
value string value string
expected string expected string
}{ }{
{"foo", "", "", "--foo value\t"}, {"foo", nil, "", "", "--foo value\t"},
{"f", "", "", "-f value\t"}, {"f", nil, "", "", "-f value\t"},
{"f", "The total `foo` desired", "all", "-f foo\tThe total foo desired (default: \"all\")"}, {"f", nil, "The total `foo` desired", "all", "-f foo\tThe total foo desired (default: \"all\")"},
{"test", "", "Something", "--test value\t(default: \"Something\")"}, {"test", nil, "", "Something", "--test value\t(default: \"Something\")"},
{"config,c", "Load configuration from `FILE`", "", "--config FILE, -c FILE\tLoad configuration from FILE"}, {"config", []string{"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\")"}, {"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) { func TestStringFlagHelpOutput(t *testing.T) {
for _, test := range stringFlagTests { 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() output := flag.String()
if output != test.expected { if output != test.expected {
@ -58,7 +59,7 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv() os.Clearenv()
os.Setenv("APP_FOO", "derp") os.Setenv("APP_FOO", "derp")
for _, test := range stringFlagTests { 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() output := flag.String()
expectedSuffix := " [$APP_FOO]" expectedSuffix := " [$APP_FOO]"
@ -73,19 +74,20 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
var stringSliceFlagTests = []struct { var stringSliceFlagTests = []struct {
name string name string
aliases []string
value *StringSlice value *StringSlice
expected string expected string
}{ }{
{"foo", NewStringSlice(""), "--foo value\t"}, {"foo", nil, NewStringSlice(""), "--foo value\t"},
{"f", NewStringSlice(""), "-f value\t"}, {"f", nil, NewStringSlice(""), "-f value\t"},
{"f", NewStringSlice("Lipstick"), "-f value\t(default: \"Lipstick\")"}, {"f", nil, NewStringSlice("Lipstick"), "-f value\t(default: \"Lipstick\")"},
{"test", NewStringSlice("Something"), "--test value\t(default: \"Something\")"}, {"test", nil, NewStringSlice("Something"), "--test value\t(default: \"Something\")"},
{"d, dee", NewStringSlice("Inka", "Dinka", "dooo"), "-d value, --dee value\t(default: \"Inka\", \"Dinka\", \"dooo\")"}, {"dee", []string{"d"}, NewStringSlice("Inka", "Dinka", "dooo"), "--dee value, -d value\t(default: \"Inka\", \"Dinka\", \"dooo\")"},
} }
func TestStringSliceFlagHelpOutput(t *testing.T) { func TestStringSliceFlagHelpOutput(t *testing.T) {
for _, test := range stringSliceFlagTests { 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() output := flag.String()
if output != test.expected { if output != test.expected {
@ -98,7 +100,7 @@ func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv() os.Clearenv()
os.Setenv("APP_QWWX", "11,4") os.Setenv("APP_QWWX", "11,4")
for _, test := range stringSliceFlagTests { 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() output := flag.String()
expectedSuffix := " [$APP_QWWX]" expectedSuffix := " [$APP_QWWX]"
@ -134,7 +136,7 @@ func TestIntFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv() os.Clearenv()
os.Setenv("APP_BAR", "2") os.Setenv("APP_BAR", "2")
for _, test := range intFlagTests { for _, test := range intFlagTests {
flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} flag := IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
output := flag.String() output := flag.String()
expectedSuffix := " [$APP_BAR]" expectedSuffix := " [$APP_BAR]"
@ -170,7 +172,7 @@ func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv() os.Clearenv()
os.Setenv("APP_BAR", "2h3m6s") os.Setenv("APP_BAR", "2h3m6s")
for _, test := range durationFlagTests { for _, test := range durationFlagTests {
flag := DurationFlag{Name: test.name, EnvVar: "APP_BAR"} flag := DurationFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
output := flag.String() output := flag.String()
expectedSuffix := " [$APP_BAR]" expectedSuffix := " [$APP_BAR]"
@ -185,17 +187,18 @@ func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
var intSliceFlagTests = []struct { var intSliceFlagTests = []struct {
name string name string
aliases []string
value *IntSlice value *IntSlice
expected string expected string
}{ }{
{"heads", NewIntSlice(), "--heads value\t"}, {"heads", nil, NewIntSlice(), "--heads value\t"},
{"H", NewIntSlice(), "-H value\t"}, {"H", nil, NewIntSlice(), "-H value\t"},
{"H, heads", NewIntSlice(9, 3), "-H value, --heads value\t(default: 9, 3)"}, {"H", []string{"heads"}, NewIntSlice(9, 3), "-H value, --heads value\t(default: 9, 3)"},
} }
func TestIntSliceFlagHelpOutput(t *testing.T) { func TestIntSliceFlagHelpOutput(t *testing.T) {
for _, test := range intSliceFlagTests { 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() output := flag.String()
if output != test.expected { if output != test.expected {
@ -208,7 +211,7 @@ func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv() os.Clearenv()
os.Setenv("APP_SMURF", "42,3") os.Setenv("APP_SMURF", "42,3")
for _, test := range intSliceFlagTests { 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() output := flag.String()
expectedSuffix := " [$APP_SMURF]" expectedSuffix := " [$APP_SMURF]"
@ -244,7 +247,7 @@ func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv() os.Clearenv()
os.Setenv("APP_BAZ", "99.4") os.Setenv("APP_BAZ", "99.4")
for _, test := range float64FlagTests { for _, test := range float64FlagTests {
flag := Float64Flag{Name: test.name, EnvVar: "APP_BAZ"} flag := Float64Flag{Name: test.name, EnvVars: []string{"APP_BAZ"}}
output := flag.String() output := flag.String()
expectedSuffix := " [$APP_BAZ]" expectedSuffix := " [$APP_BAZ]"
@ -281,7 +284,7 @@ func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv() os.Clearenv()
os.Setenv("APP_ZAP", "3") os.Setenv("APP_ZAP", "3")
for _, test := range genericFlagTests { for _, test := range genericFlagTests {
flag := GenericFlag{Name: test.name, EnvVar: "APP_ZAP"} flag := GenericFlag{Name: test.name, EnvVars: []string{"APP_ZAP"}}
output := flag.String() output := flag.String()
expectedSuffix := " [$APP_ZAP]" expectedSuffix := " [$APP_ZAP]"
@ -297,7 +300,7 @@ func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
func TestParseMultiString(t *testing.T) { func TestParseMultiString(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ Flags: []Flag{
StringFlag{Name: "serve, s"}, StringFlag{Name: "serve", Aliases: []string{"s"}},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
if ctx.String("serve") != "10" { if ctx.String("serve") != "10" {
@ -335,7 +338,7 @@ func TestParseMultiStringFromEnv(t *testing.T) {
os.Setenv("APP_COUNT", "20") os.Setenv("APP_COUNT", "20")
(&App{ (&App{
Flags: []Flag{ Flags: []Flag{
StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, StringFlag{Name: "count", Aliases: []string{"c"}, EnvVars: []string{"APP_COUNT"}},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
if ctx.String("count") != "20" { if ctx.String("count") != "20" {
@ -354,7 +357,7 @@ func TestParseMultiStringFromEnvCascade(t *testing.T) {
os.Setenv("APP_COUNT", "20") os.Setenv("APP_COUNT", "20")
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if ctx.String("count") != "20" { if ctx.String("count") != "20" {
@ -371,7 +374,7 @@ func TestParseMultiStringFromEnvCascade(t *testing.T) {
func TestParseMultiStringSlice(t *testing.T) { func TestParseMultiStringSlice(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ Flags: []Flag{
StringSliceFlag{Name: "serve, s", Value: NewStringSlice()}, StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice()},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
expected := []string{"10", "20"} expected := []string{"10", "20"}
@ -389,7 +392,7 @@ func TestParseMultiStringSlice(t *testing.T) {
func TestParseMultiStringSliceWithDefaults(t *testing.T) { func TestParseMultiStringSliceWithDefaults(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
expected := []string{"10", "20"} expected := []string{"10", "20"}
@ -407,7 +410,7 @@ func TestParseMultiStringSliceWithDefaults(t *testing.T) {
func TestParseMultiStringSliceWithDefaultsUnset(t *testing.T) { func TestParseMultiStringSliceWithDefaultsUnset(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"9", "2"}) { if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"9", "2"}) {
@ -427,7 +430,7 @@ func TestParseMultiStringSliceFromEnv(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
@ -447,7 +450,7 @@ func TestParseMultiStringSliceFromEnvWithDefaults(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
@ -467,7 +470,7 @@ func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
@ -487,7 +490,7 @@ func TestParseMultiStringSliceFromEnvCascadeWithDefaults(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
@ -504,7 +507,7 @@ func TestParseMultiStringSliceFromEnvCascadeWithDefaults(t *testing.T) {
func TestParseMultiInt(t *testing.T) { func TestParseMultiInt(t *testing.T) {
a := App{ a := App{
Flags: []Flag{ Flags: []Flag{
IntFlag{Name: "serve, s"}, IntFlag{Name: "serve", Aliases: []string{"s"}},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
if ctx.Int("serve") != 10 { if ctx.Int("serve") != 10 {
@ -543,7 +546,7 @@ func TestParseMultiIntFromEnv(t *testing.T) {
os.Setenv("APP_TIMEOUT_SECONDS", "10") os.Setenv("APP_TIMEOUT_SECONDS", "10")
a := App{ a := App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if ctx.Int("timeout") != 10 { if ctx.Int("timeout") != 10 {
@ -563,7 +566,7 @@ func TestParseMultiIntFromEnvCascade(t *testing.T) {
os.Setenv("APP_TIMEOUT_SECONDS", "10") os.Setenv("APP_TIMEOUT_SECONDS", "10")
a := App{ a := App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if ctx.Int("timeout") != 10 { if ctx.Int("timeout") != 10 {
@ -581,7 +584,7 @@ func TestParseMultiIntFromEnvCascade(t *testing.T) {
func TestParseMultiIntSlice(t *testing.T) { func TestParseMultiIntSlice(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ Flags: []Flag{
IntSliceFlag{Name: "serve, s", Value: NewIntSlice()}, IntSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewIntSlice()},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
@ -598,7 +601,7 @@ func TestParseMultiIntSlice(t *testing.T) {
func TestParseMultiIntSliceWithDefaults(t *testing.T) { func TestParseMultiIntSliceWithDefaults(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
@ -615,7 +618,7 @@ func TestParseMultiIntSliceWithDefaults(t *testing.T) {
func TestParseMultiIntSliceWithDefaultsUnset(t *testing.T) { func TestParseMultiIntSliceWithDefaultsUnset(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{9, 2}) { if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{9, 2}) {
@ -635,7 +638,7 @@ func TestParseMultiIntSliceFromEnv(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
@ -655,7 +658,7 @@ func TestParseMultiIntSliceFromEnvWithDefaults(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
@ -675,7 +678,7 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
(&App{ (&App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
@ -692,7 +695,7 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
func TestParseMultiFloat64(t *testing.T) { func TestParseMultiFloat64(t *testing.T) {
a := App{ a := App{
Flags: []Flag{ Flags: []Flag{
Float64Flag{Name: "serve, s"}, Float64Flag{Name: "serve", Aliases: []string{"s"}},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
if ctx.Float64("serve") != 10.2 { if ctx.Float64("serve") != 10.2 {
@ -731,7 +734,7 @@ func TestParseMultiFloat64FromEnv(t *testing.T) {
os.Setenv("APP_TIMEOUT_SECONDS", "15.5") os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
a := App{ a := App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if ctx.Float64("timeout") != 15.5 { if ctx.Float64("timeout") != 15.5 {
@ -751,7 +754,7 @@ func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
os.Setenv("APP_TIMEOUT_SECONDS", "15.5") os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
a := App{ a := App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if ctx.Float64("timeout") != 15.5 { if ctx.Float64("timeout") != 15.5 {
@ -769,7 +772,7 @@ func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
func TestParseMultiBool(t *testing.T) { func TestParseMultiBool(t *testing.T) {
a := App{ a := App{
Flags: []Flag{ Flags: []Flag{
BoolFlag{Name: "serve, s"}, BoolFlag{Name: "serve", Aliases: []string{"s"}},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
if ctx.Bool("serve") != true { if ctx.Bool("serve") != true {
@ -808,7 +811,7 @@ func TestParseMultiBoolFromEnv(t *testing.T) {
os.Setenv("APP_DEBUG", "1") os.Setenv("APP_DEBUG", "1")
a := App{ a := App{
Flags: []Flag{ Flags: []Flag{
BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"APP_DEBUG"}},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
if ctx.Bool("debug") != true { if ctx.Bool("debug") != true {
@ -828,7 +831,7 @@ func TestParseMultiBoolFromEnvCascade(t *testing.T) {
os.Setenv("APP_DEBUG", "1") os.Setenv("APP_DEBUG", "1")
a := App{ a := App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if ctx.Bool("debug") != true { if ctx.Bool("debug") != true {
@ -846,7 +849,7 @@ func TestParseMultiBoolFromEnvCascade(t *testing.T) {
func TestParseMultiBoolTrue(t *testing.T) { func TestParseMultiBoolTrue(t *testing.T) {
a := App{ a := App{
Flags: []Flag{ Flags: []Flag{
BoolFlag{Name: "implode, i", Value: true}, BoolFlag{Name: "implode", Aliases: []string{"i"}, Value: true},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
if ctx.Bool("implode") { if ctx.Bool("implode") {
@ -888,9 +891,10 @@ func TestParseMultiBoolTrueFromEnv(t *testing.T) {
a := App{ a := App{
Flags: []Flag{ Flags: []Flag{
BoolFlag{ BoolFlag{
Name: "debug, d", Name: "debug",
Value: true, Aliases: []string{"d"},
EnvVar: "APP_DEBUG", Value: true,
EnvVars: []string{"APP_DEBUG"},
}, },
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
@ -912,9 +916,10 @@ func TestParseMultiBoolTrueFromEnvCascade(t *testing.T) {
a := App{ a := App{
Flags: []Flag{ Flags: []Flag{
BoolFlag{ BoolFlag{
Name: "debug, d", Name: "debug",
Value: true, Aliases: []string{"d"},
EnvVar: "COMPAT_DEBUG,APP_DEBUG", Value: true,
EnvVars: []string{"COMPAT_DEBUG", "APP_DEBUG"},
}, },
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
@ -951,7 +956,7 @@ func (p *Parser) String() string {
func TestParseGeneric(t *testing.T) { func TestParseGeneric(t *testing.T) {
a := App{ a := App{
Flags: []Flag{ Flags: []Flag{
GenericFlag{Name: "serve, s", Value: &Parser{}}, GenericFlag{Name: "serve", Aliases: []string{"s"}, Value: &Parser{}},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) {
@ -971,7 +976,12 @@ func TestParseGenericFromEnv(t *testing.T) {
os.Setenv("APP_SERVE", "20,30") os.Setenv("APP_SERVE", "20,30")
a := App{ a := App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) { if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) {
@ -991,7 +1001,11 @@ func TestParseGenericFromEnvCascade(t *testing.T) {
os.Setenv("APP_FOO", "99,2000") os.Setenv("APP_FOO", "99,2000")
a := App{ a := App{
Flags: []Flag{ 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 { Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) { if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) {

View File

@ -208,11 +208,11 @@ func printHelp(out io.Writer, templ string, data interface{}) {
func checkVersion(c *Context) bool { func checkVersion(c *Context) bool {
found := false found := false
if VersionFlag.Name != "" { if VersionFlag.Name != "" {
eachName(VersionFlag.Name, func(name string) { for _, name := range VersionFlag.Names() {
if c.Bool(name) { if c.Bool(name) {
found = true found = true
} }
}) }
} }
return found return found
} }
@ -220,11 +220,11 @@ func checkVersion(c *Context) bool {
func checkHelp(c *Context) bool { func checkHelp(c *Context) bool {
found := false found := false
if HelpFlag.Name != "" { if HelpFlag.Name != "" {
eachName(HelpFlag.Name, func(name string) { for _, name := range HelpFlag.Names() {
if c.Bool(name) { if c.Bool(name) {
found = true found = true
} }
}) }
} }
return found return found
} }

View File

@ -60,13 +60,14 @@ func Test_Help_Custom_Flags(t *testing.T) {
}() }()
HelpFlag = BoolFlag{ HelpFlag = BoolFlag{
Name: "help, x", Name: "help",
Usage: "show help", Aliases: []string{"x"},
Usage: "show help",
} }
app := App{ app := App{
Flags: []Flag{ Flags: []Flag{
BoolFlag{Name: "foo, h"}, BoolFlag{Name: "foo", Aliases: []string{"h"}},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
if ctx.Bool("h") != true { if ctx.Bool("h") != true {
@ -90,13 +91,14 @@ func Test_Version_Custom_Flags(t *testing.T) {
}() }()
VersionFlag = BoolFlag{ VersionFlag = BoolFlag{
Name: "version, V", Name: "version",
Usage: "show version", Aliases: []string{"V"},
Usage: "show version",
} }
app := App{ app := App{
Flags: []Flag{ Flags: []Flag{
BoolFlag{Name: "foo, v"}, BoolFlag{Name: "foo", Aliases: []string{"v"}},
}, },
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
if ctx.Bool("v") != true { if ctx.Bool("v") != true {