diff --git a/README.md b/README.md index 0a1f624..3fe7556 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ Setting and querying flags is simple. ``` go ... app.Flags = []cli.Flag { - cli.StringFlag{ + &cli.StringFlag{ Name: "lang", Value: "english", Usage: "language for the greeting", @@ -232,7 +232,7 @@ You can also set a destination variable for a flag, to which the content will be ... var language string app.Flags = []cli.Flag { - cli.StringFlag{ + &cli.StringFlag{ Name: "lang", Value: "english", Usage: "language for the greeting", @@ -264,7 +264,7 @@ indicated with back quotes. For example this: ```go -cli.StringFlag{ +&cli.StringFlag{ Name: "config", Aliases: []string{"c"}, Usage: "Load configuration from `FILE`", @@ -285,7 +285,7 @@ You can set alternate (or short) names for flags by providing a comma-delimited ``` go app.Flags = []cli.Flag { - cli.StringFlag{ + &cli.StringFlag{ Name: "lang", Aliases: []string{"l"}, Value: "english", @@ -302,7 +302,7 @@ You can also have the default value set from the environment via `EnvVars`. e.g ``` go app.Flags = []cli.Flag { - cli.StringFlag{ + &cli.StringFlag{ Name: "lang", Aliases: []string{"l"}, Value: "english", @@ -316,7 +316,7 @@ If `EnvVars` contains more than one string, the first environment variable that ``` go app.Flags = []cli.Flag { - cli.StringFlag{ + &cli.StringFlag{ Name: "lang", Aliases: []string{"l"}, Value: "english", @@ -333,7 +333,7 @@ There is a separate package altsrc that adds support for getting flag values fro In order to get values for a flag from an alternate input source the following code would be added to wrap an existing cli.Flag like below: ``` go - altsrc.NewIntFlag(cli.IntFlag{Name: "test"}) + altsrc.NewIntFlag(&cli.IntFlag{Name: "test"}) ``` Initialization must also occur for these flags. Below is an example initializing getting data from a yaml file below. @@ -362,8 +362,8 @@ Here is a more complete sample of a command using YAML support: return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "test"}), - cli.StringFlag{Name: "load"}}, + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) @@ -375,7 +375,7 @@ Subcommands can be defined for a more git-like command line app. ```go ... -app.Commands = []cli.Command{ +app.Commands = []*cli.Command{ { Name: "add", Aliases: []string{"a"}, @@ -398,7 +398,7 @@ app.Commands = []cli.Command{ Name: "template", Aliases: []string{"r"}, Usage: "options for task templates", - Subcommands: []cli.Command{ + Subcommands: []*cli.Command{ { Name: "add", Usage: "add a new template", @@ -431,7 +431,7 @@ E.g. ```go ... - app.Commands = []cli.Command{ + app.Commands = []*cli.Command{ { Name: "noop", }, @@ -479,7 +479,7 @@ import ( func main() { app := cli.NewApp() app.Flags = []cli.Flag{ - cli.BoolFlag{ + &cli.BoolFlag{ Name: "ginger-crouton", Value: true, Usage: "is it in the soup?", @@ -508,7 +508,7 @@ the App or its subcommands. var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"} app := cli.NewApp() app.EnableBashCompletion = true -app.Commands = []cli.Command{ +app.Commands = []*cli.Command{ { Name: "complete", Aliases: []string{"c"}, diff --git a/altsrc/flag.go b/altsrc/flag.go index 6c68bf5..1e6ebce 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -62,15 +62,15 @@ func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context } } -// GenericFlag is the flag type that wraps cli.GenericFlag to allow +// GenericFlag is the flag type that wraps *cli.GenericFlag to allow // for other values to be specified type GenericFlag struct { - cli.GenericFlag + *cli.GenericFlag set *flag.FlagSet } // NewGenericFlag creates a new GenericFlag -func NewGenericFlag(flag cli.GenericFlag) *GenericFlag { +func NewGenericFlag(flag *cli.GenericFlag) *GenericFlag { return &GenericFlag{GenericFlag: flag, set: nil} } @@ -100,15 +100,15 @@ func (f *GenericFlag) Apply(set *flag.FlagSet) { f.GenericFlag.Apply(set) } -// StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow +// StringSliceFlag is the flag type that wraps *cli.StringSliceFlag to allow // for other values to be specified type StringSliceFlag struct { - cli.StringSliceFlag + *cli.StringSliceFlag set *flag.FlagSet } // NewStringSliceFlag creates a new StringSliceFlag -func NewStringSliceFlag(flag cli.StringSliceFlag) *StringSliceFlag { +func NewStringSliceFlag(flag *cli.StringSliceFlag) *StringSliceFlag { return &StringSliceFlag{StringSliceFlag: flag, set: nil} } @@ -141,15 +141,15 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) { f.StringSliceFlag.Apply(set) } -// IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow +// IntSliceFlag is the flag type that wraps *cli.IntSliceFlag to allow // for other values to be specified type IntSliceFlag struct { - cli.IntSliceFlag + *cli.IntSliceFlag set *flag.FlagSet } // NewIntSliceFlag creates a new IntSliceFlag -func NewIntSliceFlag(flag cli.IntSliceFlag) *IntSliceFlag { +func NewIntSliceFlag(flag *cli.IntSliceFlag) *IntSliceFlag { return &IntSliceFlag{IntSliceFlag: flag, set: nil} } @@ -182,15 +182,15 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) { f.IntSliceFlag.Apply(set) } -// BoolFlag is the flag type that wraps cli.BoolFlag to allow +// BoolFlag is the flag type that wraps *cli.BoolFlag to allow // for other values to be specified type BoolFlag struct { - cli.BoolFlag + *cli.BoolFlag set *flag.FlagSet } // NewBoolFlag creates a new BoolFlag -func NewBoolFlag(flag cli.BoolFlag) *BoolFlag { +func NewBoolFlag(flag *cli.BoolFlag) *BoolFlag { return &BoolFlag{BoolFlag: flag, set: nil} } @@ -219,15 +219,15 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) { f.BoolFlag.Apply(set) } -// StringFlag is the flag type that wraps cli.StringFlag to allow +// StringFlag is the flag type that wraps *cli.StringFlag to allow // for other values to be specified type StringFlag struct { - cli.StringFlag + *cli.StringFlag set *flag.FlagSet } // NewStringFlag creates a new StringFlag -func NewStringFlag(flag cli.StringFlag) *StringFlag { +func NewStringFlag(flag *cli.StringFlag) *StringFlag { return &StringFlag{StringFlag: flag, set: nil} } @@ -257,15 +257,15 @@ func (f *StringFlag) Apply(set *flag.FlagSet) { f.StringFlag.Apply(set) } -// IntFlag is the flag type that wraps cli.IntFlag to allow +// IntFlag is the flag type that wraps *cli.IntFlag to allow // for other values to be specified type IntFlag struct { - cli.IntFlag + *cli.IntFlag set *flag.FlagSet } // NewIntFlag creates a new IntFlag -func NewIntFlag(flag cli.IntFlag) *IntFlag { +func NewIntFlag(flag *cli.IntFlag) *IntFlag { return &IntFlag{IntFlag: flag, set: nil} } @@ -294,15 +294,15 @@ func (f *IntFlag) Apply(set *flag.FlagSet) { f.IntFlag.Apply(set) } -// DurationFlag is the flag type that wraps cli.DurationFlag to allow +// DurationFlag is the flag type that wraps *cli.DurationFlag to allow // for other values to be specified type DurationFlag struct { - cli.DurationFlag + *cli.DurationFlag set *flag.FlagSet } // NewDurationFlag creates a new DurationFlag -func NewDurationFlag(flag cli.DurationFlag) *DurationFlag { +func NewDurationFlag(flag *cli.DurationFlag) *DurationFlag { return &DurationFlag{DurationFlag: flag, set: nil} } @@ -332,15 +332,15 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) { f.DurationFlag.Apply(set) } -// Float64Flag is the flag type that wraps cli.Float64Flag to allow +// Float64Flag is the flag type that wraps *cli.Float64Flag to allow // for other values to be specified type Float64Flag struct { - cli.Float64Flag + *cli.Float64Flag set *flag.FlagSet } // NewFloat64Flag creates a new Float64Flag -func NewFloat64Flag(flag cli.Float64Flag) *Float64Flag { +func NewFloat64Flag(flag *cli.Float64Flag) *Float64Flag { return &Float64Flag{Float64Flag: flag, set: nil} } diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index 0b188da..54055f3 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -26,7 +26,7 @@ type testApplyInputSource struct { func TestGenericApplyInputSourceValue(t *testing.T) { v := &Parser{"abc", "def"} c := runTest(t, testApplyInputSource{ - Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}}), + Flag: NewGenericFlag(&cli.GenericFlag{Name: "test", Value: &Parser{}}), FlagName: "test", MapValue: v, }) @@ -36,7 +36,7 @@ func TestGenericApplyInputSourceValue(t *testing.T) { func TestGenericApplyInputSourceMethodContextSet(t *testing.T) { p := &Parser{"abc", "def"} c := runTest(t, testApplyInputSource{ - Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}}), + Flag: NewGenericFlag(&cli.GenericFlag{Name: "test", Value: &Parser{}}), FlagName: "test", MapValue: &Parser{"efg", "hig"}, ContextValueString: p.String(), @@ -46,7 +46,7 @@ func TestGenericApplyInputSourceMethodContextSet(t *testing.T) { func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewGenericFlag(cli.GenericFlag{ + Flag: NewGenericFlag(&cli.GenericFlag{ Name: "test", Value: &Parser{}, EnvVars: []string{"TEST"}, @@ -61,7 +61,7 @@ func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestStringSliceApplyInputSourceValue(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}), + Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}), FlagName: "test", MapValue: []string{"hello", "world"}, }) @@ -70,7 +70,7 @@ func TestStringSliceApplyInputSourceValue(t *testing.T) { func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}), + Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}), FlagName: "test", MapValue: []string{"hello", "world"}, ContextValueString: "ohno", @@ -80,7 +80,7 @@ func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) { func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), + Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: []string{"hello", "world"}, EnvVarName: "TEST", @@ -91,7 +91,7 @@ func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestIntSliceApplyInputSourceValue(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}), + Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}), FlagName: "test", MapValue: []int{1, 2}, }) @@ -100,7 +100,7 @@ func TestIntSliceApplyInputSourceValue(t *testing.T) { func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}), + Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}), FlagName: "test", MapValue: []int{1, 2}, ContextValueString: "3", @@ -110,7 +110,7 @@ func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) { func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), + Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: []int{1, 2}, EnvVarName: "TEST", @@ -121,7 +121,7 @@ func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestBoolApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewBoolFlag(cli.BoolFlag{Name: "test"}), + Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), FlagName: "test", MapValue: true, }) @@ -130,7 +130,7 @@ func TestBoolApplyInputSourceMethodSet(t *testing.T) { func TestBoolApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewBoolFlag(cli.BoolFlag{Name: "test"}), + Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), FlagName: "test", MapValue: false, ContextValueString: "true", @@ -140,7 +140,7 @@ func TestBoolApplyInputSourceMethodContextSet(t *testing.T) { func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewBoolFlag(cli.BoolFlag{Name: "test", EnvVars: []string{"TEST"}}), + Flag: NewBoolFlag(&cli.BoolFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: false, EnvVarName: "TEST", @@ -151,7 +151,7 @@ func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestStringApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewStringFlag(cli.StringFlag{Name: "test"}), + Flag: NewStringFlag(&cli.StringFlag{Name: "test"}), FlagName: "test", MapValue: "hello", }) @@ -160,7 +160,7 @@ func TestStringApplyInputSourceMethodSet(t *testing.T) { func TestStringApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewStringFlag(cli.StringFlag{Name: "test"}), + Flag: NewStringFlag(&cli.StringFlag{Name: "test"}), FlagName: "test", MapValue: "hello", ContextValueString: "goodbye", @@ -170,7 +170,7 @@ func TestStringApplyInputSourceMethodContextSet(t *testing.T) { func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewStringFlag(cli.StringFlag{Name: "test", EnvVars: []string{"TEST"}}), + Flag: NewStringFlag(&cli.StringFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: "hello", EnvVarName: "TEST", @@ -181,7 +181,7 @@ func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestIntApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewIntFlag(cli.IntFlag{Name: "test"}), + Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: 15, }) @@ -190,7 +190,7 @@ func TestIntApplyInputSourceMethodSet(t *testing.T) { func TestIntApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewIntFlag(cli.IntFlag{Name: "test"}), + Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: 15, ContextValueString: "7", @@ -200,7 +200,7 @@ func TestIntApplyInputSourceMethodContextSet(t *testing.T) { func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewIntFlag(cli.IntFlag{Name: "test", EnvVars: []string{"TEST"}}), + Flag: NewIntFlag(&cli.IntFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: 15, EnvVarName: "TEST", @@ -211,7 +211,7 @@ func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestDurationApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}), + Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: time.Duration(30 * time.Second), }) @@ -220,7 +220,7 @@ func TestDurationApplyInputSourceMethodSet(t *testing.T) { func TestDurationApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}), + Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: time.Duration(30 * time.Second), ContextValueString: time.Duration(15 * time.Second).String(), @@ -230,7 +230,7 @@ func TestDurationApplyInputSourceMethodContextSet(t *testing.T) { func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewDurationFlag(cli.DurationFlag{Name: "test", EnvVars: []string{"TEST"}}), + Flag: NewDurationFlag(&cli.DurationFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: time.Duration(30 * time.Second), EnvVarName: "TEST", @@ -241,7 +241,7 @@ func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) { func TestFloat64ApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewFloat64Flag(cli.Float64Flag{Name: "test"}), + Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: 1.3, }) @@ -250,7 +250,7 @@ func TestFloat64ApplyInputSourceMethodSet(t *testing.T) { func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewFloat64Flag(cli.Float64Flag{Name: "test"}), + Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: 1.3, ContextValueString: fmt.Sprintf("%v", 1.4), @@ -260,7 +260,7 @@ func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { func TestFloat64ApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ - Flag: NewFloat64Flag(cli.Float64Flag{Name: "test", EnvVars: []string{"TEST"}}), + Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: 1.3, EnvVarName: "TEST", diff --git a/altsrc/yaml_command_test.go b/altsrc/yaml_command_test.go index d1a15d7..44e8351 100644 --- a/altsrc/yaml_command_test.go +++ b/altsrc/yaml_command_test.go @@ -35,8 +35,8 @@ func TestCommandYamlFileTest(t *testing.T) { return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "test"}), - cli.StringFlag{Name: "load"}}, + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) @@ -68,8 +68,8 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) { return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "test", EnvVars: []string{"THE_TEST"}}), - cli.StringFlag{Name: "load"}}, + NewIntFlag(&cli.IntFlag{Name: "test", EnvVars: []string{"THE_TEST"}}), + &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) @@ -103,8 +103,8 @@ func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) { return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "top.test", EnvVars: []string{"THE_TEST"}}), - cli.StringFlag{Name: "load"}}, + NewIntFlag(&cli.IntFlag{Name: "top.test", EnvVars: []string{"THE_TEST"}}), + &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) @@ -135,8 +135,8 @@ func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) { return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "test"}), - cli.StringFlag{Name: "load"}}, + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) @@ -168,8 +168,8 @@ func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) { return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "top.test"}), - cli.StringFlag{Name: "load"}}, + NewIntFlag(&cli.IntFlag{Name: "top.test"}), + &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) @@ -200,8 +200,8 @@ func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) { return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "test", Value: 7}), - cli.StringFlag{Name: "load"}}, + NewIntFlag(&cli.IntFlag{Name: "test", Value: 7}), + &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) @@ -233,8 +233,8 @@ func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) { return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7}), - cli.StringFlag{Name: "load"}}, + NewIntFlag(&cli.IntFlag{Name: "top.test", Value: 7}), + &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) @@ -268,8 +268,8 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVars: []string{"THE_TEST"}}), - cli.StringFlag{Name: "load"}}, + NewIntFlag(&cli.IntFlag{Name: "test", Value: 7, EnvVars: []string{"THE_TEST"}}), + &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) @@ -303,8 +303,8 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *tes return nil }, Flags: []cli.Flag{ - NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVars: []string{"THE_TEST"}}), - cli.StringFlag{Name: "load"}}, + NewIntFlag(&cli.IntFlag{Name: "top.test", Value: 7, EnvVars: []string{"THE_TEST"}}), + &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) diff --git a/app.go b/app.go index 622c7a1..68a92a2 100644 --- a/app.go +++ b/app.go @@ -27,7 +27,7 @@ type App struct { // Version of the program Version string // List of commands to execute - Commands []Command + Commands []*Command // List of flags to parse Flags []Flag // Boolean to enable bash completion commands @@ -37,7 +37,7 @@ type App struct { // Boolean to hide built-in version flag and the VERSION section of help HideVersion bool // Populate on app startup, only gettable through method Categories() - categories CommandCategories + categories *CommandCategories // An action to execute when the bash-completion flag is set BashComplete BashCompleteFunc // An action to execute before any subcommands are run, but after the context is ready @@ -55,7 +55,7 @@ type App struct { // Compilation date Compiled time.Time // List of all authors who contributed - Authors []Author + Authors []*Author // Copyright of the binary if any Copyright string // Writer writer to write output to @@ -104,7 +104,7 @@ func (a *App) Setup() { a.didSetup = true - newCmds := []Command{} + newCmds := []*Command{} for _, c := range a.Commands { if c.HelpName == "" { c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) @@ -113,7 +113,7 @@ func (a *App) Setup() { } a.Commands = newCmds - a.categories = CommandCategories{} + a.categories = NewCommandCategories() for _, command := range a.Commands { a.categories = a.categories.AddCommand(command.Category, command) } @@ -121,8 +121,9 @@ func (a *App) Setup() { // append help to commands if a.Command(helpCommand.Name) == nil && !a.HideHelp { - a.Commands = append(a.Commands, helpCommand) - if !reflect.DeepEqual(HelpFlag, BoolFlag{}) { + a.appendCommand(helpCommand) + + if HelpFlag != nil { a.appendFlag(HelpFlag) } } @@ -224,14 +225,15 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { // append help to commands if len(a.Commands) > 0 { if a.Command(helpCommand.Name) == nil && !a.HideHelp { - a.Commands = append(a.Commands, helpCommand) - if !reflect.DeepEqual(HelpFlag, BoolFlag{}) { + a.appendCommand(helpCommand) + + if HelpFlag != nil { a.appendFlag(HelpFlag) } } } - newCmds := []Command{} + newCmds := []*Command{} for _, c := range a.Commands { if c.HelpName == "" { c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) @@ -331,7 +333,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { func (a *App) Command(name string) *Command { for _, c := range a.Commands { if c.HasName(name) { - return &c + return c } } @@ -339,7 +341,7 @@ func (a *App) Command(name string) *Command { } // Categories returns a slice containing all the categories with the commands they contain -func (a *App) Categories() CommandCategories { +func (a *App) Categories() *CommandCategories { return a.categories } @@ -347,7 +349,7 @@ func (a *App) Categories() CommandCategories { // Hidden=false func (a *App) VisibleCategories() []*CommandCategory { ret := []*CommandCategory{} - for _, category := range a.categories { + for _, category := range a.categories.Categories { if visible := func() *CommandCategory { for _, command := range category.Commands { if !command.Hidden { @@ -363,8 +365,8 @@ func (a *App) VisibleCategories() []*CommandCategory { } // VisibleCommands returns a slice of the Commands with Hidden=false -func (a *App) VisibleCommands() []Command { - ret := []Command{} +func (a *App) VisibleCommands() []*Command { + ret := []*Command{} for _, command := range a.Commands { if !command.Hidden { ret = append(ret, command) @@ -398,9 +400,15 @@ func (a *App) errWriter() io.Writer { return a.ErrWriter } -func (a *App) appendFlag(flag Flag) { - if !a.hasFlag(flag) { - a.Flags = append(a.Flags, flag) +func (a *App) appendFlag(fl Flag) { + if !hasFlag(a.Flags, fl) { + a.Flags = append(a.Flags, fl) + } +} + +func (a *App) appendCommand(c *Command) { + if !hasCommand(a.Commands, c) { + a.Commands = append(a.Commands, c) } } @@ -411,7 +419,7 @@ type Author struct { } // String makes Author comply to the Stringer interface, to allow an easy print in the templating process -func (a Author) String() string { +func (a *Author) String() string { e := "" if a.Email != "" { e = "<" + a.Email + "> " diff --git a/app_test.go b/app_test.go index 8d81a5d..3c79766 100644 --- a/app_test.go +++ b/app_test.go @@ -24,14 +24,14 @@ func ExampleApp_Run() { app := NewApp() app.Name = "greet" app.Flags = []Flag{ - StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, + &StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, } app.Action = func(c *Context) error { fmt.Printf("Hello %v\n", c.String("name")) return nil } app.UsageText = "app [first_arg] [second_arg]" - app.Authors = []Author{{Name: "Oliver Allen", Email: "oliver@toyshop.example.com"}} + app.Authors = []*Author{{Name: "Oliver Allen", Email: "oliver@toyshop.example.com"}} app.Run(os.Args) // Output: // Hello Jeremy @@ -42,20 +42,20 @@ func ExampleApp_Run_subcommand() { os.Args = []string{"say", "hi", "english", "--name", "Jeremy"} app := NewApp() app.Name = "say" - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "hello", Aliases: []string{"hi"}, Usage: "use it to see a description", Description: "This is how we describe hello the function", - Subcommands: []Command{ + Subcommands: []*Command{ { Name: "english", Aliases: []string{"en"}, Usage: "sends a greeting in english", Description: "greets someone in english", Flags: []Flag{ - StringFlag{ + &StringFlag{ Name: "name", Value: "Bob", Usage: "Name of the person to greet", @@ -82,9 +82,9 @@ func ExampleApp_Run_help() { app := NewApp() app.Name = "greet" app.Flags = []Flag{ - StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, + &StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, } - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "describeit", Aliases: []string{"d"}, @@ -115,7 +115,7 @@ func ExampleApp_Run_bashComplete() { app := NewApp() app.Name = "greet" app.EnableBashCompletion = true - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "describeit", Aliases: []string{"d"}, @@ -175,9 +175,9 @@ var commandAppTests = []struct { func TestApp_Command(t *testing.T) { app := NewApp() - fooCommand := Command{Name: "foobar", Aliases: []string{"f"}} - batCommand := Command{Name: "batbaz", Aliases: []string{"b"}} - app.Commands = []Command{ + fooCommand := &Command{Name: "foobar", Aliases: []string{"f"}} + batCommand := &Command{Name: "batbaz", Aliases: []string{"b"}} + app.Commands = []*Command{ fooCommand, batCommand, } @@ -191,7 +191,7 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) { var context *Context a := NewApp() - a.Commands = []Command{ + a.Commands = []*Command{ { Name: "foo", Action: func(c *Context) error { @@ -199,7 +199,7 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) { return nil }, Flags: []Flag{ - StringFlag{ + &StringFlag{ Name: "lang", Value: "english", Usage: "language for the greeting", @@ -216,13 +216,13 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) { func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) { var parsedOption string - var args []string + var args *Args app := NewApp() - command := Command{ + command := &Command{ Name: "cmd", Flags: []Flag{ - StringFlag{Name: "option", Value: "", Usage: "some option"}, + &StringFlag{Name: "option", Value: "", Usage: "some option"}, }, Action: func(c *Context) error { parsedOption = c.String("option") @@ -230,58 +230,58 @@ func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) { return nil }, } - app.Commands = []Command{command} + app.Commands = []*Command{command} app.Run([]string{"", "cmd", "--option", "my-option", "my-arg", "--", "--notARealFlag"}) expect(t, parsedOption, "my-option") - expect(t, args[0], "my-arg") - expect(t, args[1], "--") - expect(t, args[2], "--notARealFlag") + expect(t, args.Get(0), "my-arg") + expect(t, args.Get(1), "--") + expect(t, args.Get(2), "--notARealFlag") } func TestApp_CommandWithDash(t *testing.T) { - var args []string + var args *Args app := NewApp() - command := Command{ + command := &Command{ Name: "cmd", Action: func(c *Context) error { args = c.Args() return nil }, } - app.Commands = []Command{command} + app.Commands = []*Command{command} app.Run([]string{"", "cmd", "my-arg", "-"}) - expect(t, args[0], "my-arg") - expect(t, args[1], "-") + expect(t, args.Get(0), "my-arg") + expect(t, args.Get(1), "-") } func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) { - var args []string + var args *Args app := NewApp() - command := Command{ + command := &Command{ Name: "cmd", Action: func(c *Context) error { args = c.Args() return nil }, } - app.Commands = []Command{command} + app.Commands = []*Command{command} app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"}) - expect(t, args[0], "my-arg") - expect(t, args[1], "--") - expect(t, args[2], "notAFlagAtAll") + expect(t, args.Get(0), "my-arg") + expect(t, args.Get(1), "--") + expect(t, args.Get(2), "notAFlagAtAll") } func TestApp_VisibleCommands(t *testing.T) { app := NewApp() - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "frob", HelpName: "foo frob", @@ -296,7 +296,7 @@ func TestApp_VisibleCommands(t *testing.T) { } app.Setup() - expected := []Command{ + expected := []*Command{ app.Commands[0], app.Commands[2], // help } @@ -310,14 +310,22 @@ func TestApp_VisibleCommands(t *testing.T) { expect(t, fmt.Sprintf("%p", expectedCommand.Action), fmt.Sprintf("%p", actualCommand.Action)) } - // nil out funcs, as they cannot be compared - // (https://github.com/golang/go/issues/8554) - expectedCommand.Action = nil - actualCommand.Action = nil - - if !reflect.DeepEqual(expectedCommand, actualCommand) { - t.Errorf("expected\n%#v\n!=\n%#v", expectedCommand, actualCommand) - } + func() { + // nil out funcs, as they cannot be compared + // (https://github.com/golang/go/issues/8554) + expectedAction := expectedCommand.Action + actualAction := actualCommand.Action + defer func() { + expectedCommand.Action = expectedAction + actualCommand.Action = actualAction + }() + expectedCommand.Action = nil + actualCommand.Action = nil + + if !reflect.DeepEqual(expectedCommand, actualCommand) { + t.Errorf("expected\n%#v\n!=\n%#v", expectedCommand, actualCommand) + } + }() } } @@ -326,7 +334,7 @@ func TestApp_Float64Flag(t *testing.T) { app := NewApp() app.Flags = []Flag{ - Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"}, + &Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"}, } app.Action = func(c *Context) error { meters = c.Float64("height") @@ -343,11 +351,11 @@ func TestApp_ParseSliceFlags(t *testing.T) { var parsedStringSlice []string app := NewApp() - command := Command{ + command := &Command{ Name: "cmd", Flags: []Flag{ - IntSliceFlag{Name: "p", Value: NewIntSlice(), Usage: "set one or more ip addr"}, - StringSliceFlag{Name: "ip", Value: NewStringSlice(), Usage: "set one or more ports to open"}, + &IntSliceFlag{Name: "p", Value: NewIntSlice(), Usage: "set one or more ip addr"}, + &StringSliceFlag{Name: "ip", Value: NewStringSlice(), Usage: "set one or more ports to open"}, }, Action: func(c *Context) error { parsedIntSlice = c.IntSlice("p") @@ -357,7 +365,7 @@ func TestApp_ParseSliceFlags(t *testing.T) { return nil }, } - app.Commands = []Command{command} + app.Commands = []*Command{command} app.Run([]string{"", "cmd", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4", "my-arg"}) @@ -401,11 +409,11 @@ func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) { var parsedStringSlice []string app := NewApp() - command := Command{ + command := &Command{ Name: "cmd", Flags: []Flag{ - IntSliceFlag{Name: "a", Usage: "set numbers"}, - StringSliceFlag{Name: "str", Usage: "set strings"}, + &IntSliceFlag{Name: "a", Usage: "set numbers"}, + &StringSliceFlag{Name: "str", Usage: "set strings"}, }, Action: func(c *Context) error { parsedIntSlice = c.IntSlice("a") @@ -413,7 +421,7 @@ func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) { return nil }, } - app.Commands = []Command{command} + app.Commands = []*Command{command} app.Run([]string{"", "cmd", "-a", "2", "-str", "A", "my-arg"}) @@ -491,7 +499,7 @@ func TestApp_BeforeFunc(t *testing.T) { return nil } - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "sub", Action: func(c *Context) error { @@ -503,7 +511,7 @@ func TestApp_BeforeFunc(t *testing.T) { } app.Flags = []Flag{ - StringFlag{Name: "opt"}, + &StringFlag{Name: "opt"}, } // run with the Before() func succeeding @@ -583,7 +591,7 @@ func TestApp_AfterFunc(t *testing.T) { return nil } - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "sub", Action: func(c *Context) error { @@ -595,7 +603,7 @@ func TestApp_AfterFunc(t *testing.T) { } app.Flags = []Flag{ - StringFlag{Name: "opt"}, + &StringFlag{Name: "opt"}, } // run with the After() func succeeding @@ -639,7 +647,7 @@ func TestAppNoHelpFlag(t *testing.T) { HelpFlag = oldFlag }() - HelpFlag = BoolFlag{} + HelpFlag = nil app := NewApp() app.Writer = ioutil.Discard @@ -698,7 +706,7 @@ func TestApp_CommandNotFound(t *testing.T) { counts.CommandNotFound = counts.Total } - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "bar", Action: func(c *Context) error { @@ -765,7 +773,7 @@ func TestApp_OrderOfOperations(t *testing.T) { } app.After = afterNoError - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "bar", Action: func(c *Context) error { @@ -871,21 +879,21 @@ func TestApp_Run_CommandWithSubcommandHasHelpTopic(t *testing.T) { buf := new(bytes.Buffer) app.Writer = buf - subCmdBar := Command{ + subCmdBar := &Command{ Name: "bar", Usage: "does bar things", } - subCmdBaz := Command{ + subCmdBaz := &Command{ Name: "baz", Usage: "does baz things", } - cmd := Command{ + cmd := &Command{ Name: "foo", Description: "descriptive wall of text about how it does foo things", - Subcommands: []Command{subCmdBar, subCmdBaz}, + Subcommands: []*Command{subCmdBar, subCmdBaz}, } - app.Commands = []Command{cmd} + app.Commands = []*Command{cmd} err := app.Run(flagSet) if err != nil { @@ -916,16 +924,16 @@ func TestApp_Run_SubcommandFullPath(t *testing.T) { buf := new(bytes.Buffer) app.Writer = buf app.Name = "command" - subCmd := Command{ + subCmd := &Command{ Name: "bar", Usage: "does bar things", } - cmd := Command{ + cmd := &Command{ Name: "foo", Description: "foo commands", - Subcommands: []Command{subCmd}, + Subcommands: []*Command{subCmd}, } - app.Commands = []Command{cmd} + app.Commands = []*Command{cmd} err := app.Run([]string{"command", "foo", "bar", "--help"}) if err != nil { @@ -933,11 +941,14 @@ func TestApp_Run_SubcommandFullPath(t *testing.T) { } output := buf.String() - if !strings.Contains(output, "command foo bar - does bar things") { - t.Errorf("expected full path to subcommand: %s", output) + expected := "command foo bar - does bar things" + if !strings.Contains(output, expected) { + t.Errorf("expected %q in output: %s", expected, output) } - if !strings.Contains(output, "command foo bar [arguments...]") { - t.Errorf("expected full path to subcommand: %s", output) + + expected = "command foo bar [command options] [arguments...]" + if !strings.Contains(output, expected) { + t.Errorf("expected %q in output: %s", expected, output) } } @@ -946,17 +957,17 @@ func TestApp_Run_SubcommandHelpName(t *testing.T) { buf := new(bytes.Buffer) app.Writer = buf app.Name = "command" - subCmd := Command{ + subCmd := &Command{ Name: "bar", HelpName: "custom", Usage: "does bar things", } - cmd := Command{ + cmd := &Command{ Name: "foo", Description: "foo commands", - Subcommands: []Command{subCmd}, + Subcommands: []*Command{subCmd}, } - app.Commands = []Command{cmd} + app.Commands = []*Command{cmd} err := app.Run([]string{"command", "foo", "bar", "--help"}) if err != nil { @@ -964,11 +975,15 @@ func TestApp_Run_SubcommandHelpName(t *testing.T) { } output := buf.String() - if !strings.Contains(output, "custom - does bar things") { - t.Errorf("expected HelpName for subcommand: %s", output) + + expected := "custom - does bar things" + if !strings.Contains(output, expected) { + t.Errorf("expected %q in output: %s", expected, output) } - if !strings.Contains(output, "custom [arguments...]") { - t.Errorf("expected HelpName to subcommand: %s", output) + + expected = "custom [command options] [arguments...]" + if !strings.Contains(output, expected) { + t.Errorf("expected %q in output: %s", expected, output) } } @@ -977,17 +992,17 @@ func TestApp_Run_CommandHelpName(t *testing.T) { buf := new(bytes.Buffer) app.Writer = buf app.Name = "command" - subCmd := Command{ + subCmd := &Command{ Name: "bar", Usage: "does bar things", } - cmd := Command{ + cmd := &Command{ Name: "foo", HelpName: "custom", Description: "foo commands", - Subcommands: []Command{subCmd}, + Subcommands: []*Command{subCmd}, } - app.Commands = []Command{cmd} + app.Commands = []*Command{cmd} err := app.Run([]string{"command", "foo", "bar", "--help"}) if err != nil { @@ -995,11 +1010,15 @@ func TestApp_Run_CommandHelpName(t *testing.T) { } output := buf.String() - if !strings.Contains(output, "command foo bar - does bar things") { - t.Errorf("expected full path to subcommand: %s", output) + + expected := "command foo bar - does bar things" + if !strings.Contains(output, expected) { + t.Errorf("expected %q in output: %s", expected, output) } - if !strings.Contains(output, "command foo bar [arguments...]") { - t.Errorf("expected full path to subcommand: %s", output) + + expected = "command foo bar [command options] [arguments...]" + if !strings.Contains(output, expected) { + t.Errorf("expected %q in output: %s", expected, output) } } @@ -1008,17 +1027,17 @@ func TestApp_Run_CommandSubcommandHelpName(t *testing.T) { buf := new(bytes.Buffer) app.Writer = buf app.Name = "base" - subCmd := Command{ + subCmd := &Command{ Name: "bar", HelpName: "custom", Usage: "does bar things", } - cmd := Command{ + cmd := &Command{ Name: "foo", Description: "foo commands", - Subcommands: []Command{subCmd}, + Subcommands: []*Command{subCmd}, } - app.Commands = []Command{cmd} + app.Commands = []*Command{cmd} err := app.Run([]string{"command", "foo", "--help"}) if err != nil { @@ -1026,11 +1045,15 @@ func TestApp_Run_CommandSubcommandHelpName(t *testing.T) { } output := buf.String() - if !strings.Contains(output, "base foo - foo commands") { - t.Errorf("expected full path to subcommand: %s", output) + + expected := "base foo - foo commands" + if !strings.Contains(output, expected) { + t.Errorf("expected %q in output: %q", expected, output) } - if !strings.Contains(output, "base foo command [command options] [arguments...]") { - t.Errorf("expected full path to subcommand: %s", output) + + expected = "base foo command [command options] [arguments...]" + if !strings.Contains(output, expected) { + t.Errorf("expected %q in output: %q", expected, output) } } @@ -1100,7 +1123,7 @@ func TestApp_Run_Version(t *testing.T) { func TestApp_Run_Categories(t *testing.T) { app := NewApp() app.Name = "categories" - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "command1", Category: "1", @@ -1119,18 +1142,20 @@ func TestApp_Run_Categories(t *testing.T) { app.Run([]string{"categories"}) - expect := CommandCategories{ - &CommandCategory{ - Name: "1", - Commands: []Command{ - app.Commands[0], - app.Commands[1], + expect := &CommandCategories{ + Categories: []*CommandCategory{ + { + Name: "1", + Commands: []*Command{ + app.Commands[0], + app.Commands[1], + }, }, - }, - &CommandCategory{ - Name: "2", - Commands: []Command{ - app.Commands[2], + { + Name: "2", + Commands: []*Command{ + app.Commands[2], + }, }, }, } @@ -1149,7 +1174,7 @@ func TestApp_Run_Categories(t *testing.T) { func TestApp_VisibleCategories(t *testing.T) { app := NewApp() app.Name = "visible-categories" - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "command1", Category: "1", @@ -1171,13 +1196,13 @@ func TestApp_VisibleCategories(t *testing.T) { expected := []*CommandCategory{ { Name: "2", - Commands: []Command{ + Commands: []*Command{ app.Commands[1], }, }, { Name: "3", - Commands: []Command{ + Commands: []*Command{ app.Commands[2], }, }, @@ -1188,7 +1213,7 @@ func TestApp_VisibleCategories(t *testing.T) { app = NewApp() app.Name = "visible-categories" - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "command1", Category: "1", @@ -1211,7 +1236,7 @@ func TestApp_VisibleCategories(t *testing.T) { expected = []*CommandCategory{ { Name: "3", - Commands: []Command{ + Commands: []*Command{ app.Commands[2], }, }, @@ -1222,7 +1247,7 @@ func TestApp_VisibleCategories(t *testing.T) { app = NewApp() app.Name = "visible-categories" - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "command1", Category: "1", @@ -1270,9 +1295,9 @@ func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) { app := NewApp() - app.Commands = []Command{ + app.Commands = []*Command{ { - Subcommands: []Command{ + Subcommands: []*Command{ { Name: "sub", }, @@ -1299,7 +1324,7 @@ func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) { func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) { app := NewApp() app.Flags = []Flag{ - IntFlag{Name: "flag"}, + &IntFlag{Name: "flag"}, } app.OnUsageError = func(c *Context, err error, isSubcommand bool) error { if isSubcommand { @@ -1310,7 +1335,7 @@ func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) { } return errors.New("intercepted: " + err.Error()) } - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "bar", }, @@ -1329,7 +1354,7 @@ func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) { func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) { app := NewApp() app.Flags = []Flag{ - IntFlag{Name: "flag"}, + &IntFlag{Name: "flag"}, } app.OnUsageError = func(c *Context, err error, isSubcommand bool) error { if isSubcommand { @@ -1340,7 +1365,7 @@ func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) { } return errors.New("intercepted: " + err.Error()) } - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "bar", }, diff --git a/category.go b/category.go index 1a60550..9fb00f7 100644 --- a/category.go +++ b/category.go @@ -1,40 +1,48 @@ package cli // CommandCategories is a slice of *CommandCategory. -type CommandCategories []*CommandCategory +type CommandCategories struct { + Categories []*CommandCategory +} + +func NewCommandCategories() *CommandCategories { + return &CommandCategories{Categories: []*CommandCategory{}} +} // CommandCategory is a category containing commands. type CommandCategory struct { Name string - Commands Commands + Commands []*Command } -func (c CommandCategories) Less(i, j int) bool { - return c[i].Name < c[j].Name +func (c *CommandCategories) Less(i, j int) bool { + return c.Categories[i].Name < c.Categories[j].Name } -func (c CommandCategories) Len() int { - return len(c) +func (c *CommandCategories) Len() int { + return len(c.Categories) } -func (c CommandCategories) Swap(i, j int) { - c[i], c[j] = c[j], c[i] +func (c *CommandCategories) Swap(i, j int) { + c.Categories[i], c.Categories[j] = c.Categories[j], c.Categories[i] } // AddCommand adds a command to a category. -func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { - for _, commandCategory := range c { +func (c *CommandCategories) AddCommand(category string, command *Command) *CommandCategories { + for _, commandCategory := range c.Categories { if commandCategory.Name == category { commandCategory.Commands = append(commandCategory.Commands, command) return c } } - return append(c, &CommandCategory{Name: category, Commands: []Command{command}}) + c.Categories = append(c.Categories, + &CommandCategory{Name: category, Commands: []*Command{command}}) + return c } // VisibleCommands returns a slice of the Commands with Hidden=false -func (c *CommandCategory) VisibleCommands() []Command { - ret := []Command{} +func (c *CommandCategory) VisibleCommands() []*Command { + ret := []*Command{} for _, command := range c.Commands { if !command.Hidden { ret = append(ret, command) diff --git a/command.go b/command.go index c2f39d0..fe31a36 100644 --- a/command.go +++ b/command.go @@ -3,7 +3,6 @@ package cli import ( "fmt" "io/ioutil" - "reflect" "sort" "strings" ) @@ -37,7 +36,7 @@ type Command struct { // Execute this function if a usage error occurs. OnUsageError OnUsageErrorFunc // List of child commands - Subcommands Commands + Subcommands []*Command // List of flags to parse Flags []Flag // Treat all flags as normal arguments if true @@ -54,32 +53,26 @@ type Command struct { // FullName returns the full name of the command. // For subcommands this ensures that parent commands are part of the command path -func (c Command) FullName() string { +func (c *Command) FullName() string { if c.commandNamePath == nil { return c.Name } return strings.Join(c.commandNamePath, " ") } -// Commands is a slice of Command -type Commands []Command - // Run invokes the command given the context, parses ctx.Args() to generate command-specific flags -func (c Command) Run(ctx *Context) (err error) { +func (c *Command) Run(ctx *Context) (err error) { if len(c.Subcommands) > 0 { return c.startApp(ctx) } - if !c.HideHelp && !reflect.DeepEqual(HelpFlag, BoolFlag{}) { + if !c.HideHelp && HelpFlag != nil { // append help to flags - c.Flags = append( - c.Flags, - HelpFlag, - ) + c.appendFlag(HelpFlag) } if ctx.App.EnableBashCompletion { - c.Flags = append(c.Flags, BashCompletionFlag) + c.appendFlag(BashCompletionFlag) } set := flagSet(c.Name, c.Flags) @@ -156,13 +149,12 @@ func (c Command) Run(ctx *Context) (err error) { } // Names returns the names including short names and aliases. -func (c Command) Names() []string { - names := []string{c.Name} - return append(names, c.Aliases...) +func (c *Command) Names() []string { + return append([]string{c.Name}, c.Aliases...) } // HasName returns true if Command.Name matches given name -func (c Command) HasName(name string) bool { +func (c *Command) HasName(name string) bool { for _, n := range c.Names() { if n == name { return true @@ -171,7 +163,7 @@ func (c Command) HasName(name string) bool { return false } -func (c Command) startApp(ctx *Context) error { +func (c *Command) startApp(ctx *Context) error { app := NewApp() app.Metadata = ctx.App.Metadata // set the name and usage @@ -201,7 +193,7 @@ func (c Command) startApp(ctx *Context) error { app.Compiled = ctx.App.Compiled app.Writer = ctx.App.Writer - app.categories = CommandCategories{} + app.categories = NewCommandCategories() for _, command := range c.Subcommands { app.categories = app.categories.AddCommand(command.Category, command) } @@ -231,6 +223,22 @@ func (c Command) startApp(ctx *Context) error { } // VisibleFlags returns a slice of the Flags with Hidden=false -func (c Command) VisibleFlags() []Flag { +func (c *Command) VisibleFlags() []Flag { return visibleFlags(c.Flags) } + +func (c *Command) appendFlag(fl Flag) { + if !hasFlag(c.Flags, fl) { + c.Flags = append(c.Flags, fl) + } +} + +func hasCommand(commands []*Command, command *Command) bool { + for _, existing := range commands { + if command == existing { + return true + } + } + + return false +} diff --git a/command_test.go b/command_test.go index a03f8bb..033149f 100644 --- a/command_test.go +++ b/command_test.go @@ -42,13 +42,13 @@ func TestCommandFlagParsing(t *testing.T) { err := command.Run(context) expect(t, err, c.expectedErr) - expect(t, []string(context.Args()), c.testArgs) + expect(t, context.Args().Slice(), c.testArgs) } } func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { app := NewApp() - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "bar", Before: func(c *Context) error { @@ -75,11 +75,11 @@ func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) { app := NewApp() - app.Commands = []Command{ + app.Commands = []*Command{ { Name: "bar", Flags: []Flag{ - IntFlag{Name: "flag"}, + &IntFlag{Name: "flag"}, }, OnUsageError: func(c *Context, err error, _ bool) error { if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { diff --git a/context.go b/context.go index 3202c75..0728da9 100644 --- a/context.go +++ b/context.go @@ -14,7 +14,7 @@ import ( // parsed command-line options. type Context struct { App *App - Command Command + Command *Command flagSet *flag.FlagSet parentContext *Context @@ -147,56 +147,69 @@ func (c *Context) Lineage() []*Context { return lineage } -// Args contains apps console arguments -type Args []string - // Args returns the command line arguments associated with the context. -func (c *Context) Args() Args { - args := Args(c.flagSet.Args()) - return args +func (c *Context) Args() *Args { + return &Args{slice: c.flagSet.Args()} } // NArg returns the number of the command line arguments. func (c *Context) NArg() int { - return len(c.Args()) + return c.Args().Len() +} + +// Args wraps a string slice with some convenience methods +type Args struct { + slice []string } // Get returns the nth argument, or else a blank string -func (a Args) Get(n int) string { - if len(a) > n { - return a[n] +func (a *Args) Get(n int) string { + if len(a.slice) > n { + return a.slice[n] } return "" } // First returns the first argument, or else a blank string -func (a Args) First() string { +func (a *Args) First() string { return a.Get(0) } // Tail returns the rest of the arguments (not the first one) // or else an empty string slice -func (a Args) Tail() []string { - if len(a) >= 2 { - return []string(a)[1:] +func (a *Args) Tail() []string { + if a.Len() >= 2 { + return a.slice[1:] } return []string{} } +// Len returns the length of the wrapped slice +func (a *Args) Len() int { + return len(a.slice) +} + // Present checks if there are any arguments present -func (a Args) Present() bool { - return len(a) != 0 +func (a *Args) Present() bool { + return a.Len() != 0 } // Swap swaps arguments at the given indexes -func (a Args) Swap(from, to int) error { - if from >= len(a) || to >= len(a) { +func (a *Args) Swap(from, to int) error { + if from >= a.Len() || to >= a.Len() { return errors.New("index out of range") } - a[from], a[to] = a[to], a[from] + a.slice[from], a.slice[to] = a.slice[to], a.slice[from] return nil } +// Slice returns a copy of the internal slice +func (a *Args) Slice() []string { + ret := make([]string, len(a.slice)) + copy(ret, a.slice) + return ret +} + func lookupFlagSet(name string, ctx *Context) *flag.FlagSet { for _, c := range ctx.Lineage() { if f := c.flagSet.Lookup(name); f != nil { diff --git a/context_test.go b/context_test.go index 8147965..229275c 100644 --- a/context_test.go +++ b/context_test.go @@ -15,7 +15,7 @@ func TestNewContext(t *testing.T) { globalSet.Int("myflag", 42, "doc") globalSet.Float64("myflag64", float64(47), "doc") globalCtx := NewContext(nil, globalSet, nil) - command := Command{Name: "mycommand"} + command := &Command{Name: "mycommand"} c := NewContext(nil, set, globalCtx) c.Command = command expect(t, c.Int("myflag"), 12) @@ -63,7 +63,7 @@ func TestContext_Args(t *testing.T) { set.Bool("myflag", false, "doc") c := NewContext(nil, set, nil) set.Parse([]string{"--myflag", "bat", "baz"}) - expect(t, len(c.Args()), 2) + expect(t, c.Args().Len(), 2) expect(t, c.Bool("myflag"), true) } diff --git a/flag.go b/flag.go index 067e9ee..dd07d27 100644 --- a/flag.go +++ b/flag.go @@ -17,22 +17,22 @@ const defaultPlaceholder = "value" var slPfx = fmt.Sprintf("sl:::%d:::", time.Now().UTC().UnixNano()) // BashCompletionFlag enables bash-completion for all commands and subcommands -var BashCompletionFlag = BoolFlag{ +var BashCompletionFlag = &BoolFlag{ Name: "generate-bash-completion", Hidden: true, } // VersionFlag prints the version for the application -var VersionFlag = BoolFlag{ +var VersionFlag = &BoolFlag{ 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 the flag. The subcommand +// Set to nil to disable the flag. The subcommand // will still be added unless HideHelp is set to true. -var HelpFlag = BoolFlag{ +var HelpFlag = &BoolFlag{ Name: "help", Aliases: []string{"h"}, Usage: "show help", @@ -85,13 +85,13 @@ type GenericFlag struct { // String returns the string representation of the generic flag to display the // help text to the user (uses the String() method of the generic flag to show // the value) -func (f GenericFlag) String() string { +func (f *GenericFlag) String() string { return FlagStringer(f) } // Apply takes the flagset and calls Set on the generic flag with the value // 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 if f.EnvVars != nil { for _, envVar := range f.EnvVars { @@ -109,7 +109,7 @@ func (f GenericFlag) Apply(set *flag.FlagSet) { } // Names returns the names of a flag. -func (f GenericFlag) Names() []string { +func (f *GenericFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } @@ -170,12 +170,12 @@ type StringSliceFlag struct { } // String returns the usage -func (f StringSliceFlag) String() string { +func (f *StringSliceFlag) String() string { return FlagStringer(f) } // 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.EnvVars != nil { for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) @@ -201,7 +201,7 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) { } // Names returns the name of a flag. -func (f StringSliceFlag) Names() []string { +func (f *StringSliceFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } @@ -277,12 +277,12 @@ type IntSliceFlag struct { } // String returns the usage -func (f IntSliceFlag) String() string { +func (f *IntSliceFlag) String() string { return FlagStringer(f) } // 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.EnvVars != nil { for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) @@ -311,7 +311,7 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) { } // Names returns the name of the flag. -func (f IntSliceFlag) Names() []string { +func (f *IntSliceFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } @@ -327,12 +327,12 @@ type BoolFlag struct { } // String returns a readable representation of this value (for usage defaults) -func (f BoolFlag) String() string { +func (f *BoolFlag) String() string { return FlagStringer(f) } // 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.EnvVars != nil { for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) @@ -356,7 +356,7 @@ func (f BoolFlag) Apply(set *flag.FlagSet) { } // Names returns the name of the flag. -func (f BoolFlag) Names() []string { +func (f *BoolFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } @@ -372,12 +372,12 @@ type StringFlag struct { } // String returns the usage -func (f StringFlag) String() string { +func (f *StringFlag) String() string { return FlagStringer(f) } // 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.EnvVars != nil { for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) @@ -398,7 +398,7 @@ func (f StringFlag) Apply(set *flag.FlagSet) { } // Names returns the name of the flag. -func (f StringFlag) Names() []string { +func (f *StringFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } @@ -415,12 +415,12 @@ type IntFlag struct { } // String returns the usage -func (f IntFlag) String() string { +func (f *IntFlag) String() string { return FlagStringer(f) } // 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.EnvVars != nil { for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) @@ -444,7 +444,7 @@ func (f IntFlag) Apply(set *flag.FlagSet) { } // Names returns the name of the flag. -func (f IntFlag) Names() []string { +func (f *IntFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } @@ -461,12 +461,12 @@ type DurationFlag struct { } // String returns a readable representation of this value (for usage defaults) -func (f DurationFlag) String() string { +func (f *DurationFlag) String() string { return FlagStringer(f) } // 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.EnvVars != nil { for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) @@ -490,7 +490,7 @@ func (f DurationFlag) Apply(set *flag.FlagSet) { } // Names returns the name of the flag. -func (f DurationFlag) Names() []string { +func (f *DurationFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } @@ -507,12 +507,12 @@ type Float64Flag struct { } // String returns the usage -func (f Float64Flag) String() string { +func (f *Float64Flag) String() string { return FlagStringer(f) } // 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.EnvVars != nil { for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) @@ -535,14 +535,14 @@ func (f Float64Flag) Apply(set *flag.FlagSet) { } // Names returns the name of the flag. -func (f Float64Flag) Names() []string { +func (f *Float64Flag) Names() []string { return append([]string{f.Name}, f.Aliases...) } func visibleFlags(fl []Flag) []Flag { visible := []Flag{} for _, flag := range fl { - if !reflect.ValueOf(flag).FieldByName("Hidden").Bool() { + if !flagValue(flag).FieldByName("Hidden").Bool() { visible = append(visible, flag) } } @@ -644,10 +644,10 @@ func stringifyFlag(f Flag) string { fv := flagValue(f) switch f.(type) { - case IntSliceFlag: - return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyIntSliceFlag(f.(IntSliceFlag))) - case StringSliceFlag: - return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyStringSliceFlag(f.(StringSliceFlag))) + case *IntSliceFlag: + return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyIntSliceFlag(f.(*IntSliceFlag))) + case *StringSliceFlag: + return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyStringSliceFlag(f.(*StringSliceFlag))) } placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) @@ -679,7 +679,7 @@ func stringifyFlag(f Flag) string { fmt.Sprintf("%s\t%s", prefixedNames(f.Names(), placeholder), usageWithDefault)) } -func stringifyIntSliceFlag(f IntSliceFlag) string { +func stringifyIntSliceFlag(f *IntSliceFlag) string { defaultVals := []string{} if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { @@ -690,7 +690,7 @@ func stringifyIntSliceFlag(f IntSliceFlag) string { return stringifySliceFlag(f.Usage, append([]string{f.Name}, f.Aliases...), defaultVals) } -func stringifyStringSliceFlag(f StringSliceFlag) string { +func stringifyStringSliceFlag(f *StringSliceFlag) string { defaultVals := []string{} if f.Value != nil && len(f.Value.Value()) > 0 { for _, s := range f.Value.Value() { @@ -717,3 +717,13 @@ func stringifySliceFlag(usage string, names, defaultVals []string) string { usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) return fmt.Sprintf("%s\t%s", prefixedNames(names, placeholder), usageWithDefault) } + +func hasFlag(flags []Flag, fl Flag) bool { + for _, existing := range flags { + if fl == existing { + return true + } + } + + return false +} diff --git a/flag_test.go b/flag_test.go index 8ff64fb..15166c7 100644 --- a/flag_test.go +++ b/flag_test.go @@ -20,7 +20,7 @@ var boolFlagTests = []struct { func TestBoolFlagHelpOutput(t *testing.T) { for _, test := range boolFlagTests { - flag := BoolFlag{Name: test.name} + flag := &BoolFlag{Name: test.name} output := flag.String() if output != test.expected { @@ -46,7 +46,7 @@ var stringFlagTests = []struct { func TestStringFlagHelpOutput(t *testing.T) { for _, test := range stringFlagTests { - flag := StringFlag{Name: test.name, Aliases: test.aliases, 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 { @@ -59,7 +59,7 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_FOO", "derp") for _, test := range stringFlagTests { - flag := StringFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_FOO"}} + flag := &StringFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_FOO"}} output := flag.String() expectedSuffix := " [$APP_FOO]" @@ -87,7 +87,7 @@ var stringSliceFlagTests = []struct { func TestStringSliceFlagHelpOutput(t *testing.T) { for _, test := range stringSliceFlagTests { - flag := StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value} + flag := &StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value} output := flag.String() if output != test.expected { @@ -100,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, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_QWWX"}} + flag := &StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_QWWX"}} output := flag.String() expectedSuffix := " [$APP_QWWX]" @@ -123,7 +123,7 @@ var intFlagTests = []struct { func TestIntFlagHelpOutput(t *testing.T) { for _, test := range intFlagTests { - flag := IntFlag{Name: test.name, Value: 9} + flag := &IntFlag{Name: test.name, Value: 9} output := flag.String() if output != test.expected { @@ -136,7 +136,7 @@ func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_BAR", "2") for _, test := range intFlagTests { - flag := IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} + flag := &IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := flag.String() expectedSuffix := " [$APP_BAR]" @@ -159,7 +159,7 @@ var durationFlagTests = []struct { func TestDurationFlagHelpOutput(t *testing.T) { for _, test := range durationFlagTests { - flag := DurationFlag{Name: test.name, Value: 1 * time.Second} + flag := &DurationFlag{Name: test.name, Value: 1 * time.Second} output := flag.String() if output != test.expected { @@ -172,7 +172,7 @@ func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_BAR", "2h3m6s") for _, test := range durationFlagTests { - flag := DurationFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} + flag := &DurationFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := flag.String() expectedSuffix := " [$APP_BAR]" @@ -198,7 +198,7 @@ var intSliceFlagTests = []struct { func TestIntSliceFlagHelpOutput(t *testing.T) { for _, test := range intSliceFlagTests { - flag := IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value} + flag := &IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value} output := flag.String() if output != test.expected { @@ -211,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, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_SMURF"}} + flag := &IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_SMURF"}} output := flag.String() expectedSuffix := " [$APP_SMURF]" @@ -234,7 +234,7 @@ var float64FlagTests = []struct { func TestFloat64FlagHelpOutput(t *testing.T) { for _, test := range float64FlagTests { - flag := Float64Flag{Name: test.name, Value: float64(0.1)} + flag := &Float64Flag{Name: test.name, Value: float64(0.1)} output := flag.String() if output != test.expected { @@ -247,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, EnvVars: []string{"APP_BAZ"}} + flag := &Float64Flag{Name: test.name, EnvVars: []string{"APP_BAZ"}} output := flag.String() expectedSuffix := " [$APP_BAZ]" @@ -271,7 +271,7 @@ var genericFlagTests = []struct { func TestGenericFlagHelpOutput(t *testing.T) { for _, test := range genericFlagTests { - flag := GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"} + flag := &GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"} output := flag.String() if output != test.expected { @@ -284,7 +284,7 @@ func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_ZAP", "3") for _, test := range genericFlagTests { - flag := GenericFlag{Name: test.name, EnvVars: []string{"APP_ZAP"}} + flag := &GenericFlag{Name: test.name, EnvVars: []string{"APP_ZAP"}} output := flag.String() expectedSuffix := " [$APP_ZAP]" @@ -300,7 +300,7 @@ func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { func TestParseMultiString(t *testing.T) { (&App{ Flags: []Flag{ - StringFlag{Name: "serve", Aliases: []string{"s"}}, + &StringFlag{Name: "serve", Aliases: []string{"s"}}, }, Action: func(ctx *Context) error { if ctx.String("serve") != "10" { @@ -318,7 +318,7 @@ func TestParseDestinationString(t *testing.T) { var dest string a := App{ Flags: []Flag{ - StringFlag{ + &StringFlag{ Name: "dest", Destination: &dest, }, @@ -338,7 +338,7 @@ func TestParseMultiStringFromEnv(t *testing.T) { os.Setenv("APP_COUNT", "20") (&App{ Flags: []Flag{ - StringFlag{Name: "count", Aliases: []string{"c"}, EnvVars: []string{"APP_COUNT"}}, + &StringFlag{Name: "count", Aliases: []string{"c"}, EnvVars: []string{"APP_COUNT"}}, }, Action: func(ctx *Context) error { if ctx.String("count") != "20" { @@ -357,7 +357,7 @@ func TestParseMultiStringFromEnvCascade(t *testing.T) { os.Setenv("APP_COUNT", "20") (&App{ Flags: []Flag{ - StringFlag{Name: "count", Aliases: []string{"c"}, EnvVars: []string{"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" { @@ -374,7 +374,7 @@ func TestParseMultiStringFromEnvCascade(t *testing.T) { func TestParseMultiStringSlice(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice()}, + &StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice()}, }, Action: func(ctx *Context) error { expected := []string{"10", "20"} @@ -392,7 +392,7 @@ func TestParseMultiStringSlice(t *testing.T) { func TestParseMultiStringSliceWithDefaults(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice("9", "2")}, + &StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice("9", "2")}, }, Action: func(ctx *Context) error { expected := []string{"10", "20"} @@ -410,7 +410,7 @@ func TestParseMultiStringSliceWithDefaults(t *testing.T) { func TestParseMultiStringSliceWithDefaultsUnset(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "serve", Aliases: []string{"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"}) { @@ -430,7 +430,7 @@ func TestParseMultiStringSliceFromEnv(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice(), EnvVars: []string{"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"}) { @@ -450,7 +450,7 @@ func TestParseMultiStringSliceFromEnvWithDefaults(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice("1", "2", "5"), EnvVars: []string{"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"}) { @@ -470,7 +470,7 @@ func TestParseMultiStringSliceFromEnvCascade(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice(), EnvVars: []string{"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"}) { @@ -490,7 +490,7 @@ func TestParseMultiStringSliceFromEnvCascadeWithDefaults(t *testing.T) { (&App{ Flags: []Flag{ - StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice("1", "2", "5"), EnvVars: []string{"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"}) { @@ -507,7 +507,7 @@ func TestParseMultiStringSliceFromEnvCascadeWithDefaults(t *testing.T) { func TestParseMultiInt(t *testing.T) { a := App{ Flags: []Flag{ - IntFlag{Name: "serve", Aliases: []string{"s"}}, + &IntFlag{Name: "serve", Aliases: []string{"s"}}, }, Action: func(ctx *Context) error { if ctx.Int("serve") != 10 { @@ -526,7 +526,7 @@ func TestParseDestinationInt(t *testing.T) { var dest int a := App{ Flags: []Flag{ - IntFlag{ + &IntFlag{ Name: "dest", Destination: &dest, }, @@ -546,7 +546,7 @@ func TestParseMultiIntFromEnv(t *testing.T) { os.Setenv("APP_TIMEOUT_SECONDS", "10") a := App{ Flags: []Flag{ - IntFlag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"APP_TIMEOUT_SECONDS"}}, + &IntFlag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"APP_TIMEOUT_SECONDS"}}, }, Action: func(ctx *Context) error { if ctx.Int("timeout") != 10 { @@ -566,7 +566,7 @@ func TestParseMultiIntFromEnvCascade(t *testing.T) { os.Setenv("APP_TIMEOUT_SECONDS", "10") a := App{ Flags: []Flag{ - IntFlag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"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 { @@ -584,7 +584,7 @@ func TestParseMultiIntFromEnvCascade(t *testing.T) { func TestParseMultiIntSlice(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "serve", Aliases: []string{"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}) { @@ -601,7 +601,7 @@ func TestParseMultiIntSlice(t *testing.T) { func TestParseMultiIntSliceWithDefaults(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "serve", Aliases: []string{"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}) { @@ -618,7 +618,7 @@ func TestParseMultiIntSliceWithDefaults(t *testing.T) { func TestParseMultiIntSliceWithDefaultsUnset(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "serve", Aliases: []string{"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}) { @@ -638,7 +638,7 @@ func TestParseMultiIntSliceFromEnv(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(), EnvVars: []string{"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}) { @@ -658,7 +658,7 @@ func TestParseMultiIntSliceFromEnvWithDefaults(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(1, 2, 5), EnvVars: []string{"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}) { @@ -678,7 +678,7 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { (&App{ Flags: []Flag{ - IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(), EnvVars: []string{"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}) { @@ -695,7 +695,7 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { func TestParseMultiFloat64(t *testing.T) { a := App{ Flags: []Flag{ - Float64Flag{Name: "serve", Aliases: []string{"s"}}, + &Float64Flag{Name: "serve", Aliases: []string{"s"}}, }, Action: func(ctx *Context) error { if ctx.Float64("serve") != 10.2 { @@ -714,7 +714,7 @@ func TestParseDestinationFloat64(t *testing.T) { var dest float64 a := App{ Flags: []Flag{ - Float64Flag{ + &Float64Flag{ Name: "dest", Destination: &dest, }, @@ -734,7 +734,7 @@ func TestParseMultiFloat64FromEnv(t *testing.T) { os.Setenv("APP_TIMEOUT_SECONDS", "15.5") a := App{ Flags: []Flag{ - Float64Flag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"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 { @@ -754,7 +754,7 @@ func TestParseMultiFloat64FromEnvCascade(t *testing.T) { os.Setenv("APP_TIMEOUT_SECONDS", "15.5") a := App{ Flags: []Flag{ - Float64Flag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"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 { @@ -772,7 +772,7 @@ func TestParseMultiFloat64FromEnvCascade(t *testing.T) { func TestParseMultiBool(t *testing.T) { a := App{ Flags: []Flag{ - BoolFlag{Name: "serve", Aliases: []string{"s"}}, + &BoolFlag{Name: "serve", Aliases: []string{"s"}}, }, Action: func(ctx *Context) error { if ctx.Bool("serve") != true { @@ -791,7 +791,7 @@ func TestParseDestinationBool(t *testing.T) { var dest bool a := App{ Flags: []Flag{ - BoolFlag{ + &BoolFlag{ Name: "dest", Destination: &dest, }, @@ -811,7 +811,7 @@ func TestParseMultiBoolFromEnv(t *testing.T) { os.Setenv("APP_DEBUG", "1") a := App{ Flags: []Flag{ - BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"APP_DEBUG"}}, + &BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"APP_DEBUG"}}, }, Action: func(ctx *Context) error { if ctx.Bool("debug") != true { @@ -831,7 +831,7 @@ func TestParseMultiBoolFromEnvCascade(t *testing.T) { os.Setenv("APP_DEBUG", "1") a := App{ Flags: []Flag{ - BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"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 { @@ -849,7 +849,7 @@ func TestParseMultiBoolFromEnvCascade(t *testing.T) { func TestParseMultiBoolTrue(t *testing.T) { a := App{ Flags: []Flag{ - BoolFlag{Name: "implode", Aliases: []string{"i"}, Value: true}, + &BoolFlag{Name: "implode", Aliases: []string{"i"}, Value: true}, }, Action: func(ctx *Context) error { if ctx.Bool("implode") { @@ -869,7 +869,7 @@ func TestParseDestinationBoolTrue(t *testing.T) { a := App{ Flags: []Flag{ - BoolFlag{ + &BoolFlag{ Name: "dest", Value: true, Destination: &dest, @@ -890,7 +890,7 @@ func TestParseMultiBoolTrueFromEnv(t *testing.T) { os.Setenv("APP_DEBUG", "0") a := App{ Flags: []Flag{ - BoolFlag{ + &BoolFlag{ Name: "debug", Aliases: []string{"d"}, Value: true, @@ -915,7 +915,7 @@ func TestParseMultiBoolTrueFromEnvCascade(t *testing.T) { os.Setenv("APP_DEBUG", "0") a := App{ Flags: []Flag{ - BoolFlag{ + &BoolFlag{ Name: "debug", Aliases: []string{"d"}, Value: true, @@ -956,7 +956,7 @@ func (p *Parser) String() string { func TestParseGeneric(t *testing.T) { a := App{ Flags: []Flag{ - GenericFlag{Name: "serve", Aliases: []string{"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"}) { @@ -976,7 +976,7 @@ func TestParseGenericFromEnv(t *testing.T) { os.Setenv("APP_SERVE", "20,30") a := App{ Flags: []Flag{ - GenericFlag{ + &GenericFlag{ Name: "serve", Aliases: []string{"s"}, Value: &Parser{}, @@ -1001,7 +1001,7 @@ func TestParseGenericFromEnvCascade(t *testing.T) { os.Setenv("APP_FOO", "99,2000") a := App{ Flags: []Flag{ - GenericFlag{ + &GenericFlag{ Name: "foos", Value: &Parser{}, EnvVars: []string{"COMPAT_FOO", "APP_FOO"}, diff --git a/help.go b/help.go index 2675289..d02f7fd 100644 --- a/help.go +++ b/help.go @@ -74,7 +74,7 @@ OPTIONS: {{end}}{{end}} ` -var helpCommand = Command{ +var helpCommand = &Command{ Name: "help", Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", @@ -90,7 +90,7 @@ var helpCommand = Command{ }, } -var helpSubcommand = Command{ +var helpSubcommand = &Command{ Name: "help", Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", @@ -158,7 +158,15 @@ func ShowCommandHelp(ctx *Context, command string) error { // ShowSubcommandHelp prints help for the given subcommand func ShowSubcommandHelp(c *Context) error { - return ShowCommandHelp(c, c.Command.Name) + if c == nil { + return nil + } + + if c.Command != nil { + return ShowCommandHelp(c, c.Command.Name) + } + + return ShowCommandHelp(c, "") } // ShowVersion prints the version number of the App diff --git a/help_test.go b/help_test.go index 9ca4c0d..4d1dedc 100644 --- a/help_test.go +++ b/help_test.go @@ -59,7 +59,7 @@ func Test_Help_Custom_Flags(t *testing.T) { HelpFlag = oldFlag }() - HelpFlag = BoolFlag{ + HelpFlag = &BoolFlag{ Name: "help", Aliases: []string{"x"}, Usage: "show help", @@ -67,7 +67,7 @@ func Test_Help_Custom_Flags(t *testing.T) { app := App{ Flags: []Flag{ - BoolFlag{Name: "foo", Aliases: []string{"h"}}, + &BoolFlag{Name: "foo", Aliases: []string{"h"}}, }, Action: func(ctx *Context) error { if ctx.Bool("h") != true { @@ -90,7 +90,7 @@ func Test_Version_Custom_Flags(t *testing.T) { VersionFlag = oldFlag }() - VersionFlag = BoolFlag{ + VersionFlag = &BoolFlag{ Name: "version", Aliases: []string{"V"}, Usage: "show version", @@ -98,7 +98,7 @@ func Test_Version_Custom_Flags(t *testing.T) { app := App{ Flags: []Flag{ - BoolFlag{Name: "foo", Aliases: []string{"v"}}, + &BoolFlag{Name: "foo", Aliases: []string{"v"}}, }, Action: func(ctx *Context) error { if ctx.Bool("v") != true { @@ -173,7 +173,7 @@ func Test_helpSubcommand_Action_ErrorIfNoTopic(t *testing.T) { func TestShowAppHelp_CommandAliases(t *testing.T) { app := &App{ - Commands: []Command{ + Commands: []*Command{ { Name: "frobbly", Aliases: []string{"fr", "frob"}, @@ -195,7 +195,7 @@ func TestShowAppHelp_CommandAliases(t *testing.T) { func TestShowCommandHelp_CommandAliases(t *testing.T) { app := &App{ - Commands: []Command{ + Commands: []*Command{ { Name: "frobbly", Aliases: []string{"fr", "frob", "bork"}, @@ -221,7 +221,7 @@ func TestShowCommandHelp_CommandAliases(t *testing.T) { func TestShowSubcommandHelp_CommandAliases(t *testing.T) { app := &App{ - Commands: []Command{ + Commands: []*Command{ { Name: "frobbly", Aliases: []string{"fr", "frob", "bork"}, @@ -243,7 +243,7 @@ func TestShowSubcommandHelp_CommandAliases(t *testing.T) { func TestShowAppHelp_HiddenCommand(t *testing.T) { app := &App{ - Commands: []Command{ + Commands: []*Command{ { Name: "frobbly", Action: func(ctx *Context) error {