Replace all "stringly typed" fields with slice equivalents

and alter the Flag interface accordingly

Closes #415
This commit is contained in:
Dan Buch 2016-05-21 21:29:45 -04:00
parent 65357985c2
commit 81fcf706ea
No known key found for this signature in database
GPG Key ID: FAEF12936DD3E3EC
11 changed files with 314 additions and 256 deletions

View File

@ -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"},
},
}
```

View File

@ -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)
}
}

View File

@ -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",

View File

@ -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"))

7
app.go
View File

@ -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
}
}

View File

@ -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,

View File

@ -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
}

264
flag.go
View File

@ -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
}

View File

@ -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"}) {

View File

@ -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
}

View File

@ -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 {