Sorry, the diff of this file is not supported yet
| *.coverprofile | ||
| node_modules/ |
| package altsrc | ||
| //go:generate python ../generate-flag-types altsrc -i ../flag-types.json -o flag_generated.go |
| package altsrc | ||
| import ( | ||
| "flag" | ||
| "gopkg.in/urfave/cli.v1" | ||
| ) | ||
| // WARNING: This file is generated! | ||
| // BoolFlag is the flag type that wraps cli.BoolFlag to allow | ||
| // for other values to be specified | ||
| type BoolFlag struct { | ||
| cli.BoolFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewBoolFlag creates a new BoolFlag | ||
| func NewBoolFlag(fl cli.BoolFlag) *BoolFlag { | ||
| return &BoolFlag{BoolFlag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped BoolFlag.Apply | ||
| func (f *BoolFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.BoolFlag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped BoolFlag.ApplyWithError | ||
| func (f *BoolFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.BoolFlag.ApplyWithError(set) | ||
| } | ||
| // BoolTFlag is the flag type that wraps cli.BoolTFlag to allow | ||
| // for other values to be specified | ||
| type BoolTFlag struct { | ||
| cli.BoolTFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewBoolTFlag creates a new BoolTFlag | ||
| func NewBoolTFlag(fl cli.BoolTFlag) *BoolTFlag { | ||
| return &BoolTFlag{BoolTFlag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped BoolTFlag.Apply | ||
| func (f *BoolTFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.BoolTFlag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped BoolTFlag.ApplyWithError | ||
| func (f *BoolTFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.BoolTFlag.ApplyWithError(set) | ||
| } | ||
| // DurationFlag is the flag type that wraps cli.DurationFlag to allow | ||
| // for other values to be specified | ||
| type DurationFlag struct { | ||
| cli.DurationFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewDurationFlag creates a new DurationFlag | ||
| func NewDurationFlag(fl cli.DurationFlag) *DurationFlag { | ||
| return &DurationFlag{DurationFlag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped DurationFlag.Apply | ||
| func (f *DurationFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.DurationFlag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped DurationFlag.ApplyWithError | ||
| func (f *DurationFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.DurationFlag.ApplyWithError(set) | ||
| } | ||
| // Float64Flag is the flag type that wraps cli.Float64Flag to allow | ||
| // for other values to be specified | ||
| type Float64Flag struct { | ||
| cli.Float64Flag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewFloat64Flag creates a new Float64Flag | ||
| func NewFloat64Flag(fl cli.Float64Flag) *Float64Flag { | ||
| return &Float64Flag{Float64Flag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped Float64Flag.Apply | ||
| func (f *Float64Flag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.Float64Flag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped Float64Flag.ApplyWithError | ||
| func (f *Float64Flag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.Float64Flag.ApplyWithError(set) | ||
| } | ||
| // GenericFlag is the flag type that wraps cli.GenericFlag to allow | ||
| // for other values to be specified | ||
| type GenericFlag struct { | ||
| cli.GenericFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewGenericFlag creates a new GenericFlag | ||
| func NewGenericFlag(fl cli.GenericFlag) *GenericFlag { | ||
| return &GenericFlag{GenericFlag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped GenericFlag.Apply | ||
| func (f *GenericFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.GenericFlag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped GenericFlag.ApplyWithError | ||
| func (f *GenericFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.GenericFlag.ApplyWithError(set) | ||
| } | ||
| // Int64Flag is the flag type that wraps cli.Int64Flag to allow | ||
| // for other values to be specified | ||
| type Int64Flag struct { | ||
| cli.Int64Flag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewInt64Flag creates a new Int64Flag | ||
| func NewInt64Flag(fl cli.Int64Flag) *Int64Flag { | ||
| return &Int64Flag{Int64Flag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped Int64Flag.Apply | ||
| func (f *Int64Flag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.Int64Flag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped Int64Flag.ApplyWithError | ||
| func (f *Int64Flag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.Int64Flag.ApplyWithError(set) | ||
| } | ||
| // IntFlag is the flag type that wraps cli.IntFlag to allow | ||
| // for other values to be specified | ||
| type IntFlag struct { | ||
| cli.IntFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewIntFlag creates a new IntFlag | ||
| func NewIntFlag(fl cli.IntFlag) *IntFlag { | ||
| return &IntFlag{IntFlag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped IntFlag.Apply | ||
| func (f *IntFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.IntFlag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped IntFlag.ApplyWithError | ||
| func (f *IntFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.IntFlag.ApplyWithError(set) | ||
| } | ||
| // IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow | ||
| // for other values to be specified | ||
| type IntSliceFlag struct { | ||
| cli.IntSliceFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewIntSliceFlag creates a new IntSliceFlag | ||
| func NewIntSliceFlag(fl cli.IntSliceFlag) *IntSliceFlag { | ||
| return &IntSliceFlag{IntSliceFlag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped IntSliceFlag.Apply | ||
| func (f *IntSliceFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.IntSliceFlag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped IntSliceFlag.ApplyWithError | ||
| func (f *IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.IntSliceFlag.ApplyWithError(set) | ||
| } | ||
| // Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow | ||
| // for other values to be specified | ||
| type Int64SliceFlag struct { | ||
| cli.Int64SliceFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewInt64SliceFlag creates a new Int64SliceFlag | ||
| func NewInt64SliceFlag(fl cli.Int64SliceFlag) *Int64SliceFlag { | ||
| return &Int64SliceFlag{Int64SliceFlag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped Int64SliceFlag.Apply | ||
| func (f *Int64SliceFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.Int64SliceFlag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped Int64SliceFlag.ApplyWithError | ||
| func (f *Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.Int64SliceFlag.ApplyWithError(set) | ||
| } | ||
| // StringFlag is the flag type that wraps cli.StringFlag to allow | ||
| // for other values to be specified | ||
| type StringFlag struct { | ||
| cli.StringFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewStringFlag creates a new StringFlag | ||
| func NewStringFlag(fl cli.StringFlag) *StringFlag { | ||
| return &StringFlag{StringFlag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped StringFlag.Apply | ||
| func (f *StringFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.StringFlag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped StringFlag.ApplyWithError | ||
| func (f *StringFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.StringFlag.ApplyWithError(set) | ||
| } | ||
| // StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow | ||
| // for other values to be specified | ||
| type StringSliceFlag struct { | ||
| cli.StringSliceFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewStringSliceFlag creates a new StringSliceFlag | ||
| func NewStringSliceFlag(fl cli.StringSliceFlag) *StringSliceFlag { | ||
| return &StringSliceFlag{StringSliceFlag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped StringSliceFlag.Apply | ||
| func (f *StringSliceFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.StringSliceFlag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped StringSliceFlag.ApplyWithError | ||
| func (f *StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.StringSliceFlag.ApplyWithError(set) | ||
| } | ||
| // Uint64Flag is the flag type that wraps cli.Uint64Flag to allow | ||
| // for other values to be specified | ||
| type Uint64Flag struct { | ||
| cli.Uint64Flag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewUint64Flag creates a new Uint64Flag | ||
| func NewUint64Flag(fl cli.Uint64Flag) *Uint64Flag { | ||
| return &Uint64Flag{Uint64Flag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped Uint64Flag.Apply | ||
| func (f *Uint64Flag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.Uint64Flag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped Uint64Flag.ApplyWithError | ||
| func (f *Uint64Flag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.Uint64Flag.ApplyWithError(set) | ||
| } | ||
| // UintFlag is the flag type that wraps cli.UintFlag to allow | ||
| // for other values to be specified | ||
| type UintFlag struct { | ||
| cli.UintFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewUintFlag creates a new UintFlag | ||
| func NewUintFlag(fl cli.UintFlag) *UintFlag { | ||
| return &UintFlag{UintFlag: fl, set: nil} | ||
| } | ||
| // Apply saves the flagSet for later usage calls, then calls the | ||
| // wrapped UintFlag.Apply | ||
| func (f *UintFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.UintFlag.Apply(set) | ||
| } | ||
| // ApplyWithError saves the flagSet for later usage calls, then calls the | ||
| // wrapped UintFlag.ApplyWithError | ||
| func (f *UintFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| f.set = set | ||
| return f.UintFlag.ApplyWithError(set) | ||
| } |
| // Disabling building of toml support in cases where golang is 1.0 or 1.1 | ||
| // as the encoding library is not implemented or supported. | ||
| // +build go1.2 | ||
| package altsrc | ||
| import ( | ||
| "flag" | ||
| "io/ioutil" | ||
| "os" | ||
| "testing" | ||
| "gopkg.in/urfave/cli.v1" | ||
| ) | ||
| func TestCommandTomFileTest(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) | ||
| defer os.Remove("current.toml") | ||
| test := []string{"test-cmd", "--load", "current.toml"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("test") | ||
| expect(t, val, 15) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "test"}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } | ||
| func TestCommandTomlFileTestGlobalEnvVarWins(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) | ||
| defer os.Remove("current.toml") | ||
| os.Setenv("THE_TEST", "10") | ||
| defer os.Setenv("THE_TEST", "") | ||
| test := []string{"test-cmd", "--load", "current.toml"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("test") | ||
| expect(t, val, 10) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } | ||
| func TestCommandTomlFileTestGlobalEnvVarWinsNested(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) | ||
| defer os.Remove("current.toml") | ||
| os.Setenv("THE_TEST", "10") | ||
| defer os.Setenv("THE_TEST", "") | ||
| test := []string{"test-cmd", "--load", "current.toml"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("top.test") | ||
| expect(t, val, 10) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "top.test", EnvVar: "THE_TEST"}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } | ||
| func TestCommandTomlFileTestSpecifiedFlagWins(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) | ||
| defer os.Remove("current.toml") | ||
| test := []string{"test-cmd", "--load", "current.toml", "--test", "7"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("test") | ||
| expect(t, val, 7) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "test"}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } | ||
| func TestCommandTomlFileTestSpecifiedFlagWinsNested(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.toml", []byte(`[top] | ||
| test = 15`), 0666) | ||
| defer os.Remove("current.toml") | ||
| test := []string{"test-cmd", "--load", "current.toml", "--top.test", "7"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("top.test") | ||
| expect(t, val, 7) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "top.test"}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } | ||
| func TestCommandTomlFileTestDefaultValueFileWins(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) | ||
| defer os.Remove("current.toml") | ||
| test := []string{"test-cmd", "--load", "current.toml"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("test") | ||
| expect(t, val, 15) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "test", Value: 7}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } | ||
| func TestCommandTomlFileTestDefaultValueFileWinsNested(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) | ||
| defer os.Remove("current.toml") | ||
| test := []string{"test-cmd", "--load", "current.toml"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("top.test") | ||
| expect(t, val, 15) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } | ||
| func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWins(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) | ||
| defer os.Remove("current.toml") | ||
| os.Setenv("THE_TEST", "11") | ||
| defer os.Setenv("THE_TEST", "") | ||
| test := []string{"test-cmd", "--load", "current.toml"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("test") | ||
| expect(t, val, 11) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } | ||
| func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWinsNested(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) | ||
| defer os.Remove("current.toml") | ||
| os.Setenv("THE_TEST", "11") | ||
| defer os.Setenv("THE_TEST", "") | ||
| test := []string{"test-cmd", "--load", "current.toml"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("top.test") | ||
| expect(t, val, 11) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVar: "THE_TEST"}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } |
| // Disabling building of toml support in cases where golang is 1.0 or 1.1 | ||
| // as the encoding library is not implemented or supported. | ||
| // +build go1.2 | ||
| package altsrc | ||
| import ( | ||
| "fmt" | ||
| "reflect" | ||
| "github.com/BurntSushi/toml" | ||
| "gopkg.in/urfave/cli.v1" | ||
| ) | ||
| type tomlMap struct { | ||
| Map map[interface{}]interface{} | ||
| } | ||
| func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) { | ||
| ret = make(map[interface{}]interface{}) | ||
| m := i.(map[string]interface{}) | ||
| for key, val := range m { | ||
| v := reflect.ValueOf(val) | ||
| switch v.Kind() { | ||
| case reflect.Bool: | ||
| ret[key] = val.(bool) | ||
| case reflect.String: | ||
| ret[key] = val.(string) | ||
| case reflect.Int: | ||
| ret[key] = int(val.(int)) | ||
| case reflect.Int8: | ||
| ret[key] = int(val.(int8)) | ||
| case reflect.Int16: | ||
| ret[key] = int(val.(int16)) | ||
| case reflect.Int32: | ||
| ret[key] = int(val.(int32)) | ||
| case reflect.Int64: | ||
| ret[key] = int(val.(int64)) | ||
| case reflect.Uint: | ||
| ret[key] = int(val.(uint)) | ||
| case reflect.Uint8: | ||
| ret[key] = int(val.(uint8)) | ||
| case reflect.Uint16: | ||
| ret[key] = int(val.(uint16)) | ||
| case reflect.Uint32: | ||
| ret[key] = int(val.(uint32)) | ||
| case reflect.Uint64: | ||
| ret[key] = int(val.(uint64)) | ||
| case reflect.Float32: | ||
| ret[key] = float64(val.(float32)) | ||
| case reflect.Float64: | ||
| ret[key] = float64(val.(float64)) | ||
| case reflect.Map: | ||
| if tmp, err := unmarshalMap(val); err == nil { | ||
| ret[key] = tmp | ||
| } else { | ||
| return nil, err | ||
| } | ||
| case reflect.Array, reflect.Slice: | ||
| ret[key] = val.([]interface{}) | ||
| default: | ||
| return nil, fmt.Errorf("Unsupported: type = %#v", v.Kind()) | ||
| } | ||
| } | ||
| return ret, nil | ||
| } | ||
| func (self *tomlMap) UnmarshalTOML(i interface{}) error { | ||
| if tmp, err := unmarshalMap(i); err == nil { | ||
| self.Map = tmp | ||
| } else { | ||
| return err | ||
| } | ||
| return nil | ||
| } | ||
| type tomlSourceContext struct { | ||
| FilePath string | ||
| } | ||
| // NewTomlSourceFromFile creates a new TOML InputSourceContext from a filepath. | ||
| func NewTomlSourceFromFile(file string) (InputSourceContext, error) { | ||
| tsc := &tomlSourceContext{FilePath: file} | ||
| var results tomlMap = tomlMap{} | ||
| if err := readCommandToml(tsc.FilePath, &results); err != nil { | ||
| return nil, fmt.Errorf("Unable to load TOML file '%s': inner error: \n'%v'", tsc.FilePath, err.Error()) | ||
| } | ||
| return &MapInputSource{valueMap: results.Map}, nil | ||
| } | ||
| // NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a provided flag name and source context. | ||
| func NewTomlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) { | ||
| return func(context *cli.Context) (InputSourceContext, error) { | ||
| filePath := context.String(flagFileName) | ||
| return NewTomlSourceFromFile(filePath) | ||
| } | ||
| } | ||
| func readCommandToml(filePath string, container interface{}) (err error) { | ||
| b, err := loadDataFrom(filePath) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| err = toml.Unmarshal(b, container) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| err = nil | ||
| return | ||
| } |
+435
| # Change Log | ||
| **ATTN**: This project uses [semantic versioning](http://semver.org/). | ||
| ## [Unreleased] | ||
| ## 1.20.0 - 2017-08-10 | ||
| ### Fixed | ||
| * `HandleExitCoder` is now correctly iterates over all errors in | ||
| a `MultiError`. The exit code is the exit code of the last error or `1` if | ||
| there are no `ExitCoder`s in the `MultiError`. | ||
| * Fixed YAML file loading on Windows (previously would fail validate the file path) | ||
| * Subcommand `Usage`, `Description`, `ArgsUsage`, `OnUsageError` correctly | ||
| propogated | ||
| * `ErrWriter` is now passed downwards through command structure to avoid the | ||
| need to redefine it | ||
| * Pass `Command` context into `OnUsageError` rather than parent context so that | ||
| all fields are avaiable | ||
| * Errors occuring in `Before` funcs are no longer double printed | ||
| * Use `UsageText` in the help templates for commands and subcommands if | ||
| defined; otherwise build the usage as before (was previously ignoring this | ||
| field) | ||
| * `IsSet` and `GlobalIsSet` now correctly return whether a flag is set if | ||
| a program calls `Set` or `GlobalSet` directly after flag parsing (would | ||
| previously only return `true` if the flag was set during parsing) | ||
| ### Changed | ||
| * No longer exit the program on command/subcommand error if the error raised is | ||
| not an `OsExiter`. This exiting behavior was introduced in 1.19.0, but was | ||
| determined to be a regression in functionality. See [the | ||
| PR](https://github.com/urfave/cli/pull/595) for discussion. | ||
| ### Added | ||
| * `CommandsByName` type was added to make it easy to sort `Command`s by name, | ||
| alphabetically | ||
| * `altsrc` now handles loading of string and int arrays from TOML | ||
| * Support for definition of custom help templates for `App` via | ||
| `CustomAppHelpTemplate` | ||
| * Support for arbitrary key/value fields on `App` to be used with | ||
| `CustomAppHelpTemplate` via `ExtraInfo` | ||
| * `HelpFlag`, `VersionFlag`, and `BashCompletionFlag` changed to explictly be | ||
| `cli.Flag`s allowing for the use of custom flags satisfying the `cli.Flag` | ||
| interface to be used. | ||
| ## [1.19.1] - 2016-11-21 | ||
| ### Fixed | ||
| - Fixes regression introduced in 1.19.0 where using an `ActionFunc` as | ||
| the `Action` for a command would cause it to error rather than calling the | ||
| function. Should not have a affected declarative cases using `func(c | ||
| *cli.Context) err)`. | ||
| - Shell completion now handles the case where the user specifies | ||
| `--generate-bash-completion` immediately after a flag that takes an argument. | ||
| Previously it call the application with `--generate-bash-completion` as the | ||
| flag value. | ||
| ## [1.19.0] - 2016-11-19 | ||
| ### Added | ||
| - `FlagsByName` was added to make it easy to sort flags (e.g. `sort.Sort(cli.FlagsByName(app.Flags))`) | ||
| - A `Description` field was added to `App` for a more detailed description of | ||
| the application (similar to the existing `Description` field on `Command`) | ||
| - Flag type code generation via `go generate` | ||
| - Write to stderr and exit 1 if action returns non-nil error | ||
| - Added support for TOML to the `altsrc` loader | ||
| - `SkipArgReorder` was added to allow users to skip the argument reordering. | ||
| This is useful if you want to consider all "flags" after an argument as | ||
| arguments rather than flags (the default behavior of the stdlib `flag` | ||
| library). This is backported functionality from the [removal of the flag | ||
| reordering](https://github.com/urfave/cli/pull/398) in the unreleased version | ||
| 2 | ||
| - For formatted errors (those implementing `ErrorFormatter`), the errors will | ||
| be formatted during output. Compatible with `pkg/errors`. | ||
| ### Changed | ||
| - Raise minimum tested/supported Go version to 1.2+ | ||
| ### Fixed | ||
| - Consider empty environment variables as set (previously environment variables | ||
| with the equivalent of `""` would be skipped rather than their value used). | ||
| - Return an error if the value in a given environment variable cannot be parsed | ||
| as the flag type. Previously these errors were silently swallowed. | ||
| - Print full error when an invalid flag is specified (which includes the invalid flag) | ||
| - `App.Writer` defaults to `stdout` when `nil` | ||
| - If no action is specified on a command or app, the help is now printed instead of `panic`ing | ||
| - `App.Metadata` is initialized automatically now (previously was `nil` unless initialized) | ||
| - Correctly show help message if `-h` is provided to a subcommand | ||
| - `context.(Global)IsSet` now respects environment variables. Previously it | ||
| would return `false` if a flag was specified in the environment rather than | ||
| as an argument | ||
| - Removed deprecation warnings to STDERR to avoid them leaking to the end-user | ||
| - `altsrc`s import paths were updated to use `gopkg.in/urfave/cli.v1`. This | ||
| fixes issues that occurred when `gopkg.in/urfave/cli.v1` was imported as well | ||
| as `altsrc` where Go would complain that the types didn't match | ||
| ## [1.18.1] - 2016-08-28 | ||
| ### Fixed | ||
| - Removed deprecation warnings to STDERR to avoid them leaking to the end-user (backported) | ||
| ## [1.18.0] - 2016-06-27 | ||
| ### Added | ||
| - `./runtests` test runner with coverage tracking by default | ||
| - testing on OS X | ||
| - testing on Windows | ||
| - `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code | ||
| ### Changed | ||
| - Use spaces for alignment in help/usage output instead of tabs, making the | ||
| output alignment consistent regardless of tab width | ||
| ### Fixed | ||
| - Printing of command aliases in help text | ||
| - Printing of visible flags for both struct and struct pointer flags | ||
| - Display the `help` subcommand when using `CommandCategories` | ||
| - No longer swallows `panic`s that occur within the `Action`s themselves when | ||
| detecting the signature of the `Action` field | ||
| ## [1.17.1] - 2016-08-28 | ||
| ### Fixed | ||
| - Removed deprecation warnings to STDERR to avoid them leaking to the end-user | ||
| ## [1.17.0] - 2016-05-09 | ||
| ### Added | ||
| - Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc` | ||
| - `context.GlobalBoolT` was added as an analogue to `context.GlobalBool` | ||
| - Support for hiding commands by setting `Hidden: true` -- this will hide the | ||
| commands in help output | ||
| ### Changed | ||
| - `Float64Flag`, `IntFlag`, and `DurationFlag` default values are no longer | ||
| quoted in help text output. | ||
| - All flag types now include `(default: {value})` strings following usage when a | ||
| default value can be (reasonably) detected. | ||
| - `IntSliceFlag` and `StringSliceFlag` usage strings are now more consistent | ||
| with non-slice flag types | ||
| - Apps now exit with a code of 3 if an unknown subcommand is specified | ||
| (previously they printed "No help topic for...", but still exited 0. This | ||
| makes it easier to script around apps built using `cli` since they can trust | ||
| that a 0 exit code indicated a successful execution. | ||
| - cleanups based on [Go Report Card | ||
| feedback](https://goreportcard.com/report/github.com/urfave/cli) | ||
| ## [1.16.1] - 2016-08-28 | ||
| ### Fixed | ||
| - Removed deprecation warnings to STDERR to avoid them leaking to the end-user | ||
| ## [1.16.0] - 2016-05-02 | ||
| ### Added | ||
| - `Hidden` field on all flag struct types to omit from generated help text | ||
| ### Changed | ||
| - `BashCompletionFlag` (`--enable-bash-completion`) is now omitted from | ||
| generated help text via the `Hidden` field | ||
| ### Fixed | ||
| - handling of error values in `HandleAction` and `HandleExitCoder` | ||
| ## [1.15.0] - 2016-04-30 | ||
| ### Added | ||
| - This file! | ||
| - Support for placeholders in flag usage strings | ||
| - `App.Metadata` map for arbitrary data/state management | ||
| - `Set` and `GlobalSet` methods on `*cli.Context` for altering values after | ||
| parsing. | ||
| - Support for nested lookup of dot-delimited keys in structures loaded from | ||
| YAML. | ||
| ### Changed | ||
| - The `App.Action` and `Command.Action` now prefer a return signature of | ||
| `func(*cli.Context) error`, as defined by `cli.ActionFunc`. If a non-nil | ||
| `error` is returned, there may be two outcomes: | ||
| - If the error fulfills `cli.ExitCoder`, then `os.Exit` will be called | ||
| automatically | ||
| - Else the error is bubbled up and returned from `App.Run` | ||
| - Specifying an `Action` with the legacy return signature of | ||
| `func(*cli.Context)` will produce a deprecation message to stderr | ||
| - Specifying an `Action` that is not a `func` type will produce a non-zero exit | ||
| from `App.Run` | ||
| - Specifying an `Action` func that has an invalid (input) signature will | ||
| produce a non-zero exit from `App.Run` | ||
| ### Deprecated | ||
| - <a name="deprecated-cli-app-runandexitonerror"></a> | ||
| `cli.App.RunAndExitOnError`, which should now be done by returning an error | ||
| that fulfills `cli.ExitCoder` to `cli.App.Run`. | ||
| - <a name="deprecated-cli-app-action-signature"></a> the legacy signature for | ||
| `cli.App.Action` of `func(*cli.Context)`, which should now have a return | ||
| signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. | ||
| ### Fixed | ||
| - Added missing `*cli.Context.GlobalFloat64` method | ||
| ## [1.14.0] - 2016-04-03 (backfilled 2016-04-25) | ||
| ### Added | ||
| - Codebeat badge | ||
| - Support for categorization via `CategorizedHelp` and `Categories` on app. | ||
| ### Changed | ||
| - Use `filepath.Base` instead of `path.Base` in `Name` and `HelpName`. | ||
| ### Fixed | ||
| - Ensure version is not shown in help text when `HideVersion` set. | ||
| ## [1.13.0] - 2016-03-06 (backfilled 2016-04-25) | ||
| ### Added | ||
| - YAML file input support. | ||
| - `NArg` method on context. | ||
| ## [1.12.0] - 2016-02-17 (backfilled 2016-04-25) | ||
| ### Added | ||
| - Custom usage error handling. | ||
| - Custom text support in `USAGE` section of help output. | ||
| - Improved help messages for empty strings. | ||
| - AppVeyor CI configuration. | ||
| ### Changed | ||
| - Removed `panic` from default help printer func. | ||
| - De-duping and optimizations. | ||
| ### Fixed | ||
| - Correctly handle `Before`/`After` at command level when no subcommands. | ||
| - Case of literal `-` argument causing flag reordering. | ||
| - Environment variable hints on Windows. | ||
| - Docs updates. | ||
| ## [1.11.1] - 2015-12-21 (backfilled 2016-04-25) | ||
| ### Changed | ||
| - Use `path.Base` in `Name` and `HelpName` | ||
| - Export `GetName` on flag types. | ||
| ### Fixed | ||
| - Flag parsing when skipping is enabled. | ||
| - Test output cleanup. | ||
| - Move completion check to account for empty input case. | ||
| ## [1.11.0] - 2015-11-15 (backfilled 2016-04-25) | ||
| ### Added | ||
| - Destination scan support for flags. | ||
| - Testing against `tip` in Travis CI config. | ||
| ### Changed | ||
| - Go version in Travis CI config. | ||
| ### Fixed | ||
| - Removed redundant tests. | ||
| - Use correct example naming in tests. | ||
| ## [1.10.2] - 2015-10-29 (backfilled 2016-04-25) | ||
| ### Fixed | ||
| - Remove unused var in bash completion. | ||
| ## [1.10.1] - 2015-10-21 (backfilled 2016-04-25) | ||
| ### Added | ||
| - Coverage and reference logos in README. | ||
| ### Fixed | ||
| - Use specified values in help and version parsing. | ||
| - Only display app version and help message once. | ||
| ## [1.10.0] - 2015-10-06 (backfilled 2016-04-25) | ||
| ### Added | ||
| - More tests for existing functionality. | ||
| - `ArgsUsage` at app and command level for help text flexibility. | ||
| ### Fixed | ||
| - Honor `HideHelp` and `HideVersion` in `App.Run`. | ||
| - Remove juvenile word from README. | ||
| ## [1.9.0] - 2015-09-08 (backfilled 2016-04-25) | ||
| ### Added | ||
| - `FullName` on command with accompanying help output update. | ||
| - Set default `$PROG` in bash completion. | ||
| ### Changed | ||
| - Docs formatting. | ||
| ### Fixed | ||
| - Removed self-referential imports in tests. | ||
| ## [1.8.0] - 2015-06-30 (backfilled 2016-04-25) | ||
| ### Added | ||
| - Support for `Copyright` at app level. | ||
| - `Parent` func at context level to walk up context lineage. | ||
| ### Fixed | ||
| - Global flag processing at top level. | ||
| ## [1.7.1] - 2015-06-11 (backfilled 2016-04-25) | ||
| ### Added | ||
| - Aggregate errors from `Before`/`After` funcs. | ||
| - Doc comments on flag structs. | ||
| - Include non-global flags when checking version and help. | ||
| - Travis CI config updates. | ||
| ### Fixed | ||
| - Ensure slice type flags have non-nil values. | ||
| - Collect global flags from the full command hierarchy. | ||
| - Docs prose. | ||
| ## [1.7.0] - 2015-05-03 (backfilled 2016-04-25) | ||
| ### Changed | ||
| - `HelpPrinter` signature includes output writer. | ||
| ### Fixed | ||
| - Specify go 1.1+ in docs. | ||
| - Set `Writer` when running command as app. | ||
| ## [1.6.0] - 2015-03-23 (backfilled 2016-04-25) | ||
| ### Added | ||
| - Multiple author support. | ||
| - `NumFlags` at context level. | ||
| - `Aliases` at command level. | ||
| ### Deprecated | ||
| - `ShortName` at command level. | ||
| ### Fixed | ||
| - Subcommand help output. | ||
| - Backward compatible support for deprecated `Author` and `Email` fields. | ||
| - Docs regarding `Names`/`Aliases`. | ||
| ## [1.5.0] - 2015-02-20 (backfilled 2016-04-25) | ||
| ### Added | ||
| - `After` hook func support at app and command level. | ||
| ### Fixed | ||
| - Use parsed context when running command as subcommand. | ||
| - Docs prose. | ||
| ## [1.4.1] - 2015-01-09 (backfilled 2016-04-25) | ||
| ### Added | ||
| - Support for hiding `-h / --help` flags, but not `help` subcommand. | ||
| - Stop flag parsing after `--`. | ||
| ### Fixed | ||
| - Help text for generic flags to specify single value. | ||
| - Use double quotes in output for defaults. | ||
| - Use `ParseInt` instead of `ParseUint` for int environment var values. | ||
| - Use `0` as base when parsing int environment var values. | ||
| ## [1.4.0] - 2014-12-12 (backfilled 2016-04-25) | ||
| ### Added | ||
| - Support for environment variable lookup "cascade". | ||
| - Support for `Stdout` on app for output redirection. | ||
| ### Fixed | ||
| - Print command help instead of app help in `ShowCommandHelp`. | ||
| ## [1.3.1] - 2014-11-13 (backfilled 2016-04-25) | ||
| ### Added | ||
| - Docs and example code updates. | ||
| ### Changed | ||
| - Default `-v / --version` flag made optional. | ||
| ## [1.3.0] - 2014-08-10 (backfilled 2016-04-25) | ||
| ### Added | ||
| - `FlagNames` at context level. | ||
| - Exposed `VersionPrinter` var for more control over version output. | ||
| - Zsh completion hook. | ||
| - `AUTHOR` section in default app help template. | ||
| - Contribution guidelines. | ||
| - `DurationFlag` type. | ||
| ## [1.2.0] - 2014-08-02 | ||
| ### Added | ||
| - Support for environment variable defaults on flags plus tests. | ||
| ## [1.1.0] - 2014-07-15 | ||
| ### Added | ||
| - Bash completion. | ||
| - Optional hiding of built-in help command. | ||
| - Optional skipping of flag parsing at command level. | ||
| - `Author`, `Email`, and `Compiled` metadata on app. | ||
| - `Before` hook func support at app and command level. | ||
| - `CommandNotFound` func support at app level. | ||
| - Command reference available on context. | ||
| - `GenericFlag` type. | ||
| - `Float64Flag` type. | ||
| - `BoolTFlag` type. | ||
| - `IsSet` flag helper on context. | ||
| - More flag lookup funcs at context level. | ||
| - More tests & docs. | ||
| ### Changed | ||
| - Help template updates to account for presence/absence of flags. | ||
| - Separated subcommand help template. | ||
| - Exposed `HelpPrinter` var for more control over help output. | ||
| ## [1.0.0] - 2013-11-01 | ||
| ### Added | ||
| - `help` flag in default app flag set and each command flag set. | ||
| - Custom handling of argument parsing errors. | ||
| - Command lookup by name at app level. | ||
| - `StringSliceFlag` type and supporting `StringSlice` type. | ||
| - `IntSliceFlag` type and supporting `IntSlice` type. | ||
| - Slice type flag lookups by name at context level. | ||
| - Export of app and command help functions. | ||
| - More tests & docs. | ||
| ## 0.1.0 - 2013-07-22 | ||
| ### Added | ||
| - Initial implementation. | ||
| [Unreleased]: https://github.com/urfave/cli/compare/v1.18.0...HEAD | ||
| [1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0 | ||
| [1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0 | ||
| [1.16.0]: https://github.com/urfave/cli/compare/v1.15.0...v1.16.0 | ||
| [1.15.0]: https://github.com/urfave/cli/compare/v1.14.0...v1.15.0 | ||
| [1.14.0]: https://github.com/urfave/cli/compare/v1.13.0...v1.14.0 | ||
| [1.13.0]: https://github.com/urfave/cli/compare/v1.12.0...v1.13.0 | ||
| [1.12.0]: https://github.com/urfave/cli/compare/v1.11.1...v1.12.0 | ||
| [1.11.1]: https://github.com/urfave/cli/compare/v1.11.0...v1.11.1 | ||
| [1.11.0]: https://github.com/urfave/cli/compare/v1.10.2...v1.11.0 | ||
| [1.10.2]: https://github.com/urfave/cli/compare/v1.10.1...v1.10.2 | ||
| [1.10.1]: https://github.com/urfave/cli/compare/v1.10.0...v1.10.1 | ||
| [1.10.0]: https://github.com/urfave/cli/compare/v1.9.0...v1.10.0 | ||
| [1.9.0]: https://github.com/urfave/cli/compare/v1.8.0...v1.9.0 | ||
| [1.8.0]: https://github.com/urfave/cli/compare/v1.7.1...v1.8.0 | ||
| [1.7.1]: https://github.com/urfave/cli/compare/v1.7.0...v1.7.1 | ||
| [1.7.0]: https://github.com/urfave/cli/compare/v1.6.0...v1.7.0 | ||
| [1.6.0]: https://github.com/urfave/cli/compare/v1.5.0...v1.6.0 | ||
| [1.5.0]: https://github.com/urfave/cli/compare/v1.4.1...v1.5.0 | ||
| [1.4.1]: https://github.com/urfave/cli/compare/v1.4.0...v1.4.1 | ||
| [1.4.0]: https://github.com/urfave/cli/compare/v1.3.1...v1.4.0 | ||
| [1.3.1]: https://github.com/urfave/cli/compare/v1.3.0...v1.3.1 | ||
| [1.3.0]: https://github.com/urfave/cli/compare/v1.2.0...v1.3.0 | ||
| [1.2.0]: https://github.com/urfave/cli/compare/v1.1.0...v1.2.0 | ||
| [1.1.0]: https://github.com/urfave/cli/compare/v1.0.0...v1.1.0 | ||
| [1.0.0]: https://github.com/urfave/cli/compare/v0.1.0...v1.0.0 |
+122
| package cli | ||
| import ( | ||
| "bytes" | ||
| "errors" | ||
| "fmt" | ||
| "testing" | ||
| ) | ||
| func TestHandleExitCoder_nil(t *testing.T) { | ||
| exitCode := 0 | ||
| called := false | ||
| OsExiter = func(rc int) { | ||
| if !called { | ||
| exitCode = rc | ||
| called = true | ||
| } | ||
| } | ||
| defer func() { OsExiter = fakeOsExiter }() | ||
| HandleExitCoder(nil) | ||
| expect(t, exitCode, 0) | ||
| expect(t, called, false) | ||
| } | ||
| func TestHandleExitCoder_ExitCoder(t *testing.T) { | ||
| exitCode := 0 | ||
| called := false | ||
| OsExiter = func(rc int) { | ||
| if !called { | ||
| exitCode = rc | ||
| called = true | ||
| } | ||
| } | ||
| defer func() { OsExiter = fakeOsExiter }() | ||
| HandleExitCoder(NewExitError("galactic perimeter breach", 9)) | ||
| expect(t, exitCode, 9) | ||
| expect(t, called, true) | ||
| } | ||
| func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) { | ||
| exitCode := 0 | ||
| called := false | ||
| OsExiter = func(rc int) { | ||
| if !called { | ||
| exitCode = rc | ||
| called = true | ||
| } | ||
| } | ||
| defer func() { OsExiter = fakeOsExiter }() | ||
| exitErr := NewExitError("galactic perimeter breach", 9) | ||
| exitErr2 := NewExitError("last ExitCoder", 11) | ||
| err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr, exitErr2) | ||
| HandleExitCoder(err) | ||
| expect(t, exitCode, 11) | ||
| expect(t, called, true) | ||
| } | ||
| // make a stub to not import pkg/errors | ||
| type ErrorWithFormat struct { | ||
| error | ||
| } | ||
| func NewErrorWithFormat(m string) *ErrorWithFormat { | ||
| return &ErrorWithFormat{error: errors.New(m)} | ||
| } | ||
| func (f *ErrorWithFormat) Format(s fmt.State, verb rune) { | ||
| fmt.Fprintf(s, "This the format: %v", f.error) | ||
| } | ||
| func TestHandleExitCoder_ErrorWithFormat(t *testing.T) { | ||
| called := false | ||
| OsExiter = func(rc int) { | ||
| if !called { | ||
| called = true | ||
| } | ||
| } | ||
| ErrWriter = &bytes.Buffer{} | ||
| defer func() { | ||
| OsExiter = fakeOsExiter | ||
| ErrWriter = fakeErrWriter | ||
| }() | ||
| err := NewExitError(NewErrorWithFormat("I am formatted"), 1) | ||
| HandleExitCoder(err) | ||
| expect(t, called, true) | ||
| expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: I am formatted\n") | ||
| } | ||
| func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) { | ||
| called := false | ||
| OsExiter = func(rc int) { | ||
| if !called { | ||
| called = true | ||
| } | ||
| } | ||
| ErrWriter = &bytes.Buffer{} | ||
| defer func() { OsExiter = fakeOsExiter }() | ||
| err := NewMultiError(NewErrorWithFormat("err1"), NewErrorWithFormat("err2")) | ||
| HandleExitCoder(err) | ||
| expect(t, called, true) | ||
| expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: err1\nThis the format: err2\n") | ||
| } |
+115
| package cli | ||
| import ( | ||
| "fmt" | ||
| "io" | ||
| "os" | ||
| "strings" | ||
| ) | ||
| // OsExiter is the function used when the app exits. If not set defaults to os.Exit. | ||
| var OsExiter = os.Exit | ||
| // ErrWriter is used to write errors to the user. This can be anything | ||
| // implementing the io.Writer interface and defaults to os.Stderr. | ||
| var ErrWriter io.Writer = os.Stderr | ||
| // MultiError is an error that wraps multiple errors. | ||
| type MultiError struct { | ||
| Errors []error | ||
| } | ||
| // NewMultiError creates a new MultiError. Pass in one or more errors. | ||
| func NewMultiError(err ...error) MultiError { | ||
| return MultiError{Errors: err} | ||
| } | ||
| // Error implements the error interface. | ||
| func (m MultiError) Error() string { | ||
| errs := make([]string, len(m.Errors)) | ||
| for i, err := range m.Errors { | ||
| errs[i] = err.Error() | ||
| } | ||
| return strings.Join(errs, "\n") | ||
| } | ||
| type ErrorFormatter interface { | ||
| Format(s fmt.State, verb rune) | ||
| } | ||
| // ExitCoder is the interface checked by `App` and `Command` for a custom exit | ||
| // code | ||
| type ExitCoder interface { | ||
| error | ||
| ExitCode() int | ||
| } | ||
| // ExitError fulfills both the builtin `error` interface and `ExitCoder` | ||
| type ExitError struct { | ||
| exitCode int | ||
| message interface{} | ||
| } | ||
| // NewExitError makes a new *ExitError | ||
| func NewExitError(message interface{}, exitCode int) *ExitError { | ||
| return &ExitError{ | ||
| exitCode: exitCode, | ||
| message: message, | ||
| } | ||
| } | ||
| // Error returns the string message, fulfilling the interface required by | ||
| // `error` | ||
| func (ee *ExitError) Error() string { | ||
| return fmt.Sprintf("%v", ee.message) | ||
| } | ||
| // ExitCode returns the exit code, fulfilling the interface required by | ||
| // `ExitCoder` | ||
| func (ee *ExitError) ExitCode() int { | ||
| return ee.exitCode | ||
| } | ||
| // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if | ||
| // so prints the error to stderr (if it is non-empty) and calls OsExiter with the | ||
| // given exit code. If the given error is a MultiError, then this func is | ||
| // called on all members of the Errors slice and calls OsExiter with the last exit code. | ||
| func HandleExitCoder(err error) { | ||
| if err == nil { | ||
| return | ||
| } | ||
| if exitErr, ok := err.(ExitCoder); ok { | ||
| if err.Error() != "" { | ||
| if _, ok := exitErr.(ErrorFormatter); ok { | ||
| fmt.Fprintf(ErrWriter, "%+v\n", err) | ||
| } else { | ||
| fmt.Fprintln(ErrWriter, err) | ||
| } | ||
| } | ||
| OsExiter(exitErr.ExitCode()) | ||
| return | ||
| } | ||
| if multiErr, ok := err.(MultiError); ok { | ||
| code := handleMultiError(multiErr) | ||
| OsExiter(code) | ||
| return | ||
| } | ||
| } | ||
| func handleMultiError(multiErr MultiError) int { | ||
| code := 1 | ||
| for _, merr := range multiErr.Errors { | ||
| if multiErr2, ok := merr.(MultiError); ok { | ||
| code = handleMultiError(multiErr2) | ||
| } else { | ||
| fmt.Fprintln(ErrWriter, merr) | ||
| if exitErr, ok := merr.(ExitCoder); ok { | ||
| code = exitErr.ExitCode() | ||
| } | ||
| } | ||
| } | ||
| return code | ||
| } |
| package cli | ||
| import ( | ||
| "flag" | ||
| "strconv" | ||
| "time" | ||
| ) | ||
| // WARNING: This file is generated! | ||
| // BoolFlag is a flag with type bool | ||
| type BoolFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Destination *bool | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f BoolFlag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f BoolFlag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // Bool looks up the value of a local BoolFlag, returns | ||
| // false if not found | ||
| func (c *Context) Bool(name string) bool { | ||
| return lookupBool(name, c.flagSet) | ||
| } | ||
| // GlobalBool looks up the value of a global BoolFlag, returns | ||
| // false if not found | ||
| func (c *Context) GlobalBool(name string) bool { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupBool(name, fs) | ||
| } | ||
| return false | ||
| } | ||
| func lookupBool(name string, set *flag.FlagSet) bool { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := strconv.ParseBool(f.Value.String()) | ||
| if err != nil { | ||
| return false | ||
| } | ||
| return parsed | ||
| } | ||
| return false | ||
| } | ||
| // BoolTFlag is a flag with type bool that is true by default | ||
| type BoolTFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Destination *bool | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f BoolTFlag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f BoolTFlag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // BoolT looks up the value of a local BoolTFlag, returns | ||
| // false if not found | ||
| func (c *Context) BoolT(name string) bool { | ||
| return lookupBoolT(name, c.flagSet) | ||
| } | ||
| // GlobalBoolT looks up the value of a global BoolTFlag, returns | ||
| // false if not found | ||
| func (c *Context) GlobalBoolT(name string) bool { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupBoolT(name, fs) | ||
| } | ||
| return false | ||
| } | ||
| func lookupBoolT(name string, set *flag.FlagSet) bool { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := strconv.ParseBool(f.Value.String()) | ||
| if err != nil { | ||
| return false | ||
| } | ||
| return parsed | ||
| } | ||
| return false | ||
| } | ||
| // DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) | ||
| type DurationFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Value time.Duration | ||
| Destination *time.Duration | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f DurationFlag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f DurationFlag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // Duration looks up the value of a local DurationFlag, returns | ||
| // 0 if not found | ||
| func (c *Context) Duration(name string) time.Duration { | ||
| return lookupDuration(name, c.flagSet) | ||
| } | ||
| // GlobalDuration looks up the value of a global DurationFlag, returns | ||
| // 0 if not found | ||
| func (c *Context) GlobalDuration(name string) time.Duration { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupDuration(name, fs) | ||
| } | ||
| return 0 | ||
| } | ||
| func lookupDuration(name string, set *flag.FlagSet) time.Duration { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := time.ParseDuration(f.Value.String()) | ||
| if err != nil { | ||
| return 0 | ||
| } | ||
| return parsed | ||
| } | ||
| return 0 | ||
| } | ||
| // Float64Flag is a flag with type float64 | ||
| type Float64Flag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Value float64 | ||
| Destination *float64 | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f Float64Flag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f Float64Flag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // Float64 looks up the value of a local Float64Flag, returns | ||
| // 0 if not found | ||
| func (c *Context) Float64(name string) float64 { | ||
| return lookupFloat64(name, c.flagSet) | ||
| } | ||
| // GlobalFloat64 looks up the value of a global Float64Flag, returns | ||
| // 0 if not found | ||
| func (c *Context) GlobalFloat64(name string) float64 { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupFloat64(name, fs) | ||
| } | ||
| return 0 | ||
| } | ||
| func lookupFloat64(name string, set *flag.FlagSet) float64 { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := strconv.ParseFloat(f.Value.String(), 64) | ||
| if err != nil { | ||
| return 0 | ||
| } | ||
| return parsed | ||
| } | ||
| return 0 | ||
| } | ||
| // GenericFlag is a flag with type Generic | ||
| type GenericFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Value Generic | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f GenericFlag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f GenericFlag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // Generic looks up the value of a local GenericFlag, returns | ||
| // nil if not found | ||
| func (c *Context) Generic(name string) interface{} { | ||
| return lookupGeneric(name, c.flagSet) | ||
| } | ||
| // GlobalGeneric looks up the value of a global GenericFlag, returns | ||
| // nil if not found | ||
| func (c *Context) GlobalGeneric(name string) interface{} { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupGeneric(name, fs) | ||
| } | ||
| return nil | ||
| } | ||
| func lookupGeneric(name string, set *flag.FlagSet) interface{} { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := f.Value, error(nil) | ||
| if err != nil { | ||
| return nil | ||
| } | ||
| return parsed | ||
| } | ||
| return nil | ||
| } | ||
| // Int64Flag is a flag with type int64 | ||
| type Int64Flag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Value int64 | ||
| Destination *int64 | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f Int64Flag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f Int64Flag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // Int64 looks up the value of a local Int64Flag, returns | ||
| // 0 if not found | ||
| func (c *Context) Int64(name string) int64 { | ||
| return lookupInt64(name, c.flagSet) | ||
| } | ||
| // GlobalInt64 looks up the value of a global Int64Flag, returns | ||
| // 0 if not found | ||
| func (c *Context) GlobalInt64(name string) int64 { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupInt64(name, fs) | ||
| } | ||
| return 0 | ||
| } | ||
| func lookupInt64(name string, set *flag.FlagSet) int64 { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) | ||
| if err != nil { | ||
| return 0 | ||
| } | ||
| return parsed | ||
| } | ||
| return 0 | ||
| } | ||
| // IntFlag is a flag with type int | ||
| type IntFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Value int | ||
| Destination *int | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f IntFlag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f IntFlag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // Int looks up the value of a local IntFlag, returns | ||
| // 0 if not found | ||
| func (c *Context) Int(name string) int { | ||
| return lookupInt(name, c.flagSet) | ||
| } | ||
| // GlobalInt looks up the value of a global IntFlag, returns | ||
| // 0 if not found | ||
| func (c *Context) GlobalInt(name string) int { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupInt(name, fs) | ||
| } | ||
| return 0 | ||
| } | ||
| func lookupInt(name string, set *flag.FlagSet) int { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) | ||
| if err != nil { | ||
| return 0 | ||
| } | ||
| return int(parsed) | ||
| } | ||
| return 0 | ||
| } | ||
| // IntSliceFlag is a flag with type *IntSlice | ||
| type IntSliceFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Value *IntSlice | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f IntSliceFlag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f IntSliceFlag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // IntSlice looks up the value of a local IntSliceFlag, returns | ||
| // nil if not found | ||
| func (c *Context) IntSlice(name string) []int { | ||
| return lookupIntSlice(name, c.flagSet) | ||
| } | ||
| // GlobalIntSlice looks up the value of a global IntSliceFlag, returns | ||
| // nil if not found | ||
| func (c *Context) GlobalIntSlice(name string) []int { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupIntSlice(name, fs) | ||
| } | ||
| return nil | ||
| } | ||
| func lookupIntSlice(name string, set *flag.FlagSet) []int { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) | ||
| if err != nil { | ||
| return nil | ||
| } | ||
| return parsed | ||
| } | ||
| return nil | ||
| } | ||
| // Int64SliceFlag is a flag with type *Int64Slice | ||
| type Int64SliceFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Value *Int64Slice | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f Int64SliceFlag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f Int64SliceFlag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // Int64Slice looks up the value of a local Int64SliceFlag, returns | ||
| // nil if not found | ||
| func (c *Context) Int64Slice(name string) []int64 { | ||
| return lookupInt64Slice(name, c.flagSet) | ||
| } | ||
| // GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns | ||
| // nil if not found | ||
| func (c *Context) GlobalInt64Slice(name string) []int64 { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupInt64Slice(name, fs) | ||
| } | ||
| return nil | ||
| } | ||
| func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) | ||
| if err != nil { | ||
| return nil | ||
| } | ||
| return parsed | ||
| } | ||
| return nil | ||
| } | ||
| // StringFlag is a flag with type string | ||
| type StringFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Value string | ||
| Destination *string | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f StringFlag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f StringFlag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // String looks up the value of a local StringFlag, returns | ||
| // "" if not found | ||
| func (c *Context) String(name string) string { | ||
| return lookupString(name, c.flagSet) | ||
| } | ||
| // GlobalString looks up the value of a global StringFlag, returns | ||
| // "" if not found | ||
| func (c *Context) GlobalString(name string) string { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupString(name, fs) | ||
| } | ||
| return "" | ||
| } | ||
| func lookupString(name string, set *flag.FlagSet) string { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := f.Value.String(), error(nil) | ||
| if err != nil { | ||
| return "" | ||
| } | ||
| return parsed | ||
| } | ||
| return "" | ||
| } | ||
| // StringSliceFlag is a flag with type *StringSlice | ||
| type StringSliceFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Value *StringSlice | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f StringSliceFlag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f StringSliceFlag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // StringSlice looks up the value of a local StringSliceFlag, returns | ||
| // nil if not found | ||
| func (c *Context) StringSlice(name string) []string { | ||
| return lookupStringSlice(name, c.flagSet) | ||
| } | ||
| // GlobalStringSlice looks up the value of a global StringSliceFlag, returns | ||
| // nil if not found | ||
| func (c *Context) GlobalStringSlice(name string) []string { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupStringSlice(name, fs) | ||
| } | ||
| return nil | ||
| } | ||
| func lookupStringSlice(name string, set *flag.FlagSet) []string { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) | ||
| if err != nil { | ||
| return nil | ||
| } | ||
| return parsed | ||
| } | ||
| return nil | ||
| } | ||
| // Uint64Flag is a flag with type uint64 | ||
| type Uint64Flag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Value uint64 | ||
| Destination *uint64 | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f Uint64Flag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f Uint64Flag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // Uint64 looks up the value of a local Uint64Flag, returns | ||
| // 0 if not found | ||
| func (c *Context) Uint64(name string) uint64 { | ||
| return lookupUint64(name, c.flagSet) | ||
| } | ||
| // GlobalUint64 looks up the value of a global Uint64Flag, returns | ||
| // 0 if not found | ||
| func (c *Context) GlobalUint64(name string) uint64 { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupUint64(name, fs) | ||
| } | ||
| return 0 | ||
| } | ||
| func lookupUint64(name string, set *flag.FlagSet) uint64 { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) | ||
| if err != nil { | ||
| return 0 | ||
| } | ||
| return parsed | ||
| } | ||
| return 0 | ||
| } | ||
| // UintFlag is a flag with type uint | ||
| type UintFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Hidden bool | ||
| Value uint | ||
| Destination *uint | ||
| } | ||
| // String returns a readable representation of this value | ||
| // (for usage defaults) | ||
| func (f UintFlag) String() string { | ||
| return FlagStringer(f) | ||
| } | ||
| // GetName returns the name of the flag | ||
| func (f UintFlag) GetName() string { | ||
| return f.Name | ||
| } | ||
| // Uint looks up the value of a local UintFlag, returns | ||
| // 0 if not found | ||
| func (c *Context) Uint(name string) uint { | ||
| return lookupUint(name, c.flagSet) | ||
| } | ||
| // GlobalUint looks up the value of a global UintFlag, returns | ||
| // 0 if not found | ||
| func (c *Context) GlobalUint(name string) uint { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupUint(name, fs) | ||
| } | ||
| return 0 | ||
| } | ||
| func lookupUint(name string, set *flag.FlagSet) uint { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) | ||
| if err != nil { | ||
| return 0 | ||
| } | ||
| return uint(parsed) | ||
| } | ||
| return 0 | ||
| } |
| [ | ||
| { | ||
| "name": "Bool", | ||
| "type": "bool", | ||
| "value": false, | ||
| "context_default": "false", | ||
| "parser": "strconv.ParseBool(f.Value.String())" | ||
| }, | ||
| { | ||
| "name": "BoolT", | ||
| "type": "bool", | ||
| "value": false, | ||
| "doctail": " that is true by default", | ||
| "context_default": "false", | ||
| "parser": "strconv.ParseBool(f.Value.String())" | ||
| }, | ||
| { | ||
| "name": "Duration", | ||
| "type": "time.Duration", | ||
| "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", | ||
| "context_default": "0", | ||
| "parser": "time.ParseDuration(f.Value.String())" | ||
| }, | ||
| { | ||
| "name": "Float64", | ||
| "type": "float64", | ||
| "context_default": "0", | ||
| "parser": "strconv.ParseFloat(f.Value.String(), 64)" | ||
| }, | ||
| { | ||
| "name": "Generic", | ||
| "type": "Generic", | ||
| "dest": false, | ||
| "context_default": "nil", | ||
| "context_type": "interface{}" | ||
| }, | ||
| { | ||
| "name": "Int64", | ||
| "type": "int64", | ||
| "context_default": "0", | ||
| "parser": "strconv.ParseInt(f.Value.String(), 0, 64)" | ||
| }, | ||
| { | ||
| "name": "Int", | ||
| "type": "int", | ||
| "context_default": "0", | ||
| "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", | ||
| "parser_cast": "int(parsed)" | ||
| }, | ||
| { | ||
| "name": "IntSlice", | ||
| "type": "*IntSlice", | ||
| "dest": false, | ||
| "context_default": "nil", | ||
| "context_type": "[]int", | ||
| "parser": "(f.Value.(*IntSlice)).Value(), error(nil)" | ||
| }, | ||
| { | ||
| "name": "Int64Slice", | ||
| "type": "*Int64Slice", | ||
| "dest": false, | ||
| "context_default": "nil", | ||
| "context_type": "[]int64", | ||
| "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)" | ||
| }, | ||
| { | ||
| "name": "String", | ||
| "type": "string", | ||
| "context_default": "\"\"", | ||
| "parser": "f.Value.String(), error(nil)" | ||
| }, | ||
| { | ||
| "name": "StringSlice", | ||
| "type": "*StringSlice", | ||
| "dest": false, | ||
| "context_default": "nil", | ||
| "context_type": "[]string", | ||
| "parser": "(f.Value.(*StringSlice)).Value(), error(nil)" | ||
| }, | ||
| { | ||
| "name": "Uint64", | ||
| "type": "uint64", | ||
| "context_default": "0", | ||
| "parser": "strconv.ParseUint(f.Value.String(), 0, 64)" | ||
| }, | ||
| { | ||
| "name": "Uint", | ||
| "type": "uint", | ||
| "context_default": "0", | ||
| "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", | ||
| "parser_cast": "uint(parsed)" | ||
| } | ||
| ] |
+28
| package cli | ||
| // BashCompleteFunc is an action to execute when the bash-completion flag is set | ||
| type BashCompleteFunc func(*Context) | ||
| // BeforeFunc is an action to execute before any subcommands are run, but after | ||
| // the context is ready if a non-nil error is returned, no subcommands are run | ||
| type BeforeFunc func(*Context) error | ||
| // AfterFunc is an action to execute after any subcommands are run, but after the | ||
| // subcommand has finished it is run even if Action() panics | ||
| type AfterFunc func(*Context) error | ||
| // ActionFunc is the action to execute when no subcommands are specified | ||
| type ActionFunc func(*Context) error | ||
| // CommandNotFoundFunc is executed if the proper command cannot be found | ||
| type CommandNotFoundFunc func(*Context, string) | ||
| // OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying | ||
| // customized usage error messages. This function is able to replace the | ||
| // original error messages. If this function is not set, the "Incorrect usage" | ||
| // is displayed and the execution is interrupted. | ||
| type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error | ||
| // FlagStringFunc is used by the help generation to display a flag, which is | ||
| // expected to be a single line. | ||
| type FlagStringFunc func(Flag) string |
Sorry, the diff of this file is not supported yet
| // +build darwin dragonfly freebsd linux netbsd openbsd solaris | ||
| package cli | ||
| import "os" | ||
| func clearenv() { | ||
| os.Clearenv() | ||
| } |
| package cli | ||
| import ( | ||
| "os" | ||
| "syscall" | ||
| ) | ||
| // os.Clearenv() doesn't actually unset variables on Windows | ||
| // See: https://github.com/golang/go/issues/17902 | ||
| func clearenv() { | ||
| for _, s := range os.Environ() { | ||
| for j := 1; j < len(s); j++ { | ||
| if s[j] == '=' { | ||
| keyp, _ := syscall.UTF16PtrFromString(s[0:j]) | ||
| syscall.SetEnvironmentVariable(keyp, nil) | ||
| break | ||
| } | ||
| } | ||
| } | ||
| } |
Sorry, the diff of this file is not supported yet
@@ -11,3 +11,3 @@ package altsrc | ||
| "github.com/micro/cli" | ||
| "gopkg.in/urfave/cli.v1" | ||
| ) | ||
@@ -63,3 +63,3 @@ | ||
| FlagName: "test", | ||
| MapValue: []string{"hello", "world"}, | ||
| MapValue: []interface{}{"hello", "world"}, | ||
| }) | ||
@@ -73,3 +73,3 @@ expect(t, c.StringSlice("test"), []string{"hello", "world"}) | ||
| FlagName: "test", | ||
| MapValue: []string{"hello", "world"}, | ||
| MapValue: []interface{}{"hello", "world"}, | ||
| ContextValueString: "ohno", | ||
@@ -84,3 +84,3 @@ }) | ||
| FlagName: "test", | ||
| MapValue: []string{"hello", "world"}, | ||
| MapValue: []interface{}{"hello", "world"}, | ||
| EnvVarName: "TEST", | ||
@@ -96,3 +96,3 @@ EnvVarValue: "oh,no", | ||
| FlagName: "test", | ||
| MapValue: []int{1, 2}, | ||
| MapValue: []interface{}{1, 2}, | ||
| }) | ||
@@ -106,3 +106,3 @@ expect(t, c.IntSlice("test"), []int{1, 2}) | ||
| FlagName: "test", | ||
| MapValue: []int{1, 2}, | ||
| MapValue: []interface{}{1, 2}, | ||
| ContextValueString: "3", | ||
@@ -117,3 +117,3 @@ }) | ||
| FlagName: "test", | ||
| MapValue: []int{1, 2}, | ||
| MapValue: []interface{}{1, 2}, | ||
| EnvVarName: "TEST", | ||
@@ -306,3 +306,3 @@ EnvVarValue: "3,4", | ||
| func runTest(t *testing.T, test testApplyInputSource) *cli.Context { | ||
| inputSource := &MapInputSource{valueMap: map[string]interface{}{test.FlagName: test.MapValue}} | ||
| inputSource := &MapInputSource{valueMap: map[interface{}]interface{}{test.FlagName: test.MapValue}} | ||
| set := flag.NewFlagSet(test.FlagSetName, flag.ContinueOnError) | ||
@@ -309,0 +309,0 @@ c := cli.NewContext(nil, set, nil) |
+6
-184
| package altsrc | ||
| import ( | ||
| "flag" | ||
| "fmt" | ||
| "os" | ||
| "strconv" | ||
| "strings" | ||
| "syscall" | ||
| "github.com/micro/cli" | ||
| "gopkg.in/urfave/cli.v1" | ||
| ) | ||
@@ -41,3 +40,3 @@ | ||
| // that are supported by the input source | ||
| func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) func(context *cli.Context) error { | ||
| func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc { | ||
| return func(context *cli.Context) error { | ||
@@ -56,3 +55,3 @@ inputSource, err := createInputSource() | ||
| // no error it will then apply the new input source to any flags that are supported by the input source | ||
| func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) func(context *cli.Context) error { | ||
| func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) cli.BeforeFunc { | ||
| return func(context *cli.Context) error { | ||
@@ -68,14 +67,2 @@ inputSource, err := createInputSource(context) | ||
| // GenericFlag is the flag type that wraps cli.GenericFlag to allow | ||
| // for other values to be specified | ||
| type GenericFlag struct { | ||
| cli.GenericFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewGenericFlag creates a new GenericFlag | ||
| func NewGenericFlag(flag cli.GenericFlag) *GenericFlag { | ||
| return &GenericFlag{GenericFlag: flag, set: nil} | ||
| } | ||
| // ApplyInputSourceValue applies a generic value to the flagSet if required | ||
@@ -100,21 +87,2 @@ func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { | ||
| // Apply saves the flagSet for later usage then calls | ||
| // the wrapped GenericFlag.Apply | ||
| func (f *GenericFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.GenericFlag.Apply(set) | ||
| } | ||
| // StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow | ||
| // for other values to be specified | ||
| type StringSliceFlag struct { | ||
| cli.StringSliceFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewStringSliceFlag creates a new StringSliceFlag | ||
| func NewStringSliceFlag(flag cli.StringSliceFlag) *StringSliceFlag { | ||
| return &StringSliceFlag{StringSliceFlag: flag, set: nil} | ||
| } | ||
| // ApplyInputSourceValue applies a StringSlice value to the flagSet if required | ||
@@ -142,21 +110,2 @@ func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { | ||
| // Apply saves the flagSet for later usage then calls | ||
| // the wrapped StringSliceFlag.Apply | ||
| func (f *StringSliceFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.StringSliceFlag.Apply(set) | ||
| } | ||
| // IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow | ||
| // for other values to be specified | ||
| type IntSliceFlag struct { | ||
| cli.IntSliceFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewIntSliceFlag creates a new IntSliceFlag | ||
| func NewIntSliceFlag(flag cli.IntSliceFlag) *IntSliceFlag { | ||
| return &IntSliceFlag{IntSliceFlag: flag, set: nil} | ||
| } | ||
| // ApplyInputSourceValue applies a IntSlice value if required | ||
@@ -184,21 +133,2 @@ func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { | ||
| // Apply saves the flagSet for later usage then calls | ||
| // the wrapped IntSliceFlag.Apply | ||
| func (f *IntSliceFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.IntSliceFlag.Apply(set) | ||
| } | ||
| // BoolFlag is the flag type that wraps cli.BoolFlag to allow | ||
| // for other values to be specified | ||
| type BoolFlag struct { | ||
| cli.BoolFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewBoolFlag creates a new BoolFlag | ||
| func NewBoolFlag(flag cli.BoolFlag) *BoolFlag { | ||
| return &BoolFlag{BoolFlag: flag, set: nil} | ||
| } | ||
| // ApplyInputSourceValue applies a Bool value to the flagSet if required | ||
@@ -222,21 +152,2 @@ func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { | ||
| // Apply saves the flagSet for later usage then calls | ||
| // the wrapped BoolFlag.Apply | ||
| func (f *BoolFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.BoolFlag.Apply(set) | ||
| } | ||
| // BoolTFlag is the flag type that wraps cli.BoolTFlag to allow | ||
| // for other values to be specified | ||
| type BoolTFlag struct { | ||
| cli.BoolTFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewBoolTFlag creates a new BoolTFlag | ||
| func NewBoolTFlag(flag cli.BoolTFlag) *BoolTFlag { | ||
| return &BoolTFlag{BoolTFlag: flag, set: nil} | ||
| } | ||
| // ApplyInputSourceValue applies a BoolT value to the flagSet if required | ||
@@ -260,22 +171,2 @@ func (f *BoolTFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { | ||
| // Apply saves the flagSet for later usage then calls | ||
| // the wrapped BoolTFlag.Apply | ||
| func (f *BoolTFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.BoolTFlag.Apply(set) | ||
| } | ||
| // StringFlag is the flag type that wraps cli.StringFlag to allow | ||
| // for other values to be specified | ||
| type StringFlag struct { | ||
| cli.StringFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewStringFlag creates a new StringFlag | ||
| func NewStringFlag(flag cli.StringFlag) *StringFlag { | ||
| return &StringFlag{StringFlag: flag, set: nil} | ||
| } | ||
| // ApplyInputSourceValue applies a String value to the flagSet if required | ||
@@ -299,22 +190,2 @@ func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { | ||
| // Apply saves the flagSet for later usage then calls | ||
| // the wrapped StringFlag.Apply | ||
| func (f *StringFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.StringFlag.Apply(set) | ||
| } | ||
| // IntFlag is the flag type that wraps cli.IntFlag to allow | ||
| // for other values to be specified | ||
| type IntFlag struct { | ||
| cli.IntFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewIntFlag creates a new IntFlag | ||
| func NewIntFlag(flag cli.IntFlag) *IntFlag { | ||
| return &IntFlag{IntFlag: flag, set: nil} | ||
| } | ||
| // ApplyInputSourceValue applies a int value to the flagSet if required | ||
@@ -338,21 +209,2 @@ func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { | ||
| // Apply saves the flagSet for later usage then calls | ||
| // the wrapped IntFlag.Apply | ||
| func (f *IntFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.IntFlag.Apply(set) | ||
| } | ||
| // DurationFlag is the flag type that wraps cli.DurationFlag to allow | ||
| // for other values to be specified | ||
| type DurationFlag struct { | ||
| cli.DurationFlag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewDurationFlag creates a new DurationFlag | ||
| func NewDurationFlag(flag cli.DurationFlag) *DurationFlag { | ||
| return &DurationFlag{DurationFlag: flag, set: nil} | ||
| } | ||
| // ApplyInputSourceValue applies a Duration value to the flagSet if required | ||
@@ -376,22 +228,2 @@ func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { | ||
| // Apply saves the flagSet for later usage then calls | ||
| // the wrapped DurationFlag.Apply | ||
| func (f *DurationFlag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.DurationFlag.Apply(set) | ||
| } | ||
| // Float64Flag is the flag type that wraps cli.Float64Flag to allow | ||
| // for other values to be specified | ||
| type Float64Flag struct { | ||
| cli.Float64Flag | ||
| set *flag.FlagSet | ||
| } | ||
| // NewFloat64Flag creates a new Float64Flag | ||
| func NewFloat64Flag(flag cli.Float64Flag) *Float64Flag { | ||
| return &Float64Flag{Float64Flag: flag, set: nil} | ||
| } | ||
| // ApplyInputSourceValue applies a Float64 value to the flagSet if required | ||
@@ -416,20 +248,10 @@ func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { | ||
| // Apply saves the flagSet for later usage then calls | ||
| // the wrapped Float64Flag.Apply | ||
| func (f *Float64Flag) Apply(set *flag.FlagSet) { | ||
| f.set = set | ||
| f.Float64Flag.Apply(set) | ||
| } | ||
| func isEnvVarSet(envVars string) bool { | ||
| for _, envVar := range strings.Split(envVars, ",") { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal := os.Getenv(envVar); envVal != "" { | ||
| if _, ok := syscall.Getenv(envVar); ok { | ||
| // TODO: Can't use this for bools as | ||
| // set means that it was true or false based on | ||
| // Bool flag type, should work for other types | ||
| if len(envVal) > 0 { | ||
| return true | ||
| } | ||
| return true | ||
| } | ||
@@ -436,0 +258,0 @@ } |
@@ -6,3 +6,3 @@ package altsrc | ||
| "github.com/micro/cli" | ||
| "gopkg.in/urfave/cli.v1" | ||
| ) | ||
@@ -9,0 +9,0 @@ |
+123
-13
@@ -6,5 +6,6 @@ package altsrc | ||
| "reflect" | ||
| "strings" | ||
| "time" | ||
| "github.com/micro/cli" | ||
| "gopkg.in/urfave/cli.v1" | ||
| ) | ||
@@ -15,5 +16,29 @@ | ||
| type MapInputSource struct { | ||
| valueMap map[string]interface{} | ||
| valueMap map[interface{}]interface{} | ||
| } | ||
| // nestedVal checks if the name has '.' delimiters. | ||
| // If so, it tries to traverse the tree by the '.' delimited sections to find | ||
| // a nested value for the key. | ||
| func nestedVal(name string, tree map[interface{}]interface{}) (interface{}, bool) { | ||
| if sections := strings.Split(name, "."); len(sections) > 1 { | ||
| node := tree | ||
| for _, section := range sections[:len(sections)-1] { | ||
| if child, ok := node[section]; !ok { | ||
| return nil, false | ||
| } else { | ||
| if ctype, ok := child.(map[interface{}]interface{}); !ok { | ||
| return nil, false | ||
| } else { | ||
| node = ctype | ||
| } | ||
| } | ||
| } | ||
| if val, ok := node[sections[len(sections)-1]]; ok { | ||
| return val, true | ||
| } | ||
| } | ||
| return nil, false | ||
| } | ||
| // Int returns an int from the map if it exists otherwise returns 0 | ||
@@ -27,5 +52,12 @@ func (fsm *MapInputSource) Int(name string) (int, error) { | ||
| } | ||
| return otherValue, nil | ||
| } | ||
| nestedGenericValue, exists := nestedVal(name, fsm.valueMap) | ||
| if exists { | ||
| otherValue, isType := nestedGenericValue.(int) | ||
| if !isType { | ||
| return 0, incorrectTypeForFlagError(name, "int", nestedGenericValue) | ||
| } | ||
| return otherValue, nil | ||
| } | ||
@@ -45,2 +77,10 @@ return 0, nil | ||
| } | ||
| nestedGenericValue, exists := nestedVal(name, fsm.valueMap) | ||
| if exists { | ||
| otherValue, isType := nestedGenericValue.(time.Duration) | ||
| if !isType { | ||
| return 0, incorrectTypeForFlagError(name, "duration", nestedGenericValue) | ||
| } | ||
| return otherValue, nil | ||
| } | ||
@@ -60,2 +100,10 @@ return 0, nil | ||
| } | ||
| nestedGenericValue, exists := nestedVal(name, fsm.valueMap) | ||
| if exists { | ||
| otherValue, isType := nestedGenericValue.(float64) | ||
| if !isType { | ||
| return 0, incorrectTypeForFlagError(name, "float64", nestedGenericValue) | ||
| } | ||
| return otherValue, nil | ||
| } | ||
@@ -75,2 +123,10 @@ return 0, nil | ||
| } | ||
| nestedGenericValue, exists := nestedVal(name, fsm.valueMap) | ||
| if exists { | ||
| otherValue, isType := nestedGenericValue.(string) | ||
| if !isType { | ||
| return "", incorrectTypeForFlagError(name, "string", nestedGenericValue) | ||
| } | ||
| return otherValue, nil | ||
| } | ||
@@ -83,11 +139,26 @@ return "", nil | ||
| otherGenericValue, exists := fsm.valueMap[name] | ||
| if exists { | ||
| otherValue, isType := otherGenericValue.([]string) | ||
| if !exists { | ||
| otherGenericValue, exists = nestedVal(name, fsm.valueMap) | ||
| if !exists { | ||
| return nil, nil | ||
| } | ||
| } | ||
| otherValue, isType := otherGenericValue.([]interface{}) | ||
| if !isType { | ||
| return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue) | ||
| } | ||
| var stringSlice = make([]string, 0, len(otherValue)) | ||
| for i, v := range otherValue { | ||
| stringValue, isType := v.(string) | ||
| if !isType { | ||
| return nil, incorrectTypeForFlagError(name, "[]string", otherGenericValue) | ||
| return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "string", v) | ||
| } | ||
| return otherValue, nil | ||
| stringSlice = append(stringSlice, stringValue) | ||
| } | ||
| return nil, nil | ||
| return stringSlice, nil | ||
| } | ||
@@ -98,11 +169,26 @@ | ||
| otherGenericValue, exists := fsm.valueMap[name] | ||
| if exists { | ||
| otherValue, isType := otherGenericValue.([]int) | ||
| if !exists { | ||
| otherGenericValue, exists = nestedVal(name, fsm.valueMap) | ||
| if !exists { | ||
| return nil, nil | ||
| } | ||
| } | ||
| otherValue, isType := otherGenericValue.([]interface{}) | ||
| if !isType { | ||
| return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue) | ||
| } | ||
| var intSlice = make([]int, 0, len(otherValue)) | ||
| for i, v := range otherValue { | ||
| intValue, isType := v.(int) | ||
| if !isType { | ||
| return nil, incorrectTypeForFlagError(name, "[]int", otherGenericValue) | ||
| return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "int", v) | ||
| } | ||
| return otherValue, nil | ||
| intSlice = append(intSlice, intValue) | ||
| } | ||
| return nil, nil | ||
| return intSlice, nil | ||
| } | ||
@@ -120,2 +206,10 @@ | ||
| } | ||
| nestedGenericValue, exists := nestedVal(name, fsm.valueMap) | ||
| if exists { | ||
| otherValue, isType := nestedGenericValue.(cli.Generic) | ||
| if !isType { | ||
| return nil, incorrectTypeForFlagError(name, "cli.Generic", nestedGenericValue) | ||
| } | ||
| return otherValue, nil | ||
| } | ||
@@ -135,2 +229,10 @@ return nil, nil | ||
| } | ||
| nestedGenericValue, exists := nestedVal(name, fsm.valueMap) | ||
| if exists { | ||
| otherValue, isType := nestedGenericValue.(bool) | ||
| if !isType { | ||
| return false, incorrectTypeForFlagError(name, "bool", nestedGenericValue) | ||
| } | ||
| return otherValue, nil | ||
| } | ||
@@ -150,2 +252,10 @@ return false, nil | ||
| } | ||
| nestedGenericValue, exists := nestedVal(name, fsm.valueMap) | ||
| if exists { | ||
| otherValue, isType := nestedGenericValue.(bool) | ||
| if !isType { | ||
| return true, incorrectTypeForFlagError(name, "bool", nestedGenericValue) | ||
| } | ||
| return otherValue, nil | ||
| } | ||
@@ -152,0 +262,0 @@ return true, nil |
@@ -14,3 +14,3 @@ // Disabling building of yaml support in cases where golang is 1.0 or 1.1 | ||
| "github.com/micro/cli" | ||
| "gopkg.in/urfave/cli.v1" | ||
| ) | ||
@@ -33,5 +33,6 @@ | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) { | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("test") | ||
| expect(t, val, 15) | ||
| return nil | ||
| }, | ||
@@ -66,5 +67,6 @@ Flags: []cli.Flag{ | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) { | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("test") | ||
| expect(t, val, 10) | ||
| return nil | ||
| }, | ||
@@ -82,2 +84,37 @@ Flags: []cli.Flag{ | ||
| func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.yaml", []byte(`top: | ||
| test: 15`), 0666) | ||
| defer os.Remove("current.yaml") | ||
| os.Setenv("THE_TEST", "10") | ||
| defer os.Setenv("THE_TEST", "") | ||
| test := []string{"test-cmd", "--load", "current.yaml"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("top.test") | ||
| expect(t, val, 10) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "top.test", EnvVar: "THE_TEST"}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } | ||
| func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) { | ||
@@ -99,5 +136,6 @@ app := cli.NewApp() | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) { | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("test") | ||
| expect(t, val, 7) | ||
| return nil | ||
| }, | ||
@@ -115,2 +153,35 @@ Flags: []cli.Flag{ | ||
| func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.yaml", []byte(`top: | ||
| test: 15`), 0666) | ||
| defer os.Remove("current.yaml") | ||
| test := []string{"test-cmd", "--load", "current.yaml", "--top.test", "7"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("top.test") | ||
| expect(t, val, 7) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "top.test"}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } | ||
| func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) { | ||
@@ -132,5 +203,6 @@ app := cli.NewApp() | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) { | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("test") | ||
| expect(t, val, 15) | ||
| return nil | ||
| }, | ||
@@ -148,2 +220,35 @@ Flags: []cli.Flag{ | ||
| func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.yaml", []byte(`top: | ||
| test: 15`), 0666) | ||
| defer os.Remove("current.yaml") | ||
| test := []string{"test-cmd", "--load", "current.yaml"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("top.test") | ||
| expect(t, val, 15) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } | ||
| func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T) { | ||
@@ -168,5 +273,6 @@ app := cli.NewApp() | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) { | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("test") | ||
| expect(t, val, 11) | ||
| return nil | ||
| }, | ||
@@ -182,1 +288,36 @@ Flags: []cli.Flag{ | ||
| } | ||
| func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *testing.T) { | ||
| app := cli.NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| ioutil.WriteFile("current.yaml", []byte(`top: | ||
| test: 15`), 0666) | ||
| defer os.Remove("current.yaml") | ||
| os.Setenv("THE_TEST", "11") | ||
| defer os.Setenv("THE_TEST", "") | ||
| test := []string{"test-cmd", "--load", "current.yaml"} | ||
| set.Parse(test) | ||
| c := cli.NewContext(app, set, nil) | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) error { | ||
| val := c.Int("top.test") | ||
| expect(t, val, 11) | ||
| return nil | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVar: "THE_TEST"}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| expect(t, err, nil) | ||
| } |
@@ -14,4 +14,6 @@ // Disabling building of yaml support in cases where golang is 1.0 or 1.1 | ||
| "os" | ||
| "runtime" | ||
| "strings" | ||
| "github.com/micro/cli" | ||
| "gopkg.in/urfave/cli.v1" | ||
@@ -28,3 +30,3 @@ "gopkg.in/yaml.v2" | ||
| ysc := &yamlSourceContext{FilePath: file} | ||
| var results map[string]interface{} | ||
| var results map[interface{}]interface{} | ||
| err := readCommandYaml(ysc.FilePath, &results) | ||
@@ -83,2 +85,8 @@ if err != nil { | ||
| return ioutil.ReadFile(filePath) | ||
| } else if runtime.GOOS == "windows" && strings.Contains(u.String(), "\\") { | ||
| // on Windows systems u.Path is always empty, so we need to check the string directly. | ||
| if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil { | ||
| return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath) | ||
| } | ||
| return ioutil.ReadFile(filePath) | ||
| } else { | ||
@@ -85,0 +93,0 @@ return nil, fmt.Errorf("unable to determine how to load from path %s", filePath) |
+740
-95
@@ -16,2 +16,19 @@ package cli | ||
| var ( | ||
| lastExitCode = 0 | ||
| fakeOsExiter = func(rc int) { | ||
| lastExitCode = rc | ||
| } | ||
| fakeErrWriter = &bytes.Buffer{} | ||
| ) | ||
| func init() { | ||
| OsExiter = fakeOsExiter | ||
| ErrWriter = fakeErrWriter | ||
| } | ||
| type opCounts struct { | ||
| Total, BashComplete, OnUsageError, Before, CommandNotFound, Action, After, SubCommand int | ||
| } | ||
| func ExampleApp_Run() { | ||
@@ -26,4 +43,5 @@ // set args for examples sake | ||
| } | ||
| app.Action = func(c *Context) { | ||
| app.Action = func(c *Context) error { | ||
| fmt.Printf("Hello %v\n", c.String("name")) | ||
| return nil | ||
| } | ||
@@ -33,3 +51,3 @@ app.UsageText = "app [first_arg] [second_arg]" | ||
| app.Email = "harrison@lolwut.com" | ||
| app.Authors = []Author{Author{Name: "Oliver Allen", Email: "oliver@toyshop.com"}} | ||
| app.Authors = []Author{{Name: "Oliver Allen", Email: "oliver@toyshop.com"}} | ||
| app.Run(os.Args) | ||
@@ -64,4 +82,5 @@ // Output: | ||
| }, | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| fmt.Println("Hello,", c.String("name")) | ||
| return nil | ||
| }, | ||
@@ -78,4 +97,59 @@ }, | ||
| func ExampleApp_Run_help() { | ||
| func ExampleApp_Run_appHelp() { | ||
| // set args for examples sake | ||
| os.Args = []string{"greet", "help"} | ||
| app := NewApp() | ||
| app.Name = "greet" | ||
| app.Version = "0.1.0" | ||
| app.Description = "This is how we describe greet the app" | ||
| app.Authors = []Author{ | ||
| {Name: "Harrison", Email: "harrison@lolwut.com"}, | ||
| {Name: "Oliver Allen", Email: "oliver@toyshop.com"}, | ||
| } | ||
| app.Flags = []Flag{ | ||
| StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, | ||
| } | ||
| app.Commands = []Command{ | ||
| { | ||
| Name: "describeit", | ||
| Aliases: []string{"d"}, | ||
| Usage: "use it to see a description", | ||
| Description: "This is how we describe describeit the function", | ||
| Action: func(c *Context) error { | ||
| fmt.Printf("i like to describe things") | ||
| return nil | ||
| }, | ||
| }, | ||
| } | ||
| app.Run(os.Args) | ||
| // Output: | ||
| // NAME: | ||
| // greet - A new cli application | ||
| // | ||
| // USAGE: | ||
| // greet [global options] command [command options] [arguments...] | ||
| // | ||
| // VERSION: | ||
| // 0.1.0 | ||
| // | ||
| // DESCRIPTION: | ||
| // This is how we describe greet the app | ||
| // | ||
| // AUTHORS: | ||
| // Harrison <harrison@lolwut.com> | ||
| // Oliver Allen <oliver@toyshop.com> | ||
| // | ||
| // COMMANDS: | ||
| // describeit, d use it to see a description | ||
| // help, h Shows a list of commands or help for one command | ||
| // | ||
| // GLOBAL OPTIONS: | ||
| // --name value a name to say (default: "bob") | ||
| // --help, -h show help | ||
| // --version, -v print the version | ||
| } | ||
| func ExampleApp_Run_commandHelp() { | ||
| // set args for examples sake | ||
| os.Args = []string{"greet", "h", "describeit"} | ||
@@ -94,4 +168,5 @@ | ||
| Description: "This is how we describe describeit the function", | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| fmt.Printf("i like to describe things") | ||
| return nil | ||
| }, | ||
@@ -112,2 +187,45 @@ }, | ||
| func ExampleApp_Run_noAction() { | ||
| app := App{} | ||
| app.Name = "greet" | ||
| app.Run([]string{"greet"}) | ||
| // Output: | ||
| // NAME: | ||
| // greet | ||
| // | ||
| // USAGE: | ||
| // [global options] command [command options] [arguments...] | ||
| // | ||
| // COMMANDS: | ||
| // help, h Shows a list of commands or help for one command | ||
| // | ||
| // GLOBAL OPTIONS: | ||
| // --help, -h show help | ||
| // --version, -v print the version | ||
| } | ||
| func ExampleApp_Run_subcommandNoAction() { | ||
| app := App{} | ||
| app.Name = "greet" | ||
| app.Commands = []Command{ | ||
| { | ||
| Name: "describeit", | ||
| Aliases: []string{"d"}, | ||
| Usage: "use it to see a description", | ||
| Description: "This is how we describe describeit the function", | ||
| }, | ||
| } | ||
| app.Run([]string{"greet", "describeit"}) | ||
| // Output: | ||
| // NAME: | ||
| // describeit - use it to see a description | ||
| // | ||
| // USAGE: | ||
| // describeit [arguments...] | ||
| // | ||
| // DESCRIPTION: | ||
| // This is how we describe describeit the function | ||
| } | ||
| func ExampleApp_Run_bashComplete() { | ||
@@ -126,4 +244,5 @@ // set args for examples sake | ||
| Description: "This is how we describe describeit the function", | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| fmt.Printf("i like to describe things") | ||
| return nil | ||
| }, | ||
@@ -134,4 +253,5 @@ }, { | ||
| Description: "more stuff to see when generating bash completion", | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| fmt.Printf("the next example") | ||
| return nil | ||
| }, | ||
@@ -154,4 +274,5 @@ }, | ||
| app := NewApp() | ||
| app.Action = func(c *Context) { | ||
| app.Action = func(c *Context) error { | ||
| s = s + c.Args().First() | ||
| return nil | ||
| } | ||
@@ -192,2 +313,8 @@ | ||
| func TestApp_Setup_defaultsWriter(t *testing.T) { | ||
| app := &App{} | ||
| app.Setup() | ||
| expect(t, app.Writer, os.Stdout) | ||
| } | ||
| func TestApp_CommandWithArgBeforeFlags(t *testing.T) { | ||
@@ -202,5 +329,6 @@ var parsedOption, firstArg string | ||
| }, | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| parsedOption = c.String("option") | ||
| firstArg = c.Args().First() | ||
| return nil | ||
| }, | ||
@@ -223,4 +351,5 @@ } | ||
| Name: "foo", | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| context = c | ||
| return nil | ||
| }, | ||
@@ -243,2 +372,19 @@ Flags: []Flag{ | ||
| func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) { | ||
| a := App{ | ||
| Flags: []Flag{ | ||
| StringFlag{Name: "--foo"}, | ||
| }, | ||
| Writer: bytes.NewBufferString(""), | ||
| } | ||
| set := flag.NewFlagSet("", flag.ContinueOnError) | ||
| set.Parse([]string{"", "---foo"}) | ||
| c := &Context{flagSet: set} | ||
| err := a.RunAsSubcommand(c) | ||
| expect(t, err, errors.New("bad flag syntax: ---foo")) | ||
| } | ||
| func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) { | ||
@@ -254,5 +400,6 @@ var parsedOption string | ||
| }, | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| parsedOption = c.String("option") | ||
| args = c.Args() | ||
| return nil | ||
| }, | ||
@@ -276,4 +423,5 @@ } | ||
| Name: "cmd", | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| args = c.Args() | ||
| return nil | ||
| }, | ||
@@ -295,4 +443,5 @@ } | ||
| Name: "cmd", | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| args = c.Args() | ||
| return nil | ||
| }, | ||
@@ -309,2 +458,44 @@ } | ||
| func TestApp_VisibleCommands(t *testing.T) { | ||
| app := NewApp() | ||
| app.Commands = []Command{ | ||
| { | ||
| Name: "frob", | ||
| HelpName: "foo frob", | ||
| Action: func(_ *Context) error { return nil }, | ||
| }, | ||
| { | ||
| Name: "frib", | ||
| HelpName: "foo frib", | ||
| Hidden: true, | ||
| Action: func(_ *Context) error { return nil }, | ||
| }, | ||
| } | ||
| app.Setup() | ||
| expected := []Command{ | ||
| app.Commands[0], | ||
| app.Commands[2], // help | ||
| } | ||
| actual := app.VisibleCommands() | ||
| expect(t, len(expected), len(actual)) | ||
| for i, actualCommand := range actual { | ||
| expectedCommand := expected[i] | ||
| if expectedCommand.Action != nil { | ||
| // comparing func addresses is OK! | ||
| 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 TestApp_Float64Flag(t *testing.T) { | ||
@@ -317,4 +508,5 @@ var meters float64 | ||
| } | ||
| app.Action = func(c *Context) { | ||
| app.Action = func(c *Context) error { | ||
| meters = c.Float64("height") | ||
| return nil | ||
| } | ||
@@ -337,5 +529,6 @@ | ||
| }, | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| parsedIntSlice = c.IntSlice("p") | ||
| parsedStringSlice = c.StringSlice("ip") | ||
| return nil | ||
| }, | ||
@@ -393,5 +586,6 @@ } | ||
| }, | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| parsedIntSlice = c.IntSlice("a") | ||
| parsedStringSlice = c.StringSlice("str") | ||
| return nil | ||
| }, | ||
@@ -460,3 +654,3 @@ } | ||
| func TestApp_BeforeFunc(t *testing.T) { | ||
| beforeRun, subcommandRun := false, false | ||
| counts := &opCounts{} | ||
| beforeError := fmt.Errorf("fail") | ||
@@ -468,3 +662,4 @@ var err error | ||
| app.Before = func(c *Context) error { | ||
| beforeRun = true | ||
| counts.Total++ | ||
| counts.Before = counts.Total | ||
| s := c.String("opt") | ||
@@ -479,6 +674,8 @@ if s == "fail" { | ||
| app.Commands = []Command{ | ||
| Command{ | ||
| { | ||
| Name: "sub", | ||
| Action: func(c *Context) { | ||
| subcommandRun = true | ||
| Action: func(c *Context) error { | ||
| counts.Total++ | ||
| counts.SubCommand = counts.Total | ||
| return nil | ||
| }, | ||
@@ -499,7 +696,7 @@ }, | ||
| if beforeRun == false { | ||
| if counts.Before != 1 { | ||
| t.Errorf("Before() not executed when expected") | ||
| } | ||
| if subcommandRun == false { | ||
| if counts.SubCommand != 2 { | ||
| t.Errorf("Subcommand not executed when expected") | ||
@@ -509,3 +706,3 @@ } | ||
| // reset | ||
| beforeRun, subcommandRun = false, false | ||
| counts = &opCounts{} | ||
@@ -520,14 +717,37 @@ // run with the Before() func failing | ||
| if beforeRun == false { | ||
| if counts.Before != 1 { | ||
| t.Errorf("Before() not executed when expected") | ||
| } | ||
| if subcommandRun == true { | ||
| if counts.SubCommand != 0 { | ||
| t.Errorf("Subcommand executed when NOT expected") | ||
| } | ||
| // reset | ||
| counts = &opCounts{} | ||
| afterError := errors.New("fail again") | ||
| app.After = func(_ *Context) error { | ||
| return afterError | ||
| } | ||
| // run with the Before() func failing, wrapped by After() | ||
| err = app.Run([]string{"command", "--opt", "fail", "sub"}) | ||
| // should be the same error produced by the Before func | ||
| if _, ok := err.(MultiError); !ok { | ||
| t.Errorf("MultiError expected, but not received") | ||
| } | ||
| if counts.Before != 1 { | ||
| t.Errorf("Before() not executed when expected") | ||
| } | ||
| if counts.SubCommand != 0 { | ||
| t.Errorf("Subcommand executed when NOT expected") | ||
| } | ||
| } | ||
| func TestApp_AfterFunc(t *testing.T) { | ||
| afterRun, subcommandRun := false, false | ||
| counts := &opCounts{} | ||
| afterError := fmt.Errorf("fail") | ||
@@ -539,3 +759,4 @@ var err error | ||
| app.After = func(c *Context) error { | ||
| afterRun = true | ||
| counts.Total++ | ||
| counts.After = counts.Total | ||
| s := c.String("opt") | ||
@@ -550,6 +771,8 @@ if s == "fail" { | ||
| app.Commands = []Command{ | ||
| Command{ | ||
| { | ||
| Name: "sub", | ||
| Action: func(c *Context) { | ||
| subcommandRun = true | ||
| Action: func(c *Context) error { | ||
| counts.Total++ | ||
| counts.SubCommand = counts.Total | ||
| return nil | ||
| }, | ||
@@ -570,7 +793,7 @@ }, | ||
| if afterRun == false { | ||
| if counts.After != 2 { | ||
| t.Errorf("After() not executed when expected") | ||
| } | ||
| if subcommandRun == false { | ||
| if counts.SubCommand != 1 { | ||
| t.Errorf("Subcommand not executed when expected") | ||
@@ -580,3 +803,3 @@ } | ||
| // reset | ||
| afterRun, subcommandRun = false, false | ||
| counts = &opCounts{} | ||
@@ -591,7 +814,7 @@ // run with the Before() func failing | ||
| if afterRun == false { | ||
| if counts.After != 2 { | ||
| t.Errorf("After() not executed when expected") | ||
| } | ||
| if subcommandRun == false { | ||
| if counts.SubCommand != 1 { | ||
| t.Errorf("Subcommand not executed when expected") | ||
@@ -637,3 +860,3 @@ } | ||
| func TestAppVersionPrinter(t *testing.T) { | ||
| func TestApp_VersionPrinter(t *testing.T) { | ||
| oldPrinter := VersionPrinter | ||
@@ -658,15 +881,18 @@ defer func() { | ||
| func TestAppCommandNotFound(t *testing.T) { | ||
| beforeRun, subcommandRun := false, false | ||
| func TestApp_CommandNotFound(t *testing.T) { | ||
| counts := &opCounts{} | ||
| app := NewApp() | ||
| app.CommandNotFound = func(c *Context, command string) { | ||
| beforeRun = true | ||
| counts.Total++ | ||
| counts.CommandNotFound = counts.Total | ||
| } | ||
| app.Commands = []Command{ | ||
| Command{ | ||
| { | ||
| Name: "bar", | ||
| Action: func(c *Context) { | ||
| subcommandRun = true | ||
| Action: func(c *Context) error { | ||
| counts.Total++ | ||
| counts.SubCommand = counts.Total | ||
| return nil | ||
| }, | ||
@@ -678,58 +904,146 @@ }, | ||
| expect(t, beforeRun, true) | ||
| expect(t, subcommandRun, false) | ||
| expect(t, counts.CommandNotFound, 1) | ||
| expect(t, counts.SubCommand, 0) | ||
| expect(t, counts.Total, 1) | ||
| } | ||
| func TestGlobalFlag(t *testing.T) { | ||
| var globalFlag string | ||
| var globalFlagSet bool | ||
| func TestApp_OrderOfOperations(t *testing.T) { | ||
| counts := &opCounts{} | ||
| resetCounts := func() { counts = &opCounts{} } | ||
| app := NewApp() | ||
| app.Flags = []Flag{ | ||
| StringFlag{Name: "global, g", Usage: "global"}, | ||
| app.EnableBashCompletion = true | ||
| app.BashComplete = func(c *Context) { | ||
| counts.Total++ | ||
| counts.BashComplete = counts.Total | ||
| } | ||
| app.Action = func(c *Context) { | ||
| globalFlag = c.GlobalString("global") | ||
| globalFlagSet = c.GlobalIsSet("global") | ||
| app.OnUsageError = func(c *Context, err error, isSubcommand bool) error { | ||
| counts.Total++ | ||
| counts.OnUsageError = counts.Total | ||
| return errors.New("hay OnUsageError") | ||
| } | ||
| app.Run([]string{"command", "-g", "foo"}) | ||
| expect(t, globalFlag, "foo") | ||
| expect(t, globalFlagSet, true) | ||
| } | ||
| beforeNoError := func(c *Context) error { | ||
| counts.Total++ | ||
| counts.Before = counts.Total | ||
| return nil | ||
| } | ||
| func TestGlobalFlagsInSubcommands(t *testing.T) { | ||
| subcommandRun := false | ||
| parentFlag := false | ||
| app := NewApp() | ||
| beforeError := func(c *Context) error { | ||
| counts.Total++ | ||
| counts.Before = counts.Total | ||
| return errors.New("hay Before") | ||
| } | ||
| app.Flags = []Flag{ | ||
| BoolFlag{Name: "debug, d", Usage: "Enable debugging"}, | ||
| app.Before = beforeNoError | ||
| app.CommandNotFound = func(c *Context, command string) { | ||
| counts.Total++ | ||
| counts.CommandNotFound = counts.Total | ||
| } | ||
| afterNoError := func(c *Context) error { | ||
| counts.Total++ | ||
| counts.After = counts.Total | ||
| return nil | ||
| } | ||
| afterError := func(c *Context) error { | ||
| counts.Total++ | ||
| counts.After = counts.Total | ||
| return errors.New("hay After") | ||
| } | ||
| app.After = afterNoError | ||
| app.Commands = []Command{ | ||
| Command{ | ||
| Name: "foo", | ||
| Flags: []Flag{ | ||
| BoolFlag{Name: "parent, p", Usage: "Parent flag"}, | ||
| { | ||
| Name: "bar", | ||
| Action: func(c *Context) error { | ||
| counts.Total++ | ||
| counts.SubCommand = counts.Total | ||
| return nil | ||
| }, | ||
| Subcommands: []Command{ | ||
| { | ||
| Name: "bar", | ||
| Action: func(c *Context) { | ||
| if c.GlobalBool("debug") { | ||
| subcommandRun = true | ||
| } | ||
| if c.GlobalBool("parent") { | ||
| parentFlag = true | ||
| } | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| app.Run([]string{"command", "-d", "foo", "-p", "bar"}) | ||
| app.Action = func(c *Context) error { | ||
| counts.Total++ | ||
| counts.Action = counts.Total | ||
| return nil | ||
| } | ||
| expect(t, subcommandRun, true) | ||
| expect(t, parentFlag, true) | ||
| _ = app.Run([]string{"command", "--nope"}) | ||
| expect(t, counts.OnUsageError, 1) | ||
| expect(t, counts.Total, 1) | ||
| resetCounts() | ||
| _ = app.Run([]string{"command", "--generate-bash-completion"}) | ||
| expect(t, counts.BashComplete, 1) | ||
| expect(t, counts.Total, 1) | ||
| resetCounts() | ||
| oldOnUsageError := app.OnUsageError | ||
| app.OnUsageError = nil | ||
| _ = app.Run([]string{"command", "--nope"}) | ||
| expect(t, counts.Total, 0) | ||
| app.OnUsageError = oldOnUsageError | ||
| resetCounts() | ||
| _ = app.Run([]string{"command", "foo"}) | ||
| expect(t, counts.OnUsageError, 0) | ||
| expect(t, counts.Before, 1) | ||
| expect(t, counts.CommandNotFound, 0) | ||
| expect(t, counts.Action, 2) | ||
| expect(t, counts.After, 3) | ||
| expect(t, counts.Total, 3) | ||
| resetCounts() | ||
| app.Before = beforeError | ||
| _ = app.Run([]string{"command", "bar"}) | ||
| expect(t, counts.OnUsageError, 0) | ||
| expect(t, counts.Before, 1) | ||
| expect(t, counts.After, 2) | ||
| expect(t, counts.Total, 2) | ||
| app.Before = beforeNoError | ||
| resetCounts() | ||
| app.After = nil | ||
| _ = app.Run([]string{"command", "bar"}) | ||
| expect(t, counts.OnUsageError, 0) | ||
| expect(t, counts.Before, 1) | ||
| expect(t, counts.SubCommand, 2) | ||
| expect(t, counts.Total, 2) | ||
| app.After = afterNoError | ||
| resetCounts() | ||
| app.After = afterError | ||
| err := app.Run([]string{"command", "bar"}) | ||
| if err == nil { | ||
| t.Fatalf("expected a non-nil error") | ||
| } | ||
| expect(t, counts.OnUsageError, 0) | ||
| expect(t, counts.Before, 1) | ||
| expect(t, counts.SubCommand, 2) | ||
| expect(t, counts.After, 3) | ||
| expect(t, counts.Total, 3) | ||
| app.After = afterNoError | ||
| resetCounts() | ||
| oldCommands := app.Commands | ||
| app.Commands = nil | ||
| _ = app.Run([]string{"command"}) | ||
| expect(t, counts.OnUsageError, 0) | ||
| expect(t, counts.Before, 1) | ||
| expect(t, counts.Action, 2) | ||
| expect(t, counts.After, 3) | ||
| expect(t, counts.Total, 3) | ||
| app.Commands = oldCommands | ||
| } | ||
@@ -763,2 +1077,3 @@ | ||
| Subcommands: []Command{subCmdBar, subCmdBaz}, | ||
| Action: func(c *Context) error { return nil }, | ||
| } | ||
@@ -927,4 +1242,5 @@ | ||
| app.Writer = buf | ||
| app.Action = func(c *Context) { | ||
| app.Action = func(c *Context) error { | ||
| buf.WriteString("boom I say!") | ||
| return nil | ||
| } | ||
@@ -959,4 +1275,5 @@ | ||
| app.Writer = buf | ||
| app.Action = func(c *Context) { | ||
| app.Action = func(c *Context) error { | ||
| buf.WriteString("boom I say!") | ||
| return nil | ||
| } | ||
@@ -981,12 +1298,13 @@ | ||
| app.Name = "categories" | ||
| app.HideHelp = true | ||
| app.Commands = []Command{ | ||
| Command{ | ||
| { | ||
| Name: "command1", | ||
| Category: "1", | ||
| }, | ||
| Command{ | ||
| { | ||
| Name: "command2", | ||
| Category: "1", | ||
| }, | ||
| Command{ | ||
| { | ||
| Name: "command3", | ||
@@ -1023,10 +1341,116 @@ Category: "2", | ||
| if !strings.Contains(output, "1:\n command1") { | ||
| t.Errorf("want buffer to include category %q, did not: \n%q", "1:\n command1", output) | ||
| if !strings.Contains(output, "1:\n command1") { | ||
| t.Errorf("want buffer to include category %q, did not: \n%q", "1:\n command1", output) | ||
| } | ||
| } | ||
| func TestApp_VisibleCategories(t *testing.T) { | ||
| app := NewApp() | ||
| app.Name = "visible-categories" | ||
| app.HideHelp = true | ||
| app.Commands = []Command{ | ||
| { | ||
| Name: "command1", | ||
| Category: "1", | ||
| HelpName: "foo command1", | ||
| Hidden: true, | ||
| }, | ||
| { | ||
| Name: "command2", | ||
| Category: "2", | ||
| HelpName: "foo command2", | ||
| }, | ||
| { | ||
| Name: "command3", | ||
| Category: "3", | ||
| HelpName: "foo command3", | ||
| }, | ||
| } | ||
| expected := []*CommandCategory{ | ||
| { | ||
| Name: "2", | ||
| Commands: []Command{ | ||
| app.Commands[1], | ||
| }, | ||
| }, | ||
| { | ||
| Name: "3", | ||
| Commands: []Command{ | ||
| app.Commands[2], | ||
| }, | ||
| }, | ||
| } | ||
| app.Setup() | ||
| expect(t, expected, app.VisibleCategories()) | ||
| app = NewApp() | ||
| app.Name = "visible-categories" | ||
| app.HideHelp = true | ||
| app.Commands = []Command{ | ||
| { | ||
| Name: "command1", | ||
| Category: "1", | ||
| HelpName: "foo command1", | ||
| Hidden: true, | ||
| }, | ||
| { | ||
| Name: "command2", | ||
| Category: "2", | ||
| HelpName: "foo command2", | ||
| Hidden: true, | ||
| }, | ||
| { | ||
| Name: "command3", | ||
| Category: "3", | ||
| HelpName: "foo command3", | ||
| }, | ||
| } | ||
| expected = []*CommandCategory{ | ||
| { | ||
| Name: "3", | ||
| Commands: []Command{ | ||
| app.Commands[2], | ||
| }, | ||
| }, | ||
| } | ||
| app.Setup() | ||
| expect(t, expected, app.VisibleCategories()) | ||
| app = NewApp() | ||
| app.Name = "visible-categories" | ||
| app.HideHelp = true | ||
| app.Commands = []Command{ | ||
| { | ||
| Name: "command1", | ||
| Category: "1", | ||
| HelpName: "foo command1", | ||
| Hidden: true, | ||
| }, | ||
| { | ||
| Name: "command2", | ||
| Category: "2", | ||
| HelpName: "foo command2", | ||
| Hidden: true, | ||
| }, | ||
| { | ||
| Name: "command3", | ||
| Category: "3", | ||
| HelpName: "foo command3", | ||
| Hidden: true, | ||
| }, | ||
| } | ||
| expected = []*CommandCategory{} | ||
| app.Setup() | ||
| expect(t, expected, app.VisibleCategories()) | ||
| } | ||
| func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { | ||
| app := NewApp() | ||
| app.Action = func(c *Context) {} | ||
| app.Action = func(c *Context) error { return nil } | ||
| app.Before = func(c *Context) error { return fmt.Errorf("before error") } | ||
@@ -1051,5 +1475,5 @@ app.After = func(c *Context) error { return fmt.Errorf("after error") } | ||
| app.Commands = []Command{ | ||
| Command{ | ||
| { | ||
| Subcommands: []Command{ | ||
| Command{ | ||
| { | ||
| Name: "sub", | ||
@@ -1092,3 +1516,3 @@ }, | ||
| app.Commands = []Command{ | ||
| Command{ | ||
| { | ||
| Name: "bar", | ||
@@ -1123,3 +1547,3 @@ }, | ||
| app.Commands = []Command{ | ||
| Command{ | ||
| { | ||
| Name: "bar", | ||
@@ -1138,1 +1562,222 @@ }, | ||
| } | ||
| // A custom flag that conforms to the relevant interfaces, but has none of the | ||
| // fields that the other flag types do. | ||
| type customBoolFlag struct { | ||
| Nombre string | ||
| } | ||
| // Don't use the normal FlagStringer | ||
| func (c *customBoolFlag) String() string { | ||
| return "***" + c.Nombre + "***" | ||
| } | ||
| func (c *customBoolFlag) GetName() string { | ||
| return c.Nombre | ||
| } | ||
| func (c *customBoolFlag) Apply(set *flag.FlagSet) { | ||
| set.String(c.Nombre, c.Nombre, "") | ||
| } | ||
| func TestCustomFlagsUnused(t *testing.T) { | ||
| app := NewApp() | ||
| app.Flags = []Flag{&customBoolFlag{"custom"}} | ||
| err := app.Run([]string{"foo"}) | ||
| if err != nil { | ||
| t.Errorf("Run returned unexpected error: %v", err) | ||
| } | ||
| } | ||
| func TestCustomFlagsUsed(t *testing.T) { | ||
| app := NewApp() | ||
| app.Flags = []Flag{&customBoolFlag{"custom"}} | ||
| err := app.Run([]string{"foo", "--custom=bar"}) | ||
| if err != nil { | ||
| t.Errorf("Run returned unexpected error: %v", err) | ||
| } | ||
| } | ||
| func TestCustomHelpVersionFlags(t *testing.T) { | ||
| app := NewApp() | ||
| // Be sure to reset the global flags | ||
| defer func(helpFlag Flag, versionFlag Flag) { | ||
| HelpFlag = helpFlag | ||
| VersionFlag = versionFlag | ||
| }(HelpFlag, VersionFlag) | ||
| HelpFlag = &customBoolFlag{"help-custom"} | ||
| VersionFlag = &customBoolFlag{"version-custom"} | ||
| err := app.Run([]string{"foo", "--help-custom=bar"}) | ||
| if err != nil { | ||
| t.Errorf("Run returned unexpected error: %v", err) | ||
| } | ||
| } | ||
| func TestHandleAction_WithNonFuncAction(t *testing.T) { | ||
| app := NewApp() | ||
| app.Action = 42 | ||
| fs, err := flagSet(app.Name, app.Flags) | ||
| if err != nil { | ||
| t.Errorf("error creating FlagSet: %s", err) | ||
| } | ||
| err = HandleAction(app.Action, NewContext(app, fs, nil)) | ||
| if err == nil { | ||
| t.Fatalf("expected to receive error from Run, got none") | ||
| } | ||
| exitErr, ok := err.(*ExitError) | ||
| if !ok { | ||
| t.Fatalf("expected to receive a *ExitError") | ||
| } | ||
| if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action type.") { | ||
| t.Fatalf("expected an unknown Action error, but got: %v", exitErr.Error()) | ||
| } | ||
| if exitErr.ExitCode() != 2 { | ||
| t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode()) | ||
| } | ||
| } | ||
| func TestHandleAction_WithInvalidFuncSignature(t *testing.T) { | ||
| app := NewApp() | ||
| app.Action = func() string { return "" } | ||
| fs, err := flagSet(app.Name, app.Flags) | ||
| if err != nil { | ||
| t.Errorf("error creating FlagSet: %s", err) | ||
| } | ||
| err = HandleAction(app.Action, NewContext(app, fs, nil)) | ||
| if err == nil { | ||
| t.Fatalf("expected to receive error from Run, got none") | ||
| } | ||
| exitErr, ok := err.(*ExitError) | ||
| if !ok { | ||
| t.Fatalf("expected to receive a *ExitError") | ||
| } | ||
| if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action type") { | ||
| t.Fatalf("expected an unknown Action error, but got: %v", exitErr.Error()) | ||
| } | ||
| if exitErr.ExitCode() != 2 { | ||
| t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode()) | ||
| } | ||
| } | ||
| func TestHandleAction_WithInvalidFuncReturnSignature(t *testing.T) { | ||
| app := NewApp() | ||
| app.Action = func(_ *Context) (int, error) { return 0, nil } | ||
| fs, err := flagSet(app.Name, app.Flags) | ||
| if err != nil { | ||
| t.Errorf("error creating FlagSet: %s", err) | ||
| } | ||
| err = HandleAction(app.Action, NewContext(app, fs, nil)) | ||
| if err == nil { | ||
| t.Fatalf("expected to receive error from Run, got none") | ||
| } | ||
| exitErr, ok := err.(*ExitError) | ||
| if !ok { | ||
| t.Fatalf("expected to receive a *ExitError") | ||
| } | ||
| if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action type") { | ||
| t.Fatalf("expected an invalid Action signature error, but got: %v", exitErr.Error()) | ||
| } | ||
| if exitErr.ExitCode() != 2 { | ||
| t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode()) | ||
| } | ||
| } | ||
| func TestHandleAction_WithUnknownPanic(t *testing.T) { | ||
| defer func() { refute(t, recover(), nil) }() | ||
| var fn ActionFunc | ||
| app := NewApp() | ||
| app.Action = func(ctx *Context) error { | ||
| fn(ctx) | ||
| return nil | ||
| } | ||
| fs, err := flagSet(app.Name, app.Flags) | ||
| if err != nil { | ||
| t.Errorf("error creating FlagSet: %s", err) | ||
| } | ||
| HandleAction(app.Action, NewContext(app, fs, nil)) | ||
| } | ||
| func TestShellCompletionForIncompleteFlags(t *testing.T) { | ||
| app := NewApp() | ||
| app.Flags = []Flag{ | ||
| IntFlag{ | ||
| Name: "test-completion", | ||
| }, | ||
| } | ||
| app.EnableBashCompletion = true | ||
| app.BashComplete = func(ctx *Context) { | ||
| for _, command := range ctx.App.Commands { | ||
| if command.Hidden { | ||
| continue | ||
| } | ||
| for _, name := range command.Names() { | ||
| fmt.Fprintln(ctx.App.Writer, name) | ||
| } | ||
| } | ||
| for _, flag := range ctx.App.Flags { | ||
| for _, name := range strings.Split(flag.GetName(), ",") { | ||
| if name == BashCompletionFlag.GetName() { | ||
| continue | ||
| } | ||
| switch name = strings.TrimSpace(name); len(name) { | ||
| case 0: | ||
| case 1: | ||
| fmt.Fprintln(ctx.App.Writer, "-"+name) | ||
| default: | ||
| fmt.Fprintln(ctx.App.Writer, "--"+name) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| app.Action = func(ctx *Context) error { | ||
| return fmt.Errorf("should not get here") | ||
| } | ||
| err := app.Run([]string{"", "--test-completion", "--" + BashCompletionFlag.GetName()}) | ||
| if err != nil { | ||
| t.Errorf("app should not return an error: %s", err) | ||
| } | ||
| } | ||
| func TestHandleActionActuallyWorksWithActions(t *testing.T) { | ||
| var f ActionFunc | ||
| called := false | ||
| f = func(c *Context) error { | ||
| called = true | ||
| return nil | ||
| } | ||
| err := HandleAction(f, nil) | ||
| if err != nil { | ||
| t.Errorf("Should not have errored: %v", err) | ||
| } | ||
| if !called { | ||
| t.Errorf("Function was not called") | ||
| } | ||
| } |
+190
-56
@@ -13,2 +13,14 @@ package cli | ||
| var ( | ||
| changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md" | ||
| appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) | ||
| runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL) | ||
| contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you." | ||
| errInvalidActionType = NewExitError("ERROR invalid Action type. "+ | ||
| fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ | ||
| fmt.Sprintf("See %s", appActionDeprecationURL), 2) | ||
| ) | ||
| // App is the main structure of a cli application. It is recommended that | ||
@@ -29,2 +41,4 @@ // an app be created with the cli.NewApp() function | ||
| Version string | ||
| // Description of the program | ||
| Description string | ||
| // List of commands to execute | ||
@@ -40,20 +54,22 @@ Commands []Command | ||
| HideVersion bool | ||
| // Populate on app startup, only gettable throught method Categories() | ||
| // Populate on app startup, only gettable through method Categories() | ||
| categories CommandCategories | ||
| // An action to execute when the bash-completion flag is set | ||
| BashComplete func(context *Context) | ||
| BashComplete BashCompleteFunc | ||
| // An action to execute before any subcommands are run, but after the context is ready | ||
| // If a non-nil error is returned, no subcommands are run | ||
| Before func(context *Context) error | ||
| Before BeforeFunc | ||
| // An action to execute after any subcommands are run, but after the subcommand has finished | ||
| // It is run even if Action() panics | ||
| After func(context *Context) error | ||
| After AfterFunc | ||
| // The action to execute when no subcommands are specified | ||
| Action func(context *Context) | ||
| // Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}` | ||
| // *Note*: support for the deprecated `Action` signature will be removed in a future version | ||
| Action interface{} | ||
| // Execute this function if the proper command cannot be found | ||
| CommandNotFound func(context *Context, command string) | ||
| // Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages. | ||
| // This function is able to replace the original error messages. | ||
| // If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted. | ||
| OnUsageError func(context *Context, err error, isSubcommand bool) error | ||
| CommandNotFound CommandNotFoundFunc | ||
| // Execute this function if an usage error occurs | ||
| OnUsageError OnUsageErrorFunc | ||
| // Compilation date | ||
@@ -71,2 +87,14 @@ Compiled time.Time | ||
| Writer io.Writer | ||
| // ErrWriter writes error output | ||
| ErrWriter io.Writer | ||
| // Other custom info | ||
| Metadata map[string]interface{} | ||
| // Carries a function which returns app specific info. | ||
| ExtraInfo func() map[string]string | ||
| // CustomAppHelpTemplate the text template for app help topic. | ||
| // cli.go uses text/template to render templates. You can | ||
| // render custom help text by setting this variable. | ||
| CustomAppHelpTemplate string | ||
| didSetup bool | ||
| } | ||
@@ -84,3 +112,4 @@ | ||
| // Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action. | ||
| // NewApp creates a new cli Application with some reasonable defaults for Name, | ||
| // Usage, Version and Action. | ||
| func NewApp() *App { | ||
@@ -100,4 +129,12 @@ return &App{ | ||
| // Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination | ||
| func (a *App) Run(arguments []string) (err error) { | ||
| // Setup runs initialization code to ensure all data structures are ready for | ||
| // `Run` or inspection prior to `Run`. It is internally called by `Run`, but | ||
| // will return early if setup has already happened. | ||
| func (a *App) Setup() { | ||
| if a.didSetup { | ||
| return | ||
| } | ||
| a.didSetup = true | ||
| if a.Author != "" || a.Email != "" { | ||
@@ -116,2 +153,13 @@ a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) | ||
| if a.Command(helpCommand.Name) == nil && !a.HideHelp { | ||
| a.Commands = append(a.Commands, helpCommand) | ||
| if (HelpFlag != BoolFlag{}) { | ||
| a.appendFlag(HelpFlag) | ||
| } | ||
| } | ||
| if !a.HideVersion { | ||
| a.appendFlag(VersionFlag) | ||
| } | ||
| a.categories = CommandCategories{} | ||
@@ -123,21 +171,30 @@ for _, command := range a.Commands { | ||
| // append help to commands | ||
| if a.Command(helpCommand.Name) == nil && !a.HideHelp { | ||
| a.Commands = append(a.Commands, helpCommand) | ||
| if (HelpFlag != BoolFlag{}) { | ||
| a.appendFlag(HelpFlag) | ||
| } | ||
| if a.Metadata == nil { | ||
| a.Metadata = make(map[string]interface{}) | ||
| } | ||
| //append version/help flags | ||
| if a.EnableBashCompletion { | ||
| a.appendFlag(BashCompletionFlag) | ||
| if a.Writer == nil { | ||
| a.Writer = os.Stdout | ||
| } | ||
| } | ||
| if !a.HideVersion { | ||
| a.appendFlag(VersionFlag) | ||
| // Run is the entry point to the cli app. Parses the arguments slice and routes | ||
| // to the proper flag/args combination | ||
| func (a *App) Run(arguments []string) (err error) { | ||
| a.Setup() | ||
| // handle the completion flag separately from the flagset since | ||
| // completion could be attempted after a flag, but before its value was put | ||
| // on the command line. this causes the flagset to interpret the completion | ||
| // flag name as the value of the flag before it which is undesirable | ||
| // note that we can only do this because the shell autocomplete function | ||
| // always appends the completion flag at the end of the command | ||
| shellComplete, arguments := checkShellCompleteFlag(a, arguments) | ||
| // parse flags | ||
| set, err := flagSet(a.Name, a.Flags) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // parse flags | ||
| set := flagSet(a.Name, a.Flags) | ||
| set.SetOutput(ioutil.Discard) | ||
@@ -152,2 +209,3 @@ err = set.Parse(arguments[1:]) | ||
| } | ||
| context.shellComplete = shellComplete | ||
@@ -161,8 +219,8 @@ if checkCompletions(context) { | ||
| err := a.OnUsageError(context, err, false) | ||
| HandleExitCoder(err) | ||
| return err | ||
| } else { | ||
| fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") | ||
| ShowAppHelp(context) | ||
| return err | ||
| } | ||
| fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) | ||
| ShowAppHelp(context) | ||
| return err | ||
| } | ||
@@ -194,6 +252,7 @@ | ||
| if a.Before != nil { | ||
| err = a.Before(context) | ||
| if err != nil { | ||
| fmt.Fprintf(a.Writer, "%v\n\n", err) | ||
| beforeErr := a.Before(context) | ||
| if beforeErr != nil { | ||
| ShowAppHelp(context) | ||
| HandleExitCoder(beforeErr) | ||
| err = beforeErr | ||
| return err | ||
@@ -212,16 +271,27 @@ } | ||
| if a.Action == nil { | ||
| a.Action = helpCommand.Action | ||
| } | ||
| // Run default Action | ||
| a.Action(context) | ||
| return nil | ||
| err = HandleAction(a.Action, context) | ||
| HandleExitCoder(err) | ||
| return err | ||
| } | ||
| // Another entry point to the cli app, takes care of passing arguments and error handling | ||
| // RunAndExitOnError calls .Run() and exits non-zero if an error was returned | ||
| // | ||
| // Deprecated: instead you should return an error that fulfills cli.ExitCoder | ||
| // to cli.App.Run. This will cause the application to exit with the given eror | ||
| // code in the cli.ExitCoder | ||
| func (a *App) RunAndExitOnError() { | ||
| if err := a.Run(os.Args); err != nil { | ||
| fmt.Fprintln(os.Stderr, err) | ||
| os.Exit(1) | ||
| fmt.Fprintln(a.errWriter(), err) | ||
| OsExiter(1) | ||
| } | ||
| } | ||
| // Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags | ||
| // RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to | ||
| // generate command-specific flags | ||
| func (a *App) RunAsSubcommand(ctx *Context) (err error) { | ||
@@ -247,9 +317,8 @@ // append help to commands | ||
| // append flags | ||
| if a.EnableBashCompletion { | ||
| a.appendFlag(BashCompletionFlag) | ||
| // parse flags | ||
| set, err := flagSet(a.Name, a.Flags) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // parse flags | ||
| set := flagSet(a.Name, a.Flags) | ||
| set.SetOutput(ioutil.Discard) | ||
@@ -278,8 +347,8 @@ err = set.Parse(ctx.Args().Tail()) | ||
| err = a.OnUsageError(context, err, true) | ||
| HandleExitCoder(err) | ||
| return err | ||
| } else { | ||
| fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") | ||
| ShowSubcommandHelp(context) | ||
| return err | ||
| } | ||
| fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) | ||
| ShowSubcommandHelp(context) | ||
| return err | ||
| } | ||
@@ -301,2 +370,3 @@ | ||
| if afterErr != nil { | ||
| HandleExitCoder(err) | ||
| if err != nil { | ||
@@ -312,4 +382,6 @@ err = NewMultiError(err, afterErr) | ||
| if a.Before != nil { | ||
| err := a.Before(context) | ||
| if err != nil { | ||
| beforeErr := a.Before(context) | ||
| if beforeErr != nil { | ||
| HandleExitCoder(beforeErr) | ||
| err = beforeErr | ||
| return err | ||
@@ -329,8 +401,9 @@ } | ||
| // Run default Action | ||
| a.Action(context) | ||
| err = HandleAction(a.Action, context) | ||
| return nil | ||
| HandleExitCoder(err) | ||
| return err | ||
| } | ||
| // Returns the named command on App. Returns nil if the command does not exist | ||
| // Command returns the named command on App. Returns nil if the command does not exist | ||
| func (a *App) Command(name string) *Command { | ||
@@ -346,3 +419,3 @@ for _, c := range a.Commands { | ||
| // Returnes the array containing all the categories with the commands they contain | ||
| // Categories returns a slice containing all the categories with the commands they contain | ||
| func (a *App) Categories() CommandCategories { | ||
@@ -352,2 +425,37 @@ return a.categories | ||
| // VisibleCategories returns a slice of categories and commands that are | ||
| // Hidden=false | ||
| func (a *App) VisibleCategories() []*CommandCategory { | ||
| ret := []*CommandCategory{} | ||
| for _, category := range a.categories { | ||
| if visible := func() *CommandCategory { | ||
| for _, command := range category.Commands { | ||
| if !command.Hidden { | ||
| return category | ||
| } | ||
| } | ||
| return nil | ||
| }(); visible != nil { | ||
| ret = append(ret, visible) | ||
| } | ||
| } | ||
| return ret | ||
| } | ||
| // VisibleCommands returns a slice of the Commands with Hidden=false | ||
| func (a *App) VisibleCommands() []Command { | ||
| ret := []Command{} | ||
| for _, command := range a.Commands { | ||
| if !command.Hidden { | ||
| ret = append(ret, command) | ||
| } | ||
| } | ||
| return ret | ||
| } | ||
| // VisibleFlags returns a slice of the Flags with Hidden=false | ||
| func (a *App) VisibleFlags() []Flag { | ||
| return visibleFlags(a.Flags) | ||
| } | ||
| func (a *App) hasFlag(flag Flag) bool { | ||
@@ -363,2 +471,12 @@ for _, f := range a.Flags { | ||
| func (a *App) errWriter() io.Writer { | ||
| // When the app ErrWriter is nil use the package level one. | ||
| if a.ErrWriter == nil { | ||
| return ErrWriter | ||
| } | ||
| return a.ErrWriter | ||
| } | ||
| func (a *App) appendFlag(flag Flag) { | ||
@@ -380,6 +498,22 @@ if !a.hasFlag(flag) { | ||
| if a.Email != "" { | ||
| e = "<" + a.Email + "> " | ||
| e = " <" + a.Email + ">" | ||
| } | ||
| return fmt.Sprintf("%v %v", a.Name, e) | ||
| return fmt.Sprintf("%v%v", a.Name, e) | ||
| } | ||
| // HandleAction attempts to figure out which Action signature was used. If | ||
| // it's an ActionFunc or a func with the legacy signature for Action, the func | ||
| // is run! | ||
| func HandleAction(action interface{}, context *Context) (err error) { | ||
| if a, ok := action.(ActionFunc); ok { | ||
| return a(context) | ||
| } else if a, ok := action.(func(*Context) error); ok { | ||
| return a(context) | ||
| } else if a, ok := action.(func(*Context)); ok { // deprecated function signature | ||
| a(context) | ||
| return nil | ||
| } else { | ||
| return errInvalidActionType | ||
| } | ||
| } |
+14
-0
| package cli | ||
| // CommandCategories is a slice of *CommandCategory. | ||
| type CommandCategories []*CommandCategory | ||
| // CommandCategory is a category containing commands. | ||
| type CommandCategory struct { | ||
@@ -22,2 +24,3 @@ Name string | ||
| // AddCommand adds a command to a category. | ||
| func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { | ||
@@ -32,1 +35,12 @@ for _, commandCategory := range c { | ||
| } | ||
| // VisibleCommands returns a slice of the Commands with Hidden=false | ||
| func (c *CommandCategory) VisibleCommands() []Command { | ||
| ret := []Command{} | ||
| for _, command := range c.Commands { | ||
| if !command.Hidden { | ||
| ret = append(ret, command) | ||
| } | ||
| } | ||
| return ret | ||
| } |
+3
-21
@@ -13,4 +13,5 @@ // Package cli provides a minimal framework for creating and organizing command line | ||
| // app.Usage = "say a greeting" | ||
| // app.Action = func(c *cli.Context) { | ||
| // app.Action = func(c *cli.Context) error { | ||
| // println("Greetings") | ||
| // return nil | ||
| // } | ||
@@ -22,21 +23,2 @@ // | ||
| import ( | ||
| "strings" | ||
| ) | ||
| type MultiError struct { | ||
| Errors []error | ||
| } | ||
| func NewMultiError(err ...error) MultiError { | ||
| return MultiError{Errors: err} | ||
| } | ||
| func (m MultiError) Error() string { | ||
| errs := make([]string, len(m.Errors)) | ||
| for i, err := range m.Errors { | ||
| errs[i] = err.Error() | ||
| } | ||
| return strings.Join(errs, "\n") | ||
| } | ||
| //go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go |
+160
-17
@@ -16,8 +16,14 @@ package cli | ||
| skipFlagParsing bool | ||
| skipArgReorder bool | ||
| expectedErr error | ||
| }{ | ||
| {[]string{"blah", "blah", "-break"}, false, errors.New("flag provided but not defined: -break")}, // Test normal "not ignoring flags" flow | ||
| {[]string{"blah", "blah"}, true, nil}, // Test SkipFlagParsing without any args that look like flags | ||
| {[]string{"blah", "-break"}, true, nil}, // Test SkipFlagParsing with random flag arg | ||
| {[]string{"blah", "-help"}, true, nil}, // Test SkipFlagParsing with "special" help flag arg | ||
| // Test normal "not ignoring flags" flow | ||
| {[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break")}, | ||
| // Test no arg reorder | ||
| {[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil}, | ||
| {[]string{"test-cmd", "blah", "blah"}, true, false, nil}, // Test SkipFlagParsing without any args that look like flags | ||
| {[]string{"test-cmd", "blah", "-break"}, true, false, nil}, // Test SkipFlagParsing with random flag arg | ||
| {[]string{"test-cmd", "blah", "-help"}, true, false, nil}, // Test SkipFlagParsing with "special" help flag arg | ||
| } | ||
@@ -34,11 +40,11 @@ | ||
| command := Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(_ *Context) {}, | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(_ *Context) error { return nil }, | ||
| SkipFlagParsing: c.skipFlagParsing, | ||
| SkipArgReorder: c.skipArgReorder, | ||
| } | ||
| command.SkipFlagParsing = c.skipFlagParsing | ||
| err := command.Run(context) | ||
@@ -54,6 +60,10 @@ | ||
| app.Commands = []Command{ | ||
| Command{ | ||
| Name: "bar", | ||
| Before: func(c *Context) error { return fmt.Errorf("before error") }, | ||
| After: func(c *Context) error { return fmt.Errorf("after error") }, | ||
| { | ||
| Name: "bar", | ||
| Before: func(c *Context) error { | ||
| return fmt.Errorf("before error") | ||
| }, | ||
| After: func(c *Context) error { | ||
| return fmt.Errorf("after error") | ||
| }, | ||
| }, | ||
@@ -75,6 +85,78 @@ } | ||
| func TestCommand_Run_BeforeSavesMetadata(t *testing.T) { | ||
| var receivedMsgFromAction string | ||
| var receivedMsgFromAfter string | ||
| app := NewApp() | ||
| app.Commands = []Command{ | ||
| { | ||
| Name: "bar", | ||
| Before: func(c *Context) error { | ||
| c.App.Metadata["msg"] = "hello world" | ||
| return nil | ||
| }, | ||
| Action: func(c *Context) error { | ||
| msg, ok := c.App.Metadata["msg"] | ||
| if !ok { | ||
| return errors.New("msg not found") | ||
| } | ||
| receivedMsgFromAction = msg.(string) | ||
| return nil | ||
| }, | ||
| After: func(c *Context) error { | ||
| msg, ok := c.App.Metadata["msg"] | ||
| if !ok { | ||
| return errors.New("msg not found") | ||
| } | ||
| receivedMsgFromAfter = msg.(string) | ||
| return nil | ||
| }, | ||
| }, | ||
| } | ||
| err := app.Run([]string{"foo", "bar"}) | ||
| if err != nil { | ||
| t.Fatalf("expected no error from Run, got %s", err) | ||
| } | ||
| expectedMsg := "hello world" | ||
| if receivedMsgFromAction != expectedMsg { | ||
| t.Fatalf("expected msg from Action to match. Given: %q\nExpected: %q", | ||
| receivedMsgFromAction, expectedMsg) | ||
| } | ||
| if receivedMsgFromAfter != expectedMsg { | ||
| t.Fatalf("expected msg from After to match. Given: %q\nExpected: %q", | ||
| receivedMsgFromAction, expectedMsg) | ||
| } | ||
| } | ||
| func TestCommand_OnUsageError_hasCommandContext(t *testing.T) { | ||
| app := NewApp() | ||
| app.Commands = []Command{ | ||
| { | ||
| Name: "bar", | ||
| Flags: []Flag{ | ||
| IntFlag{Name: "flag"}, | ||
| }, | ||
| OnUsageError: func(c *Context, err error, _ bool) error { | ||
| return fmt.Errorf("intercepted in %s: %s", c.Command.Name, err.Error()) | ||
| }, | ||
| }, | ||
| } | ||
| err := app.Run([]string{"foo", "bar", "--flag=wrong"}) | ||
| if err == nil { | ||
| t.Fatalf("expected to receive error from Run, got none") | ||
| } | ||
| if !strings.HasPrefix(err.Error(), "intercepted in bar") { | ||
| t.Errorf("Expect an intercepted error, but got \"%v\"", err) | ||
| } | ||
| } | ||
| func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) { | ||
| app := NewApp() | ||
| app.Commands = []Command{ | ||
| Command{ | ||
| { | ||
| Name: "bar", | ||
@@ -84,3 +166,3 @@ Flags: []Flag{ | ||
| }, | ||
| OnUsageError: func(c *Context, err error) error { | ||
| OnUsageError: func(c *Context, err error, _ bool) error { | ||
| if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { | ||
@@ -103,1 +185,62 @@ t.Errorf("Expect an invalid value error, but got \"%v\"", err) | ||
| } | ||
| func TestCommand_OnUsageError_WithSubcommand(t *testing.T) { | ||
| app := NewApp() | ||
| app.Commands = []Command{ | ||
| { | ||
| Name: "bar", | ||
| Subcommands: []Command{ | ||
| { | ||
| Name: "baz", | ||
| }, | ||
| }, | ||
| Flags: []Flag{ | ||
| IntFlag{Name: "flag"}, | ||
| }, | ||
| OnUsageError: func(c *Context, err error, _ bool) error { | ||
| if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { | ||
| t.Errorf("Expect an invalid value error, but got \"%v\"", err) | ||
| } | ||
| return errors.New("intercepted: " + err.Error()) | ||
| }, | ||
| }, | ||
| } | ||
| err := app.Run([]string{"foo", "bar", "--flag=wrong"}) | ||
| if err == nil { | ||
| t.Fatalf("expected to receive error from Run, got none") | ||
| } | ||
| if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { | ||
| t.Errorf("Expect an intercepted error, but got \"%v\"", err) | ||
| } | ||
| } | ||
| func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) { | ||
| app := NewApp() | ||
| app.ErrWriter = ioutil.Discard | ||
| app.Commands = []Command{ | ||
| { | ||
| Name: "bar", | ||
| Usage: "this is for testing", | ||
| Subcommands: []Command{ | ||
| { | ||
| Name: "baz", | ||
| Usage: "this is for testing", | ||
| Action: func(c *Context) error { | ||
| if c.App.ErrWriter != ioutil.Discard { | ||
| return fmt.Errorf("ErrWriter not passed") | ||
| } | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| err := app.Run([]string{"foo", "bar", "baz"}) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| } |
+88
-46
@@ -29,15 +29,16 @@ package cli | ||
| // The function to call when checking for bash command completions | ||
| BashComplete func(context *Context) | ||
| BashComplete BashCompleteFunc | ||
| // An action to execute before any sub-subcommands are run, but after the context is ready | ||
| // If a non-nil error is returned, no sub-subcommands are run | ||
| Before func(context *Context) error | ||
| // An action to execute after any subcommands are run, but before the subcommand has finished | ||
| Before BeforeFunc | ||
| // An action to execute after any subcommands are run, but after the subcommand has finished | ||
| // It is run even if Action() panics | ||
| After func(context *Context) error | ||
| After AfterFunc | ||
| // The function to call when this command is invoked | ||
| Action func(context *Context) | ||
| // Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages. | ||
| // This function is able to replace the original error messages. | ||
| // If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted. | ||
| OnUsageError func(context *Context, err error) error | ||
| Action interface{} | ||
| // TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind | ||
| // of deprecation period has passed, maybe? | ||
| // Execute this function if a usage error occurs. | ||
| OnUsageError OnUsageErrorFunc | ||
| // List of child commands | ||
@@ -49,4 +50,11 @@ Subcommands Commands | ||
| SkipFlagParsing bool | ||
| // Skip argument reordering which attempts to move flags before arguments, | ||
| // but only works if all flags appear after all arguments. This behavior was | ||
| // removed n version 2 since it only works under specific conditions so we | ||
| // backport here by exposing it as an option for compatibility. | ||
| SkipArgReorder bool | ||
| // Boolean to hide built-in help command | ||
| HideHelp bool | ||
| // Boolean to hide this command from help or completion | ||
| Hidden bool | ||
@@ -56,5 +64,24 @@ // Full name of command for help, defaults to full command name, including parent commands. | ||
| commandNamePath []string | ||
| // CustomHelpTemplate the text template for the command help topic. | ||
| // cli.go uses text/template to render templates. You can | ||
| // render custom help text by setting this variable. | ||
| CustomHelpTemplate string | ||
| } | ||
| // Returns the full name of the command. | ||
| type CommandsByName []Command | ||
| func (c CommandsByName) Len() int { | ||
| return len(c) | ||
| } | ||
| func (c CommandsByName) Less(i, j int) bool { | ||
| return c[i].Name < c[j].Name | ||
| } | ||
| func (c CommandsByName) Swap(i, j int) { | ||
| c[i], c[j] = c[j], c[i] | ||
| } | ||
| // FullName returns the full name of the command. | ||
| // For subcommands this ensures that parent commands are part of the command path | ||
@@ -68,5 +95,6 @@ func (c Command) FullName() string { | ||
| // Commands is a slice of Command | ||
| type Commands []Command | ||
| // Invokes the command given the context, parses ctx.Args() to generate command-specific flags | ||
| // Run invokes the command given the context, parses ctx.Args() to generate command-specific flags | ||
| func (c Command) Run(ctx *Context) (err error) { | ||
@@ -85,10 +113,11 @@ if len(c.Subcommands) > 0 { | ||
| if ctx.App.EnableBashCompletion { | ||
| c.Flags = append(c.Flags, BashCompletionFlag) | ||
| set, err := flagSet(c.Name, c.Flags) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| set := flagSet(c.Name, c.Flags) | ||
| set.SetOutput(ioutil.Discard) | ||
| if !c.SkipFlagParsing { | ||
| if c.SkipFlagParsing { | ||
| err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...)) | ||
| } else if !c.SkipArgReorder { | ||
| firstFlagIndex := -1 | ||
@@ -126,19 +155,5 @@ terminatorIndex := -1 | ||
| } else { | ||
| if c.SkipFlagParsing { | ||
| err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...)) | ||
| } | ||
| err = set.Parse(ctx.Args().Tail()) | ||
| } | ||
| if err != nil { | ||
| if c.OnUsageError != nil { | ||
| err := c.OnUsageError(ctx, err) | ||
| return err | ||
| } else { | ||
| fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.") | ||
| fmt.Fprintln(ctx.App.Writer) | ||
| ShowCommandHelp(ctx, c.Name) | ||
| return err | ||
| } | ||
| } | ||
| nerr := normalizeFlags(c.Flags, set) | ||
@@ -151,4 +166,5 @@ if nerr != nil { | ||
| } | ||
| context := NewContext(ctx.App, set, ctx) | ||
| context.Command = c | ||
| if checkCommandCompletions(context, c.Name) { | ||
@@ -158,2 +174,14 @@ return nil | ||
| if err != nil { | ||
| if c.OnUsageError != nil { | ||
| err := c.OnUsageError(context, err, false) | ||
| HandleExitCoder(err) | ||
| return err | ||
| } | ||
| fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) | ||
| fmt.Fprintln(context.App.Writer) | ||
| ShowCommandHelp(context, c.Name) | ||
| return err | ||
| } | ||
| if checkCommandHelp(context, c.Name) { | ||
@@ -167,2 +195,3 @@ return nil | ||
| if afterErr != nil { | ||
| HandleExitCoder(err) | ||
| if err != nil { | ||
@@ -178,7 +207,6 @@ err = NewMultiError(err, afterErr) | ||
| if c.Before != nil { | ||
| err := c.Before(context) | ||
| err = c.Before(context) | ||
| if err != nil { | ||
| fmt.Fprintln(ctx.App.Writer, err) | ||
| fmt.Fprintln(ctx.App.Writer) | ||
| ShowCommandHelp(ctx, c.Name) | ||
| ShowCommandHelp(context, c.Name) | ||
| HandleExitCoder(err) | ||
| return err | ||
@@ -188,7 +216,15 @@ } | ||
| context.Command = c | ||
| c.Action(context) | ||
| return nil | ||
| if c.Action == nil { | ||
| c.Action = helpSubcommand.Action | ||
| } | ||
| err = HandleAction(c.Action, context) | ||
| if err != nil { | ||
| HandleExitCoder(err) | ||
| } | ||
| return err | ||
| } | ||
| // Names returns the names including short names and aliases. | ||
| func (c Command) Names() []string { | ||
@@ -204,3 +240,3 @@ names := []string{c.Name} | ||
| // Returns true if Command.Name or Command.ShortName matches given name | ||
| // HasName returns true if Command.Name or Command.ShortName matches given name | ||
| func (c Command) HasName(name string) bool { | ||
@@ -217,3 +253,3 @@ for _, n := range c.Names() { | ||
| app := NewApp() | ||
| app.Metadata = ctx.App.Metadata | ||
| // set the name and usage | ||
@@ -227,10 +263,9 @@ app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) | ||
| if c.Description != "" { | ||
| app.Usage = c.Description | ||
| } else { | ||
| app.Usage = c.Usage | ||
| } | ||
| app.Usage = c.Usage | ||
| app.Description = c.Description | ||
| app.ArgsUsage = c.ArgsUsage | ||
| // set CommandNotFound | ||
| app.CommandNotFound = ctx.App.CommandNotFound | ||
| app.CustomAppHelpTemplate = c.CustomHelpTemplate | ||
@@ -248,2 +283,3 @@ // set the flags and commands | ||
| app.Writer = ctx.App.Writer | ||
| app.ErrWriter = ctx.App.ErrWriter | ||
@@ -271,2 +307,3 @@ app.categories = CommandCategories{} | ||
| } | ||
| app.OnUsageError = c.OnUsageError | ||
@@ -279,1 +316,6 @@ for index, cc := range app.Commands { | ||
| } | ||
| // VisibleFlags returns a slice of the Flags with Hidden=false | ||
| func (c Command) VisibleFlags() []Flag { | ||
| return visibleFlags(c.Flags) | ||
| } |
+282
-0
@@ -5,2 +5,3 @@ package cli | ||
| "flag" | ||
| "os" | ||
| "testing" | ||
@@ -13,4 +14,12 @@ "time" | ||
| set.Int("myflag", 12, "doc") | ||
| set.Int64("myflagInt64", int64(12), "doc") | ||
| set.Uint("myflagUint", uint(93), "doc") | ||
| set.Uint64("myflagUint64", uint64(93), "doc") | ||
| set.Float64("myflag64", float64(17), "doc") | ||
| globalSet := flag.NewFlagSet("test", 0) | ||
| globalSet.Int("myflag", 42, "doc") | ||
| globalSet.Int64("myflagInt64", int64(42), "doc") | ||
| globalSet.Uint("myflagUint", uint(33), "doc") | ||
| globalSet.Uint64("myflagUint64", uint64(33), "doc") | ||
| globalSet.Float64("myflag64", float64(47), "doc") | ||
| globalCtx := NewContext(nil, globalSet, nil) | ||
@@ -21,3 +30,11 @@ command := Command{Name: "mycommand"} | ||
| expect(t, c.Int("myflag"), 12) | ||
| expect(t, c.Int64("myflagInt64"), int64(12)) | ||
| expect(t, c.Uint("myflagUint"), uint(93)) | ||
| expect(t, c.Uint64("myflagUint64"), uint64(93)) | ||
| expect(t, c.Float64("myflag64"), float64(17)) | ||
| expect(t, c.GlobalInt("myflag"), 42) | ||
| expect(t, c.GlobalInt64("myflagInt64"), int64(42)) | ||
| expect(t, c.GlobalUint("myflagUint"), uint(33)) | ||
| expect(t, c.GlobalUint64("myflagUint64"), uint64(33)) | ||
| expect(t, c.GlobalFloat64("myflag64"), float64(47)) | ||
| expect(t, c.Command.Name, "mycommand") | ||
@@ -33,2 +50,54 @@ } | ||
| func TestContext_Int64(t *testing.T) { | ||
| set := flag.NewFlagSet("test", 0) | ||
| set.Int64("myflagInt64", 12, "doc") | ||
| c := NewContext(nil, set, nil) | ||
| expect(t, c.Int64("myflagInt64"), int64(12)) | ||
| } | ||
| func TestContext_Uint(t *testing.T) { | ||
| set := flag.NewFlagSet("test", 0) | ||
| set.Uint("myflagUint", uint(13), "doc") | ||
| c := NewContext(nil, set, nil) | ||
| expect(t, c.Uint("myflagUint"), uint(13)) | ||
| } | ||
| func TestContext_Uint64(t *testing.T) { | ||
| set := flag.NewFlagSet("test", 0) | ||
| set.Uint64("myflagUint64", uint64(9), "doc") | ||
| c := NewContext(nil, set, nil) | ||
| expect(t, c.Uint64("myflagUint64"), uint64(9)) | ||
| } | ||
| func TestContext_GlobalInt(t *testing.T) { | ||
| set := flag.NewFlagSet("test", 0) | ||
| set.Int("myflag", 12, "doc") | ||
| c := NewContext(nil, set, nil) | ||
| expect(t, c.GlobalInt("myflag"), 12) | ||
| expect(t, c.GlobalInt("nope"), 0) | ||
| } | ||
| func TestContext_GlobalInt64(t *testing.T) { | ||
| set := flag.NewFlagSet("test", 0) | ||
| set.Int64("myflagInt64", 12, "doc") | ||
| c := NewContext(nil, set, nil) | ||
| expect(t, c.GlobalInt64("myflagInt64"), int64(12)) | ||
| expect(t, c.GlobalInt64("nope"), int64(0)) | ||
| } | ||
| func TestContext_Float64(t *testing.T) { | ||
| set := flag.NewFlagSet("test", 0) | ||
| set.Float64("myflag", float64(17), "doc") | ||
| c := NewContext(nil, set, nil) | ||
| expect(t, c.Float64("myflag"), float64(17)) | ||
| } | ||
| func TestContext_GlobalFloat64(t *testing.T) { | ||
| set := flag.NewFlagSet("test", 0) | ||
| set.Float64("myflag", float64(17), "doc") | ||
| c := NewContext(nil, set, nil) | ||
| expect(t, c.GlobalFloat64("myflag"), float64(17)) | ||
| expect(t, c.GlobalFloat64("nope"), float64(0)) | ||
| } | ||
| func TestContext_Duration(t *testing.T) { | ||
@@ -62,2 +131,26 @@ set := flag.NewFlagSet("test", 0) | ||
| func TestContext_GlobalBool(t *testing.T) { | ||
| set := flag.NewFlagSet("test", 0) | ||
| globalSet := flag.NewFlagSet("test-global", 0) | ||
| globalSet.Bool("myflag", false, "doc") | ||
| globalCtx := NewContext(nil, globalSet, nil) | ||
| c := NewContext(nil, set, globalCtx) | ||
| expect(t, c.GlobalBool("myflag"), false) | ||
| expect(t, c.GlobalBool("nope"), false) | ||
| } | ||
| func TestContext_GlobalBoolT(t *testing.T) { | ||
| set := flag.NewFlagSet("test", 0) | ||
| globalSet := flag.NewFlagSet("test-global", 0) | ||
| globalSet.Bool("myflag", true, "doc") | ||
| globalCtx := NewContext(nil, globalSet, nil) | ||
| c := NewContext(nil, set, globalCtx) | ||
| expect(t, c.GlobalBoolT("myflag"), true) | ||
| expect(t, c.GlobalBoolT("nope"), false) | ||
| } | ||
| func TestContext_Args(t *testing.T) { | ||
@@ -96,2 +189,48 @@ set := flag.NewFlagSet("test", 0) | ||
| // XXX Corresponds to hack in context.IsSet for flags with EnvVar field | ||
| // Should be moved to `flag_test` in v2 | ||
| func TestContext_IsSet_fromEnv(t *testing.T) { | ||
| var ( | ||
| timeoutIsSet, tIsSet bool | ||
| noEnvVarIsSet, nIsSet bool | ||
| passwordIsSet, pIsSet bool | ||
| unparsableIsSet, uIsSet bool | ||
| ) | ||
| clearenv() | ||
| os.Setenv("APP_TIMEOUT_SECONDS", "15.5") | ||
| os.Setenv("APP_PASSWORD", "") | ||
| a := App{ | ||
| Flags: []Flag{ | ||
| Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, | ||
| StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"}, | ||
| Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"}, | ||
| Float64Flag{Name: "no-env-var, n"}, | ||
| }, | ||
| Action: func(ctx *Context) error { | ||
| timeoutIsSet = ctx.IsSet("timeout") | ||
| tIsSet = ctx.IsSet("t") | ||
| passwordIsSet = ctx.IsSet("password") | ||
| pIsSet = ctx.IsSet("p") | ||
| unparsableIsSet = ctx.IsSet("unparsable") | ||
| uIsSet = ctx.IsSet("u") | ||
| noEnvVarIsSet = ctx.IsSet("no-env-var") | ||
| nIsSet = ctx.IsSet("n") | ||
| return nil | ||
| }, | ||
| } | ||
| a.Run([]string{"run"}) | ||
| expect(t, timeoutIsSet, true) | ||
| expect(t, tIsSet, true) | ||
| expect(t, passwordIsSet, true) | ||
| expect(t, pIsSet, true) | ||
| expect(t, noEnvVarIsSet, false) | ||
| expect(t, nIsSet, false) | ||
| os.Setenv("APP_UNPARSABLE", "foobar") | ||
| a.Run([]string{"run"}) | ||
| expect(t, unparsableIsSet, false) | ||
| expect(t, uIsSet, false) | ||
| } | ||
| func TestContext_GlobalIsSet(t *testing.T) { | ||
@@ -116,2 +255,57 @@ set := flag.NewFlagSet("test", 0) | ||
| // XXX Corresponds to hack in context.IsSet for flags with EnvVar field | ||
| // Should be moved to `flag_test` in v2 | ||
| func TestContext_GlobalIsSet_fromEnv(t *testing.T) { | ||
| var ( | ||
| timeoutIsSet, tIsSet bool | ||
| noEnvVarIsSet, nIsSet bool | ||
| passwordIsSet, pIsSet bool | ||
| unparsableIsSet, uIsSet bool | ||
| ) | ||
| clearenv() | ||
| os.Setenv("APP_TIMEOUT_SECONDS", "15.5") | ||
| os.Setenv("APP_PASSWORD", "") | ||
| a := App{ | ||
| Flags: []Flag{ | ||
| Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, | ||
| StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"}, | ||
| Float64Flag{Name: "no-env-var, n"}, | ||
| Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"}, | ||
| }, | ||
| Commands: []Command{ | ||
| { | ||
| Name: "hello", | ||
| Action: func(ctx *Context) error { | ||
| timeoutIsSet = ctx.GlobalIsSet("timeout") | ||
| tIsSet = ctx.GlobalIsSet("t") | ||
| passwordIsSet = ctx.GlobalIsSet("password") | ||
| pIsSet = ctx.GlobalIsSet("p") | ||
| unparsableIsSet = ctx.GlobalIsSet("unparsable") | ||
| uIsSet = ctx.GlobalIsSet("u") | ||
| noEnvVarIsSet = ctx.GlobalIsSet("no-env-var") | ||
| nIsSet = ctx.GlobalIsSet("n") | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| if err := a.Run([]string{"run", "hello"}); err != nil { | ||
| t.Logf("error running Run(): %+v", err) | ||
| } | ||
| expect(t, timeoutIsSet, true) | ||
| expect(t, tIsSet, true) | ||
| expect(t, passwordIsSet, true) | ||
| expect(t, pIsSet, true) | ||
| expect(t, noEnvVarIsSet, false) | ||
| expect(t, nIsSet, false) | ||
| os.Setenv("APP_UNPARSABLE", "foobar") | ||
| if err := a.Run([]string{"run"}); err != nil { | ||
| t.Logf("error running Run(): %+v", err) | ||
| } | ||
| expect(t, unparsableIsSet, false) | ||
| expect(t, uIsSet, false) | ||
| } | ||
| func TestContext_NumFlags(t *testing.T) { | ||
@@ -129,1 +323,89 @@ set := flag.NewFlagSet("test", 0) | ||
| } | ||
| func TestContext_GlobalFlag(t *testing.T) { | ||
| var globalFlag string | ||
| var globalFlagSet bool | ||
| app := NewApp() | ||
| app.Flags = []Flag{ | ||
| StringFlag{Name: "global, g", Usage: "global"}, | ||
| } | ||
| app.Action = func(c *Context) error { | ||
| globalFlag = c.GlobalString("global") | ||
| globalFlagSet = c.GlobalIsSet("global") | ||
| return nil | ||
| } | ||
| app.Run([]string{"command", "-g", "foo"}) | ||
| expect(t, globalFlag, "foo") | ||
| expect(t, globalFlagSet, true) | ||
| } | ||
| func TestContext_GlobalFlagsInSubcommands(t *testing.T) { | ||
| subcommandRun := false | ||
| parentFlag := false | ||
| app := NewApp() | ||
| app.Flags = []Flag{ | ||
| BoolFlag{Name: "debug, d", Usage: "Enable debugging"}, | ||
| } | ||
| app.Commands = []Command{ | ||
| { | ||
| Name: "foo", | ||
| Flags: []Flag{ | ||
| BoolFlag{Name: "parent, p", Usage: "Parent flag"}, | ||
| }, | ||
| Subcommands: []Command{ | ||
| { | ||
| Name: "bar", | ||
| Action: func(c *Context) error { | ||
| if c.GlobalBool("debug") { | ||
| subcommandRun = true | ||
| } | ||
| if c.GlobalBool("parent") { | ||
| parentFlag = true | ||
| } | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| app.Run([]string{"command", "-d", "foo", "-p", "bar"}) | ||
| expect(t, subcommandRun, true) | ||
| expect(t, parentFlag, true) | ||
| } | ||
| func TestContext_Set(t *testing.T) { | ||
| set := flag.NewFlagSet("test", 0) | ||
| set.Int("int", 5, "an int") | ||
| c := NewContext(nil, set, nil) | ||
| expect(t, c.IsSet("int"), false) | ||
| c.Set("int", "1") | ||
| expect(t, c.Int("int"), 1) | ||
| expect(t, c.IsSet("int"), true) | ||
| } | ||
| func TestContext_GlobalSet(t *testing.T) { | ||
| gSet := flag.NewFlagSet("test", 0) | ||
| gSet.Int("int", 5, "an int") | ||
| set := flag.NewFlagSet("sub", 0) | ||
| set.Int("int", 3, "an int") | ||
| pc := NewContext(nil, gSet, nil) | ||
| c := NewContext(nil, set, pc) | ||
| c.Set("int", "1") | ||
| expect(t, c.Int("int"), 1) | ||
| expect(t, c.GlobalInt("int"), 5) | ||
| expect(t, c.GlobalIsSet("int"), false) | ||
| c.GlobalSet("int", "1") | ||
| expect(t, c.Int("int"), 1) | ||
| expect(t, c.GlobalInt("int"), 1) | ||
| expect(t, c.GlobalIsSet("int"), true) | ||
| } |
+116
-231
@@ -6,5 +6,5 @@ package cli | ||
| "flag" | ||
| "strconv" | ||
| "reflect" | ||
| "strings" | ||
| "time" | ||
| "syscall" | ||
| ) | ||
@@ -17,150 +17,117 @@ | ||
| type Context struct { | ||
| App *App | ||
| Command Command | ||
| flagSet *flag.FlagSet | ||
| setFlags map[string]bool | ||
| globalSetFlags map[string]bool | ||
| parentContext *Context | ||
| App *App | ||
| Command Command | ||
| shellComplete bool | ||
| flagSet *flag.FlagSet | ||
| setFlags map[string]bool | ||
| parentContext *Context | ||
| } | ||
| // Creates a new context. For use in when invoking an App or Command action. | ||
| // NewContext creates a new context. For use in when invoking an App or Command action. | ||
| func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { | ||
| return &Context{App: app, flagSet: set, parentContext: parentCtx} | ||
| } | ||
| c := &Context{App: app, flagSet: set, parentContext: parentCtx} | ||
| // Looks up the value of a local int flag, returns 0 if no int flag exists | ||
| func (c *Context) Int(name string) int { | ||
| return lookupInt(name, c.flagSet) | ||
| } | ||
| if parentCtx != nil { | ||
| c.shellComplete = parentCtx.shellComplete | ||
| } | ||
| // Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists | ||
| func (c *Context) Duration(name string) time.Duration { | ||
| return lookupDuration(name, c.flagSet) | ||
| return c | ||
| } | ||
| // Looks up the value of a local float64 flag, returns 0 if no float64 flag exists | ||
| func (c *Context) Float64(name string) float64 { | ||
| return lookupFloat64(name, c.flagSet) | ||
| // NumFlags returns the number of flags set | ||
| func (c *Context) NumFlags() int { | ||
| return c.flagSet.NFlag() | ||
| } | ||
| // Looks up the value of a local bool flag, returns false if no bool flag exists | ||
| func (c *Context) Bool(name string) bool { | ||
| return lookupBool(name, c.flagSet) | ||
| // Set sets a context flag to a value. | ||
| func (c *Context) Set(name, value string) error { | ||
| c.setFlags = nil | ||
| return c.flagSet.Set(name, value) | ||
| } | ||
| // Looks up the value of a local boolT flag, returns false if no bool flag exists | ||
| func (c *Context) BoolT(name string) bool { | ||
| return lookupBoolT(name, c.flagSet) | ||
| // GlobalSet sets a context flag to a value on the global flagset | ||
| func (c *Context) GlobalSet(name, value string) error { | ||
| globalContext(c).setFlags = nil | ||
| return globalContext(c).flagSet.Set(name, value) | ||
| } | ||
| // Looks up the value of a local string flag, returns "" if no string flag exists | ||
| func (c *Context) String(name string) string { | ||
| return lookupString(name, c.flagSet) | ||
| } | ||
| // IsSet determines if the flag was actually set | ||
| func (c *Context) IsSet(name string) bool { | ||
| if c.setFlags == nil { | ||
| c.setFlags = make(map[string]bool) | ||
| // Looks up the value of a local string slice flag, returns nil if no string slice flag exists | ||
| func (c *Context) StringSlice(name string) []string { | ||
| return lookupStringSlice(name, c.flagSet) | ||
| } | ||
| c.flagSet.Visit(func(f *flag.Flag) { | ||
| c.setFlags[f.Name] = true | ||
| }) | ||
| // Looks up the value of a local int slice flag, returns nil if no int slice flag exists | ||
| func (c *Context) IntSlice(name string) []int { | ||
| return lookupIntSlice(name, c.flagSet) | ||
| } | ||
| c.flagSet.VisitAll(func(f *flag.Flag) { | ||
| if _, ok := c.setFlags[f.Name]; ok { | ||
| return | ||
| } | ||
| c.setFlags[f.Name] = false | ||
| }) | ||
| // Looks up the value of a local generic flag, returns nil if no generic flag exists | ||
| func (c *Context) Generic(name string) interface{} { | ||
| return lookupGeneric(name, c.flagSet) | ||
| } | ||
| // XXX hack to support IsSet for flags with EnvVar | ||
| // | ||
| // There isn't an easy way to do this with the current implementation since | ||
| // whether a flag was set via an environment variable is very difficult to | ||
| // determine here. Instead, we intend to introduce a backwards incompatible | ||
| // change in version 2 to add `IsSet` to the Flag interface to push the | ||
| // responsibility closer to where the information required to determine | ||
| // whether a flag is set by non-standard means such as environment | ||
| // variables is avaliable. | ||
| // | ||
| // See https://github.com/urfave/cli/issues/294 for additional discussion | ||
| flags := c.Command.Flags | ||
| if c.Command.Name == "" { // cannot == Command{} since it contains slice types | ||
| if c.App != nil { | ||
| flags = c.App.Flags | ||
| } | ||
| } | ||
| for _, f := range flags { | ||
| eachName(f.GetName(), func(name string) { | ||
| if isSet, ok := c.setFlags[name]; isSet || !ok { | ||
| return | ||
| } | ||
| // Looks up the value of a global int flag, returns 0 if no int flag exists | ||
| func (c *Context) GlobalInt(name string) int { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupInt(name, fs) | ||
| } | ||
| return 0 | ||
| } | ||
| val := reflect.ValueOf(f) | ||
| if val.Kind() == reflect.Ptr { | ||
| val = val.Elem() | ||
| } | ||
| // Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists | ||
| func (c *Context) GlobalDuration(name string) time.Duration { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupDuration(name, fs) | ||
| } | ||
| return 0 | ||
| } | ||
| envVarValue := val.FieldByName("EnvVar") | ||
| if !envVarValue.IsValid() { | ||
| return | ||
| } | ||
| // Looks up the value of a global bool flag, returns false if no bool flag exists | ||
| func (c *Context) GlobalBool(name string) bool { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupBool(name, fs) | ||
| eachName(envVarValue.String(), func(envVar string) { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if _, ok := syscall.Getenv(envVar); ok { | ||
| c.setFlags[name] = true | ||
| return | ||
| } | ||
| }) | ||
| }) | ||
| } | ||
| } | ||
| return false | ||
| } | ||
| // Looks up the value of a global string flag, returns "" if no string flag exists | ||
| func (c *Context) GlobalString(name string) string { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupString(name, fs) | ||
| } | ||
| return "" | ||
| return c.setFlags[name] | ||
| } | ||
| // Looks up the value of a global string slice flag, returns nil if no string slice flag exists | ||
| func (c *Context) GlobalStringSlice(name string) []string { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupStringSlice(name, fs) | ||
| // GlobalIsSet determines if the global flag was actually set | ||
| func (c *Context) GlobalIsSet(name string) bool { | ||
| ctx := c | ||
| if ctx.parentContext != nil { | ||
| ctx = ctx.parentContext | ||
| } | ||
| return nil | ||
| } | ||
| // Looks up the value of a global int slice flag, returns nil if no int slice flag exists | ||
| func (c *Context) GlobalIntSlice(name string) []int { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupIntSlice(name, fs) | ||
| } | ||
| return nil | ||
| } | ||
| // Looks up the value of a global generic flag, returns nil if no generic flag exists | ||
| func (c *Context) GlobalGeneric(name string) interface{} { | ||
| if fs := lookupGlobalFlagSet(name, c); fs != nil { | ||
| return lookupGeneric(name, fs) | ||
| } | ||
| return nil | ||
| } | ||
| // Returns the number of flags set | ||
| func (c *Context) NumFlags() int { | ||
| return c.flagSet.NFlag() | ||
| } | ||
| // Determines if the flag was actually set | ||
| func (c *Context) IsSet(name string) bool { | ||
| if c.setFlags == nil { | ||
| c.setFlags = make(map[string]bool) | ||
| c.flagSet.Visit(func(f *flag.Flag) { | ||
| c.setFlags[f.Name] = true | ||
| }) | ||
| } | ||
| return c.setFlags[name] == true | ||
| } | ||
| // Determines if the global flag was actually set | ||
| func (c *Context) GlobalIsSet(name string) bool { | ||
| if c.globalSetFlags == nil { | ||
| c.globalSetFlags = make(map[string]bool) | ||
| ctx := c | ||
| if ctx.parentContext != nil { | ||
| ctx = ctx.parentContext | ||
| for ; ctx != nil; ctx = ctx.parentContext { | ||
| if ctx.IsSet(name) { | ||
| return true | ||
| } | ||
| for ; ctx != nil && c.globalSetFlags[name] == false; ctx = ctx.parentContext { | ||
| ctx.flagSet.Visit(func(f *flag.Flag) { | ||
| c.globalSetFlags[f.Name] = true | ||
| }) | ||
| } | ||
| } | ||
| return c.globalSetFlags[name] | ||
| return false | ||
| } | ||
| // Returns a slice of flag names used in this context. | ||
| // FlagNames returns a slice of flag names used in this context. | ||
| func (c *Context) FlagNames() (names []string) { | ||
@@ -177,3 +144,3 @@ for _, flag := range c.Command.Flags { | ||
| // Returns a slice of global flag names used by the app. | ||
| // GlobalFlagNames returns a slice of global flag names used by the app. | ||
| func (c *Context) GlobalFlagNames() (names []string) { | ||
@@ -190,3 +157,3 @@ for _, flag := range c.App.Flags { | ||
| // Returns the parent context, if any | ||
| // Parent returns the parent context, if any | ||
| func (c *Context) Parent() *Context { | ||
@@ -196,5 +163,11 @@ return c.parentContext | ||
| // value returns the value of the flag coressponding to `name` | ||
| func (c *Context) value(name string) interface{} { | ||
| return c.flagSet.Lookup(name).Value.(flag.Getter).Get() | ||
| } | ||
| // Args contains apps console arguments | ||
| type Args []string | ||
| // Returns the command line arguments associated with the context. | ||
| // Args returns the command line arguments associated with the context. | ||
| func (c *Context) Args() Args { | ||
@@ -205,3 +178,3 @@ args := Args(c.flagSet.Args()) | ||
| // Returns the number of the command line arguments. | ||
| // NArg returns the number of the command line arguments. | ||
| func (c *Context) NArg() int { | ||
@@ -211,3 +184,3 @@ return len(c.Args()) | ||
| // Returns the nth argument, or else a blank string | ||
| // Get returns the nth argument, or else a blank string | ||
| func (a Args) Get(n int) string { | ||
@@ -220,3 +193,3 @@ if len(a) > n { | ||
| // Returns the first argument, or else a blank string | ||
| // First returns the first argument, or else a blank string | ||
| func (a Args) First() string { | ||
@@ -226,3 +199,3 @@ return a.Get(0) | ||
| // Return the rest of the arguments (not the first one) | ||
| // Tail returns the rest of the arguments (not the first one) | ||
| // or else an empty string slice | ||
@@ -236,3 +209,3 @@ func (a Args) Tail() []string { | ||
| // Checks if there are any arguments present | ||
| // Present checks if there are any arguments present | ||
| func (a Args) Present() bool { | ||
@@ -242,3 +215,3 @@ return len(a) != 0 | ||
| // Swaps arguments at the given indexes | ||
| // Swap swaps arguments at the given indexes | ||
| func (a Args) Swap(from, to int) error { | ||
@@ -252,2 +225,15 @@ if from >= len(a) || to >= len(a) { | ||
| func globalContext(ctx *Context) *Context { | ||
| if ctx == nil { | ||
| return nil | ||
| } | ||
| for { | ||
| if ctx.parentContext == nil { | ||
| return ctx | ||
| } | ||
| ctx = ctx.parentContext | ||
| } | ||
| } | ||
| func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { | ||
@@ -265,103 +251,2 @@ if ctx.parentContext != nil { | ||
| func lookupInt(name string, set *flag.FlagSet) int { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| val, err := strconv.Atoi(f.Value.String()) | ||
| if err != nil { | ||
| return 0 | ||
| } | ||
| return val | ||
| } | ||
| return 0 | ||
| } | ||
| func lookupDuration(name string, set *flag.FlagSet) time.Duration { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| val, err := time.ParseDuration(f.Value.String()) | ||
| if err == nil { | ||
| return val | ||
| } | ||
| } | ||
| return 0 | ||
| } | ||
| func lookupFloat64(name string, set *flag.FlagSet) float64 { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| val, err := strconv.ParseFloat(f.Value.String(), 64) | ||
| if err != nil { | ||
| return 0 | ||
| } | ||
| return val | ||
| } | ||
| return 0 | ||
| } | ||
| func lookupString(name string, set *flag.FlagSet) string { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| return f.Value.String() | ||
| } | ||
| return "" | ||
| } | ||
| func lookupStringSlice(name string, set *flag.FlagSet) []string { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| return (f.Value.(*StringSlice)).Value() | ||
| } | ||
| return nil | ||
| } | ||
| func lookupIntSlice(name string, set *flag.FlagSet) []int { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| return (f.Value.(*IntSlice)).Value() | ||
| } | ||
| return nil | ||
| } | ||
| func lookupGeneric(name string, set *flag.FlagSet) interface{} { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| return f.Value | ||
| } | ||
| return nil | ||
| } | ||
| func lookupBool(name string, set *flag.FlagSet) bool { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| val, err := strconv.ParseBool(f.Value.String()) | ||
| if err != nil { | ||
| return false | ||
| } | ||
| return val | ||
| } | ||
| return false | ||
| } | ||
| func lookupBoolT(name string, set *flag.FlagSet) bool { | ||
| f := set.Lookup(name) | ||
| if f != nil { | ||
| val, err := strconv.ParseBool(f.Value.String()) | ||
| if err != nil { | ||
| return true | ||
| } | ||
| return val | ||
| } | ||
| return false | ||
| } | ||
| func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { | ||
@@ -368,0 +253,0 @@ switch ff.Value.(type) { |
+426
-70
@@ -7,5 +7,7 @@ package cli | ||
| "reflect" | ||
| "regexp" | ||
| "runtime" | ||
| "strings" | ||
| "testing" | ||
| "time" | ||
| ) | ||
@@ -22,3 +24,2 @@ | ||
| func TestBoolFlagHelpOutput(t *testing.T) { | ||
| for _, test := range boolFlagTests { | ||
@@ -29,3 +30,3 @@ flag := BoolFlag{Name: test.name} | ||
| if output != test.expected { | ||
| t.Errorf("%s does not match %s", output, test.expected) | ||
| t.Errorf("%q does not match %q", output, test.expected) | ||
| } | ||
@@ -35,21 +36,109 @@ } | ||
| func TestFlagsFromEnv(t *testing.T) { | ||
| var flagTests = []struct { | ||
| input string | ||
| output interface{} | ||
| flag Flag | ||
| errRegexp string | ||
| }{ | ||
| {"", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, | ||
| {"1", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, | ||
| {"false", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, | ||
| {"foobar", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Sprintf(`could not parse foobar as bool value for flag debug: .*`)}, | ||
| {"", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, | ||
| {"1", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, | ||
| {"false", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, | ||
| {"foobar", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Sprintf(`could not parse foobar as bool value for flag debug: .*`)}, | ||
| {"1s", 1 * time.Second, DurationFlag{Name: "time", EnvVar: "TIME"}, ""}, | ||
| {"foobar", false, DurationFlag{Name: "time", EnvVar: "TIME"}, fmt.Sprintf(`could not parse foobar as duration for flag time: .*`)}, | ||
| {"1.2", 1.2, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, | ||
| {"1", 1.0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, | ||
| {"foobar", 0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as float64 value for flag seconds: .*`)}, | ||
| {"1", int64(1), Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, | ||
| {"1.2", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as int value for flag seconds: .*`)}, | ||
| {"foobar", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int value for flag seconds: .*`)}, | ||
| {"1", 1, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, | ||
| {"1.2", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as int value for flag seconds: .*`)}, | ||
| {"foobar", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int value for flag seconds: .*`)}, | ||
| {"1,2", IntSlice{1, 2}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, | ||
| {"1.2,2", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2,2 as int slice value for flag seconds: .*`)}, | ||
| {"foobar", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int slice value for flag seconds: .*`)}, | ||
| {"1,2", Int64Slice{1, 2}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, | ||
| {"1.2,2", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2,2 as int64 slice value for flag seconds: .*`)}, | ||
| {"foobar", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int64 slice value for flag seconds: .*`)}, | ||
| {"foo", "foo", StringFlag{Name: "name", EnvVar: "NAME"}, ""}, | ||
| {"foo,bar", StringSlice{"foo", "bar"}, StringSliceFlag{Name: "names", EnvVar: "NAMES"}, ""}, | ||
| {"1", uint(1), UintFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, | ||
| {"1.2", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as uint value for flag seconds: .*`)}, | ||
| {"foobar", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as uint value for flag seconds: .*`)}, | ||
| {"1", uint64(1), Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, | ||
| {"1.2", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as uint64 value for flag seconds: .*`)}, | ||
| {"foobar", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as uint64 value for flag seconds: .*`)}, | ||
| {"foo,bar", &Parser{"foo", "bar"}, GenericFlag{Name: "names", Value: &Parser{}, EnvVar: "NAMES"}, ""}, | ||
| } | ||
| for _, test := range flagTests { | ||
| os.Clearenv() | ||
| os.Setenv(reflect.ValueOf(test.flag).FieldByName("EnvVar").String(), test.input) | ||
| a := App{ | ||
| Flags: []Flag{test.flag}, | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.value(test.flag.GetName()), test.output) { | ||
| t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.value(test.flag.GetName())) | ||
| } | ||
| return nil | ||
| }, | ||
| } | ||
| err := a.Run([]string{"run"}) | ||
| if test.errRegexp != "" { | ||
| if err == nil { | ||
| t.Errorf("expected error to match %s, got none", test.errRegexp) | ||
| } else { | ||
| if matched, _ := regexp.MatchString(test.errRegexp, err.Error()); !matched { | ||
| t.Errorf("expected error to match %s, got error %s", test.errRegexp, err) | ||
| } | ||
| } | ||
| } else { | ||
| if err != nil && test.errRegexp == "" { | ||
| t.Errorf("expected no error got %s", err) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| var stringFlagTests = []struct { | ||
| name string | ||
| usage string | ||
| value string | ||
| expected string | ||
| }{ | ||
| {"help", "", "--help \t"}, | ||
| {"h", "", "-h \t"}, | ||
| {"h", "", "-h \t"}, | ||
| {"test", "Something", "--test \"Something\"\t"}, | ||
| {"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\")"}, | ||
| } | ||
| func TestStringFlagHelpOutput(t *testing.T) { | ||
| for _, test := range stringFlagTests { | ||
| flag := StringFlag{Name: test.name, Value: test.value} | ||
| flag := StringFlag{Name: test.name, Usage: test.usage, Value: test.value} | ||
| output := flag.String() | ||
| if output != test.expected { | ||
| t.Errorf("%s does not match %s", output, test.expected) | ||
| t.Errorf("%q does not match %q", output, test.expected) | ||
| } | ||
@@ -81,17 +170,17 @@ } | ||
| }{ | ||
| {"help", func() *StringSlice { | ||
| {"foo", func() *StringSlice { | ||
| s := &StringSlice{} | ||
| s.Set("") | ||
| return s | ||
| }(), "--help [--help option --help option]\t"}, | ||
| {"h", func() *StringSlice { | ||
| }(), "--foo value\t"}, | ||
| {"f", func() *StringSlice { | ||
| s := &StringSlice{} | ||
| s.Set("") | ||
| return s | ||
| }(), "-h [-h option -h option]\t"}, | ||
| {"h", func() *StringSlice { | ||
| }(), "-f value\t"}, | ||
| {"f", func() *StringSlice { | ||
| s := &StringSlice{} | ||
| s.Set("") | ||
| s.Set("Lipstick") | ||
| return s | ||
| }(), "-h [-h option -h option]\t"}, | ||
| }(), "-f value\t(default: \"Lipstick\")"}, | ||
| {"test", func() *StringSlice { | ||
@@ -101,7 +190,6 @@ s := &StringSlice{} | ||
| return s | ||
| }(), "--test [--test option --test option]\t"}, | ||
| }(), "--test value\t(default: \"Something\")"}, | ||
| } | ||
| func TestStringSliceFlagHelpOutput(t *testing.T) { | ||
| for _, test := range stringSliceFlagTests { | ||
@@ -138,10 +226,9 @@ flag := StringSliceFlag{Name: test.name, Value: test.value} | ||
| }{ | ||
| {"help", "--help \"0\"\t"}, | ||
| {"h", "-h \"0\"\t"}, | ||
| {"hats", "--hats value\t(default: 9)"}, | ||
| {"H", "-H value\t(default: 9)"}, | ||
| } | ||
| func TestIntFlagHelpOutput(t *testing.T) { | ||
| for _, test := range intFlagTests { | ||
| flag := IntFlag{Name: test.name} | ||
| flag := IntFlag{Name: test.name, Value: 9} | ||
| output := flag.String() | ||
@@ -172,2 +259,110 @@ | ||
| var int64FlagTests = []struct { | ||
| name string | ||
| expected string | ||
| }{ | ||
| {"hats", "--hats value\t(default: 8589934592)"}, | ||
| {"H", "-H value\t(default: 8589934592)"}, | ||
| } | ||
| func TestInt64FlagHelpOutput(t *testing.T) { | ||
| for _, test := range int64FlagTests { | ||
| flag := Int64Flag{Name: test.name, Value: 8589934592} | ||
| output := flag.String() | ||
| if output != test.expected { | ||
| t.Errorf("%s does not match %s", output, test.expected) | ||
| } | ||
| } | ||
| } | ||
| func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) { | ||
| os.Clearenv() | ||
| os.Setenv("APP_BAR", "2") | ||
| for _, test := range int64FlagTests { | ||
| flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} | ||
| output := flag.String() | ||
| expectedSuffix := " [$APP_BAR]" | ||
| if runtime.GOOS == "windows" { | ||
| expectedSuffix = " [%APP_BAR%]" | ||
| } | ||
| if !strings.HasSuffix(output, expectedSuffix) { | ||
| t.Errorf("%s does not end with"+expectedSuffix, output) | ||
| } | ||
| } | ||
| } | ||
| var uintFlagTests = []struct { | ||
| name string | ||
| expected string | ||
| }{ | ||
| {"nerfs", "--nerfs value\t(default: 41)"}, | ||
| {"N", "-N value\t(default: 41)"}, | ||
| } | ||
| func TestUintFlagHelpOutput(t *testing.T) { | ||
| for _, test := range uintFlagTests { | ||
| flag := UintFlag{Name: test.name, Value: 41} | ||
| output := flag.String() | ||
| if output != test.expected { | ||
| t.Errorf("%s does not match %s", output, test.expected) | ||
| } | ||
| } | ||
| } | ||
| func TestUintFlagWithEnvVarHelpOutput(t *testing.T) { | ||
| os.Clearenv() | ||
| os.Setenv("APP_BAR", "2") | ||
| for _, test := range uintFlagTests { | ||
| flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} | ||
| output := flag.String() | ||
| expectedSuffix := " [$APP_BAR]" | ||
| if runtime.GOOS == "windows" { | ||
| expectedSuffix = " [%APP_BAR%]" | ||
| } | ||
| if !strings.HasSuffix(output, expectedSuffix) { | ||
| t.Errorf("%s does not end with"+expectedSuffix, output) | ||
| } | ||
| } | ||
| } | ||
| var uint64FlagTests = []struct { | ||
| name string | ||
| expected string | ||
| }{ | ||
| {"gerfs", "--gerfs value\t(default: 8589934582)"}, | ||
| {"G", "-G value\t(default: 8589934582)"}, | ||
| } | ||
| func TestUint64FlagHelpOutput(t *testing.T) { | ||
| for _, test := range uint64FlagTests { | ||
| flag := Uint64Flag{Name: test.name, Value: 8589934582} | ||
| output := flag.String() | ||
| if output != test.expected { | ||
| t.Errorf("%s does not match %s", output, test.expected) | ||
| } | ||
| } | ||
| } | ||
| func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) { | ||
| os.Clearenv() | ||
| os.Setenv("APP_BAR", "2") | ||
| for _, test := range uint64FlagTests { | ||
| flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} | ||
| output := flag.String() | ||
| expectedSuffix := " [$APP_BAR]" | ||
| if runtime.GOOS == "windows" { | ||
| expectedSuffix = " [%APP_BAR%]" | ||
| } | ||
| if !strings.HasSuffix(output, expectedSuffix) { | ||
| t.Errorf("%s does not end with"+expectedSuffix, output) | ||
| } | ||
| } | ||
| } | ||
| var durationFlagTests = []struct { | ||
@@ -177,14 +372,13 @@ name string | ||
| }{ | ||
| {"help", "--help \"0\"\t"}, | ||
| {"h", "-h \"0\"\t"}, | ||
| {"hooting", "--hooting value\t(default: 1s)"}, | ||
| {"H", "-H value\t(default: 1s)"}, | ||
| } | ||
| func TestDurationFlagHelpOutput(t *testing.T) { | ||
| for _, test := range durationFlagTests { | ||
| flag := DurationFlag{Name: test.name} | ||
| flag := DurationFlag{Name: test.name, Value: 1 * time.Second} | ||
| output := flag.String() | ||
| if output != test.expected { | ||
| t.Errorf("%s does not match %s", output, test.expected) | ||
| t.Errorf("%q does not match %q", output, test.expected) | ||
| } | ||
@@ -216,14 +410,13 @@ } | ||
| }{ | ||
| {"help", &IntSlice{}, "--help [--help option --help option]\t"}, | ||
| {"h", &IntSlice{}, "-h [-h option -h option]\t"}, | ||
| {"h", &IntSlice{}, "-h [-h option -h option]\t"}, | ||
| {"test", func() *IntSlice { | ||
| {"heads", &IntSlice{}, "--heads value\t"}, | ||
| {"H", &IntSlice{}, "-H value\t"}, | ||
| {"H, heads", func() *IntSlice { | ||
| i := &IntSlice{} | ||
| i.Set("9") | ||
| i.Set("3") | ||
| return i | ||
| }(), "--test [--test option --test option]\t"}, | ||
| }(), "-H value, --heads value\t(default: 9, 3)"}, | ||
| } | ||
| func TestIntSliceFlagHelpOutput(t *testing.T) { | ||
| for _, test := range intSliceFlagTests { | ||
@@ -256,2 +449,45 @@ flag := IntSliceFlag{Name: test.name, Value: test.value} | ||
| var int64SliceFlagTests = []struct { | ||
| name string | ||
| value *Int64Slice | ||
| expected string | ||
| }{ | ||
| {"heads", &Int64Slice{}, "--heads value\t"}, | ||
| {"H", &Int64Slice{}, "-H value\t"}, | ||
| {"H, heads", func() *Int64Slice { | ||
| i := &Int64Slice{} | ||
| i.Set("2") | ||
| i.Set("17179869184") | ||
| return i | ||
| }(), "-H value, --heads value\t(default: 2, 17179869184)"}, | ||
| } | ||
| func TestInt64SliceFlagHelpOutput(t *testing.T) { | ||
| for _, test := range int64SliceFlagTests { | ||
| flag := Int64SliceFlag{Name: test.name, Value: test.value} | ||
| output := flag.String() | ||
| if output != test.expected { | ||
| t.Errorf("%q does not match %q", output, test.expected) | ||
| } | ||
| } | ||
| } | ||
| func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) { | ||
| os.Clearenv() | ||
| os.Setenv("APP_SMURF", "42,17179869184") | ||
| for _, test := range int64SliceFlagTests { | ||
| flag := Int64SliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} | ||
| output := flag.String() | ||
| expectedSuffix := " [$APP_SMURF]" | ||
| if runtime.GOOS == "windows" { | ||
| expectedSuffix = " [%APP_SMURF%]" | ||
| } | ||
| if !strings.HasSuffix(output, expectedSuffix) { | ||
| t.Errorf("%q does not end with"+expectedSuffix, output) | ||
| } | ||
| } | ||
| } | ||
| var float64FlagTests = []struct { | ||
@@ -261,14 +497,13 @@ name string | ||
| }{ | ||
| {"help", "--help \"0\"\t"}, | ||
| {"h", "-h \"0\"\t"}, | ||
| {"hooting", "--hooting value\t(default: 0.1)"}, | ||
| {"H", "-H value\t(default: 0.1)"}, | ||
| } | ||
| func TestFloat64FlagHelpOutput(t *testing.T) { | ||
| for _, test := range float64FlagTests { | ||
| flag := Float64Flag{Name: test.name} | ||
| flag := Float64Flag{Name: test.name, Value: float64(0.1)} | ||
| output := flag.String() | ||
| if output != test.expected { | ||
| t.Errorf("%s does not match %s", output, test.expected) | ||
| t.Errorf("%q does not match %q", output, test.expected) | ||
| } | ||
@@ -300,8 +535,7 @@ } | ||
| }{ | ||
| {"test", &Parser{"abc", "def"}, "--test \"abc,def\"\ttest flag"}, | ||
| {"t", &Parser{"abc", "def"}, "-t \"abc,def\"\ttest flag"}, | ||
| {"toads", &Parser{"abc", "def"}, "--toads value\ttest flag (default: abc,def)"}, | ||
| {"t", &Parser{"abc", "def"}, "-t value\ttest flag (default: abc,def)"}, | ||
| } | ||
| func TestGenericFlagHelpOutput(t *testing.T) { | ||
| for _, test := range genericFlagTests { | ||
@@ -339,3 +573,3 @@ flag := GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"} | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.String("serve") != "10" { | ||
@@ -347,2 +581,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -361,6 +596,7 @@ }).Run([]string{"run", "-s", "10"}) | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if dest != "10" { | ||
| t.Errorf("expected destination String 10") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -378,3 +614,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.String("count") != "20" { | ||
@@ -386,2 +622,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -398,3 +635,3 @@ }).Run([]string{"run"}) | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.String("count") != "20" { | ||
@@ -406,2 +643,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -416,3 +654,3 @@ }).Run([]string{"run"}) | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) { | ||
@@ -424,2 +662,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -437,3 +676,3 @@ }).Run([]string{"run", "-s", "10", "-s", "20"}) | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { | ||
@@ -445,2 +684,3 @@ t.Errorf("main name not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -458,3 +698,3 @@ }).Run([]string{"run"}) | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { | ||
@@ -466,2 +706,3 @@ t.Errorf("main name not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -476,3 +717,3 @@ }).Run([]string{"run"}) | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Int("serve") != 10 { | ||
@@ -484,2 +725,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -499,6 +741,7 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if dest != 10 { | ||
| t.Errorf("expected destination Int 10") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -516,3 +759,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Int("timeout") != 10 { | ||
@@ -524,2 +767,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -537,3 +781,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Int("timeout") != 10 { | ||
@@ -545,2 +789,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -556,3 +801,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { | ||
@@ -564,2 +809,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -577,3 +823,3 @@ }).Run([]string{"run", "-s", "10", "-s", "20"}) | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { | ||
@@ -585,2 +831,3 @@ t.Errorf("main name not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -598,3 +845,3 @@ }).Run([]string{"run"}) | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { | ||
@@ -606,2 +853,3 @@ t.Errorf("main name not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -611,2 +859,59 @@ }).Run([]string{"run"}) | ||
| func TestParseMultiInt64Slice(t *testing.T) { | ||
| (&App{ | ||
| Flags: []Flag{ | ||
| Int64SliceFlag{Name: "serve, s", Value: &Int64Slice{}}, | ||
| }, | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.Int64Slice("serve"), []int64{10, 17179869184}) { | ||
| t.Errorf("main name not set") | ||
| } | ||
| if !reflect.DeepEqual(ctx.Int64Slice("s"), []int64{10, 17179869184}) { | ||
| t.Errorf("short name not set") | ||
| } | ||
| return nil | ||
| }, | ||
| }).Run([]string{"run", "-s", "10", "-s", "17179869184"}) | ||
| } | ||
| func TestParseMultiInt64SliceFromEnv(t *testing.T) { | ||
| os.Clearenv() | ||
| os.Setenv("APP_INTERVALS", "20,30,17179869184") | ||
| (&App{ | ||
| Flags: []Flag{ | ||
| Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "APP_INTERVALS"}, | ||
| }, | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) { | ||
| t.Errorf("main name not set from env") | ||
| } | ||
| if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) { | ||
| t.Errorf("short name not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
| }).Run([]string{"run"}) | ||
| } | ||
| func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) { | ||
| os.Clearenv() | ||
| os.Setenv("APP_INTERVALS", "20,30,17179869184") | ||
| (&App{ | ||
| Flags: []Flag{ | ||
| Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, | ||
| }, | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) { | ||
| t.Errorf("main name not set from env") | ||
| } | ||
| if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) { | ||
| t.Errorf("short name not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
| }).Run([]string{"run"}) | ||
| } | ||
| func TestParseMultiFloat64(t *testing.T) { | ||
@@ -617,3 +922,3 @@ a := App{ | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Float64("serve") != 10.2 { | ||
@@ -625,2 +930,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -640,6 +946,7 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if dest != 10.2 { | ||
| t.Errorf("expected destination Float64 10.2") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -657,3 +964,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Float64("timeout") != 15.5 { | ||
@@ -665,2 +972,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -678,3 +986,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Float64("timeout") != 15.5 { | ||
@@ -686,2 +994,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -697,3 +1006,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Bool("serve") != true { | ||
@@ -705,2 +1014,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -720,6 +1030,7 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if dest != true { | ||
| t.Errorf("expected destination Bool true") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -737,3 +1048,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Bool("debug") != true { | ||
@@ -745,2 +1056,3 @@ t.Errorf("main name not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -758,3 +1070,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Bool("debug") != true { | ||
@@ -766,2 +1078,3 @@ t.Errorf("main name not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -772,2 +1085,34 @@ } | ||
| func TestParseBoolTFromEnv(t *testing.T) { | ||
| var boolTFlagTests = []struct { | ||
| input string | ||
| output bool | ||
| }{ | ||
| {"", false}, | ||
| {"1", true}, | ||
| {"false", false}, | ||
| {"true", true}, | ||
| } | ||
| for _, test := range boolTFlagTests { | ||
| os.Clearenv() | ||
| os.Setenv("DEBUG", test.input) | ||
| a := App{ | ||
| Flags: []Flag{ | ||
| BoolTFlag{Name: "debug, d", EnvVar: "DEBUG"}, | ||
| }, | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Bool("debug") != test.output { | ||
| t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("debug")) | ||
| } | ||
| if ctx.Bool("d") != test.output { | ||
| t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("d")) | ||
| } | ||
| return nil | ||
| }, | ||
| } | ||
| a.Run([]string{"run"}) | ||
| } | ||
| } | ||
| func TestParseMultiBoolT(t *testing.T) { | ||
@@ -778,3 +1123,3 @@ a := App{ | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.BoolT("serve") != true { | ||
@@ -786,2 +1131,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -801,6 +1147,7 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if dest != true { | ||
| t.Errorf("expected destination BoolT true") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -818,3 +1165,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.BoolT("debug") != false { | ||
@@ -826,2 +1173,3 @@ t.Errorf("main name not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -839,3 +1187,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.BoolT("debug") != false { | ||
@@ -847,2 +1195,3 @@ t.Errorf("main name not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -871,2 +1220,6 @@ } | ||
| func (p *Parser) Get() interface{} { | ||
| return p | ||
| } | ||
| func TestParseGeneric(t *testing.T) { | ||
@@ -877,3 +1230,3 @@ a := App{ | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { | ||
@@ -885,2 +1238,3 @@ t.Errorf("main name not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -898,3 +1252,3 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) { | ||
@@ -906,2 +1260,3 @@ t.Errorf("main name not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -919,6 +1274,7 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) { | ||
| t.Errorf("value not set from env") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -925,0 +1281,0 @@ } |
+462
-209
@@ -6,24 +6,28 @@ package cli | ||
| "fmt" | ||
| "os" | ||
| "reflect" | ||
| "runtime" | ||
| "strconv" | ||
| "strings" | ||
| "syscall" | ||
| "time" | ||
| ) | ||
| // This flag enables bash-completion for all commands and subcommands | ||
| var BashCompletionFlag = BoolFlag{ | ||
| Name: "generate-bash-completion", | ||
| const defaultPlaceholder = "value" | ||
| // BashCompletionFlag enables bash-completion for all commands and subcommands | ||
| var BashCompletionFlag Flag = BoolFlag{ | ||
| Name: "generate-bash-completion", | ||
| Hidden: true, | ||
| } | ||
| // This flag prints the version for the application | ||
| var VersionFlag = BoolFlag{ | ||
| Name: "version", | ||
| // VersionFlag prints the version for the application | ||
| var VersionFlag Flag = BoolFlag{ | ||
| Name: "version, v", | ||
| Usage: "print the version", | ||
| } | ||
| // This flag prints the help for all commands and subcommands | ||
| // HelpFlag prints the help for all commands and subcommands | ||
| // Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand | ||
| // unless HideHelp is set to true) | ||
| var HelpFlag = BoolFlag{ | ||
| var HelpFlag Flag = BoolFlag{ | ||
| Name: "help, h", | ||
@@ -33,2 +37,21 @@ Usage: "show help", | ||
| // FlagStringer converts a flag definition to a string. This is used by help | ||
| // to display a flag. | ||
| var FlagStringer FlagStringFunc = stringifyFlag | ||
| // FlagsByName is a slice of Flag. | ||
| type FlagsByName []Flag | ||
| func (f FlagsByName) Len() int { | ||
| return len(f) | ||
| } | ||
| func (f FlagsByName) Less(i, j int) bool { | ||
| return f[i].GetName() < f[j].GetName() | ||
| } | ||
| func (f FlagsByName) Swap(i, j int) { | ||
| f[i], f[j] = f[j], f[i] | ||
| } | ||
| // Flag is a common interface related to parsing flags in cli. | ||
@@ -44,9 +67,25 @@ // For more advanced flag parsing techniques, it is recommended that | ||
| func flagSet(name string, flags []Flag) *flag.FlagSet { | ||
| // errorableFlag is an interface that allows us to return errors during apply | ||
| // it allows flags defined in this library to return errors in a fashion backwards compatible | ||
| // TODO remove in v2 and modify the existing Flag interface to return errors | ||
| type errorableFlag interface { | ||
| Flag | ||
| ApplyWithError(*flag.FlagSet) error | ||
| } | ||
| func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { | ||
| set := flag.NewFlagSet(name, flag.ContinueOnError) | ||
| for _, f := range flags { | ||
| f.Apply(set) | ||
| //TODO remove in v2 when errorableFlag is removed | ||
| if ef, ok := f.(errorableFlag); ok { | ||
| if err := ef.ApplyWithError(set); err != nil { | ||
| return nil, err | ||
| } | ||
| } else { | ||
| f.Apply(set) | ||
| } | ||
| } | ||
| return set | ||
| return set, nil | ||
| } | ||
@@ -68,31 +107,12 @@ | ||
| // GenericFlag is the flag type for types implementing Generic | ||
| type GenericFlag struct { | ||
| Name string | ||
| Value Generic | ||
| Usage string | ||
| EnvVar string | ||
| // Apply takes the flagset and calls Set on the generic flag with the value | ||
| // provided by the user for parsing by the flag | ||
| // Ignores parsing errors | ||
| func (f GenericFlag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // 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 { | ||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage)) | ||
| } | ||
| func (f GenericFlag) FormatValueHelp() string { | ||
| if f.Value == nil { | ||
| return "" | ||
| } | ||
| s := f.Value.String() | ||
| if len(s) == 0 { | ||
| return "" | ||
| } | ||
| return fmt.Sprintf("\"%s\"", s) | ||
| } | ||
| // Apply takes the flagset and calls Set on the generic flag with the value | ||
| // ApplyWithError 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) ApplyWithError(set *flag.FlagSet) error { | ||
| val := f.Value | ||
@@ -102,4 +122,6 @@ if f.EnvVar != "" { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal := os.Getenv(envVar); envVal != "" { | ||
| val.Set(envVal) | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| if err := val.Set(envVal); err != nil { | ||
| return fmt.Errorf("could not parse %s as value for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
| break | ||
@@ -113,9 +135,7 @@ } | ||
| }) | ||
| } | ||
| func (f GenericFlag) GetName() string { | ||
| return f.Name | ||
| return nil | ||
| } | ||
| // StringSlice is an opaque type for []string to satisfy flag.Value | ||
| // StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter | ||
| type StringSlice []string | ||
@@ -139,28 +159,25 @@ | ||
| // StringSlice 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 | ||
| // Get returns the slice of strings set by this flag | ||
| func (f *StringSlice) Get() interface{} { | ||
| return *f | ||
| } | ||
| // String returns the usage | ||
| func (f StringSliceFlag) String() string { | ||
| firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") | ||
| pref := prefixFor(firstName) | ||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f StringSliceFlag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // Apply populates the flag given the flag set and environment | ||
| func (f StringSliceFlag) Apply(set *flag.FlagSet) { | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| if f.EnvVar != "" { | ||
| for _, envVar := range strings.Split(f.EnvVar, ",") { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal := os.Getenv(envVar); envVal != "" { | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| newVal := &StringSlice{} | ||
| for _, s := range strings.Split(envVal, ",") { | ||
| s = strings.TrimSpace(s) | ||
| newVal.Set(s) | ||
| if err := newVal.Set(s); err != nil { | ||
| return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
| } | ||
@@ -185,9 +202,7 @@ | ||
| }) | ||
| } | ||
| func (f StringSliceFlag) GetName() string { | ||
| return f.Name | ||
| return nil | ||
| } | ||
| // StringSlice is an opaque type for []int to satisfy flag.Value | ||
| // IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter | ||
| type IntSlice []int | ||
@@ -200,5 +215,4 @@ | ||
| return err | ||
| } else { | ||
| *f = append(*f, tmp) | ||
| } | ||
| *f = append(*f, tmp) | ||
| return nil | ||
@@ -209,3 +223,3 @@ } | ||
| func (f *IntSlice) String() string { | ||
| return fmt.Sprintf("%d", *f) | ||
| return fmt.Sprintf("%#v", *f) | ||
| } | ||
@@ -218,30 +232,24 @@ | ||
| // 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 | ||
| // Get returns the slice of ints set by this flag | ||
| func (f *IntSlice) Get() interface{} { | ||
| return *f | ||
| } | ||
| // String returns the usage | ||
| func (f IntSliceFlag) String() string { | ||
| firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") | ||
| pref := prefixFor(firstName) | ||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f IntSliceFlag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // Apply populates the flag given the flag set and environment | ||
| func (f IntSliceFlag) Apply(set *flag.FlagSet) { | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| if f.EnvVar != "" { | ||
| for _, envVar := range strings.Split(f.EnvVar, ",") { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal := os.Getenv(envVar); envVal != "" { | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| newVal := &IntSlice{} | ||
| for _, s := range strings.Split(envVal, ",") { | ||
| s = strings.TrimSpace(s) | ||
| err := newVal.Set(s) | ||
| if err != nil { | ||
| fmt.Fprintf(os.Stderr, err.Error()) | ||
| if err := newVal.Set(s); err != nil { | ||
| return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
@@ -261,23 +269,76 @@ } | ||
| }) | ||
| } | ||
| func (f IntSliceFlag) GetName() string { | ||
| return f.Name | ||
| return nil | ||
| } | ||
| // BoolFlag is a switch that defaults to false | ||
| type BoolFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Destination *bool | ||
| // Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter | ||
| type Int64Slice []int64 | ||
| // Set parses the value into an integer and appends it to the list of values | ||
| func (f *Int64Slice) Set(value string) error { | ||
| tmp, err := strconv.ParseInt(value, 10, 64) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| *f = append(*f, tmp) | ||
| return nil | ||
| } | ||
| // String returns a readable representation of this value (for usage defaults) | ||
| func (f BoolFlag) String() string { | ||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) | ||
| func (f *Int64Slice) String() string { | ||
| return fmt.Sprintf("%#v", *f) | ||
| } | ||
| // Value returns the slice of ints set by this flag | ||
| func (f *Int64Slice) Value() []int64 { | ||
| return *f | ||
| } | ||
| // Get returns the slice of ints set by this flag | ||
| func (f *Int64Slice) Get() interface{} { | ||
| return *f | ||
| } | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f Int64SliceFlag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| if f.EnvVar != "" { | ||
| for _, envVar := range strings.Split(f.EnvVar, ",") { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| newVal := &Int64Slice{} | ||
| for _, s := range strings.Split(envVal, ",") { | ||
| s = strings.TrimSpace(s) | ||
| if err := newVal.Set(s); err != nil { | ||
| return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
| } | ||
| f.Value = newVal | ||
| break | ||
| } | ||
| } | ||
| } | ||
| eachName(f.Name, func(name string) { | ||
| if f.Value == nil { | ||
| f.Value = &Int64Slice{} | ||
| } | ||
| set.Var(f.Value, name, f.Usage) | ||
| }) | ||
| return nil | ||
| } | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f BoolFlag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| val := false | ||
@@ -287,7 +348,14 @@ if f.EnvVar != "" { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal := os.Getenv(envVar); envVal != "" { | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| if envVal == "" { | ||
| val = false | ||
| break | ||
| } | ||
| envValBool, err := strconv.ParseBool(envVal) | ||
| if err == nil { | ||
| val = envValBool | ||
| if err != nil { | ||
| return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
| val = envValBool | ||
| break | ||
@@ -305,24 +373,14 @@ } | ||
| }) | ||
| } | ||
| func (f BoolFlag) GetName() string { | ||
| return f.Name | ||
| return nil | ||
| } | ||
| // BoolTFlag this represents a boolean flag that is true by default, but can | ||
| // still be set to false by --some-flag=false | ||
| type BoolTFlag struct { | ||
| Name string | ||
| Usage string | ||
| EnvVar string | ||
| Destination *bool | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f BoolTFlag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // String returns a readable representation of this value (for usage defaults) | ||
| func (f BoolTFlag) String() string { | ||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) | ||
| } | ||
| // Apply populates the flag given the flag set and environment | ||
| func (f BoolTFlag) Apply(set *flag.FlagSet) { | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| val := true | ||
@@ -332,8 +390,15 @@ if f.EnvVar != "" { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal := os.Getenv(envVar); envVal != "" { | ||
| envValBool, err := strconv.ParseBool(envVal) | ||
| if err == nil { | ||
| val = envValBool | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| if envVal == "" { | ||
| val = false | ||
| break | ||
| } | ||
| envValBool, err := strconv.ParseBool(envVal) | ||
| if err != nil { | ||
| return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
| val = envValBool | ||
| break | ||
| } | ||
@@ -350,36 +415,18 @@ } | ||
| }) | ||
| } | ||
| func (f BoolTFlag) GetName() string { | ||
| return f.Name | ||
| return nil | ||
| } | ||
| // StringFlag represents a flag that takes as string value | ||
| type StringFlag struct { | ||
| Name string | ||
| Value string | ||
| Usage string | ||
| EnvVar string | ||
| Destination *string | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f StringFlag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // String returns the usage | ||
| func (f StringFlag) String() string { | ||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage)) | ||
| } | ||
| func (f StringFlag) FormatValueHelp() string { | ||
| s := f.Value | ||
| if len(s) == 0 { | ||
| return "" | ||
| } | ||
| return fmt.Sprintf("\"%s\"", s) | ||
| } | ||
| // Apply populates the flag given the flag set and environment | ||
| func (f StringFlag) Apply(set *flag.FlagSet) { | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| if f.EnvVar != "" { | ||
| for _, envVar := range strings.Split(f.EnvVar, ",") { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal := os.Getenv(envVar); envVal != "" { | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| f.Value = envVal | ||
@@ -398,34 +445,58 @@ break | ||
| }) | ||
| return nil | ||
| } | ||
| func (f StringFlag) GetName() string { | ||
| return f.Name | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f IntFlag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // IntFlag is a flag that takes an integer | ||
| // Errors if the value provided cannot be parsed | ||
| type IntFlag struct { | ||
| Name string | ||
| Value int | ||
| Usage string | ||
| EnvVar string | ||
| Destination *int | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| if f.EnvVar != "" { | ||
| for _, envVar := range strings.Split(f.EnvVar, ",") { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| envValInt, err := strconv.ParseInt(envVal, 0, 64) | ||
| if err != nil { | ||
| return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
| f.Value = int(envValInt) | ||
| break | ||
| } | ||
| } | ||
| } | ||
| eachName(f.Name, func(name string) { | ||
| if f.Destination != nil { | ||
| set.IntVar(f.Destination, name, f.Value, f.Usage) | ||
| return | ||
| } | ||
| set.Int(name, f.Value, f.Usage) | ||
| }) | ||
| return nil | ||
| } | ||
| // String returns the usage | ||
| func (f IntFlag) String() string { | ||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f Int64Flag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // Apply populates the flag given the flag set and environment | ||
| func (f IntFlag) Apply(set *flag.FlagSet) { | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { | ||
| if f.EnvVar != "" { | ||
| for _, envVar := range strings.Split(f.EnvVar, ",") { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal := os.Getenv(envVar); envVal != "" { | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| envValInt, err := strconv.ParseInt(envVal, 0, 64) | ||
| if err == nil { | ||
| f.Value = int(envValInt) | ||
| break | ||
| if err != nil { | ||
| return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
| f.Value = envValInt | ||
| break | ||
| } | ||
@@ -437,39 +508,98 @@ } | ||
| if f.Destination != nil { | ||
| set.IntVar(f.Destination, name, f.Value, f.Usage) | ||
| set.Int64Var(f.Destination, name, f.Value, f.Usage) | ||
| return | ||
| } | ||
| set.Int(name, f.Value, f.Usage) | ||
| set.Int64(name, f.Value, f.Usage) | ||
| }) | ||
| return nil | ||
| } | ||
| func (f IntFlag) GetName() string { | ||
| return f.Name | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f UintFlag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // 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 | ||
| Value time.Duration | ||
| Usage string | ||
| EnvVar string | ||
| Destination *time.Duration | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| if f.EnvVar != "" { | ||
| for _, envVar := range strings.Split(f.EnvVar, ",") { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| envValInt, err := strconv.ParseUint(envVal, 0, 64) | ||
| if err != nil { | ||
| return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
| f.Value = uint(envValInt) | ||
| break | ||
| } | ||
| } | ||
| } | ||
| eachName(f.Name, func(name string) { | ||
| if f.Destination != nil { | ||
| set.UintVar(f.Destination, name, f.Value, f.Usage) | ||
| return | ||
| } | ||
| set.Uint(name, f.Value, f.Usage) | ||
| }) | ||
| return nil | ||
| } | ||
| // String returns a readable representation of this value (for usage defaults) | ||
| func (f DurationFlag) String() string { | ||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f Uint64Flag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { | ||
| if f.EnvVar != "" { | ||
| for _, envVar := range strings.Split(f.EnvVar, ",") { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| envValInt, err := strconv.ParseUint(envVal, 0, 64) | ||
| if err != nil { | ||
| return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
| f.Value = uint64(envValInt) | ||
| break | ||
| } | ||
| } | ||
| } | ||
| eachName(f.Name, func(name string) { | ||
| if f.Destination != nil { | ||
| set.Uint64Var(f.Destination, name, f.Value, f.Usage) | ||
| return | ||
| } | ||
| set.Uint64(name, f.Value, f.Usage) | ||
| }) | ||
| return nil | ||
| } | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f DurationFlag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { | ||
| if f.EnvVar != "" { | ||
| for _, envVar := range strings.Split(f.EnvVar, ",") { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal := os.Getenv(envVar); envVal != "" { | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| envValDuration, err := time.ParseDuration(envVal) | ||
| if err == nil { | ||
| f.Value = envValDuration | ||
| break | ||
| if err != nil { | ||
| return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
| f.Value = envValDuration | ||
| break | ||
| } | ||
@@ -486,33 +616,25 @@ } | ||
| }) | ||
| } | ||
| func (f DurationFlag) GetName() string { | ||
| return f.Name | ||
| return nil | ||
| } | ||
| // Float64Flag is a flag that takes an float value | ||
| // Errors if the value provided cannot be parsed | ||
| type Float64Flag struct { | ||
| Name string | ||
| Value float64 | ||
| Usage string | ||
| EnvVar string | ||
| Destination *float64 | ||
| // Apply populates the flag given the flag set and environment | ||
| // Ignores errors | ||
| func (f Float64Flag) Apply(set *flag.FlagSet) { | ||
| f.ApplyWithError(set) | ||
| } | ||
| // String returns the usage | ||
| func (f Float64Flag) String() string { | ||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) | ||
| } | ||
| // Apply populates the flag given the flag set and environment | ||
| func (f Float64Flag) Apply(set *flag.FlagSet) { | ||
| // ApplyWithError populates the flag given the flag set and environment | ||
| func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { | ||
| if f.EnvVar != "" { | ||
| for _, envVar := range strings.Split(f.EnvVar, ",") { | ||
| envVar = strings.TrimSpace(envVar) | ||
| if envVal := os.Getenv(envVar); envVal != "" { | ||
| if envVal, ok := syscall.Getenv(envVar); ok { | ||
| envValFloat, err := strconv.ParseFloat(envVal, 10) | ||
| if err == nil { | ||
| f.Value = float64(envValFloat) | ||
| if err != nil { | ||
| return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) | ||
| } | ||
| f.Value = float64(envValFloat) | ||
| break | ||
| } | ||
@@ -529,6 +651,15 @@ } | ||
| }) | ||
| return nil | ||
| } | ||
| func (f Float64Flag) GetName() string { | ||
| return f.Name | ||
| func visibleFlags(fl []Flag) []Flag { | ||
| visible := []Flag{} | ||
| for _, flag := range fl { | ||
| field := flagValue(flag).FieldByName("Hidden") | ||
| if !field.IsValid() || !field.Bool() { | ||
| visible = append(visible, flag) | ||
| } | ||
| } | ||
| return visible | ||
| } | ||
@@ -546,3 +677,21 @@ | ||
| func prefixedNames(fullName string) (prefixed string) { | ||
| // Returns the placeholder, if any, and the unquoted usage string. | ||
| func unquoteUsage(usage string) (string, string) { | ||
| for i := 0; i < len(usage); i++ { | ||
| if usage[i] == '`' { | ||
| for j := i + 1; j < len(usage); j++ { | ||
| if usage[j] == '`' { | ||
| name := usage[i+1 : j] | ||
| usage = usage[:i] + name + usage[j+1:] | ||
| return name, usage | ||
| } | ||
| } | ||
| break | ||
| } | ||
| } | ||
| return "", usage | ||
| } | ||
| func prefixedNames(fullName, placeholder string) string { | ||
| var prefixed string | ||
| parts := strings.Split(fullName, ",") | ||
@@ -552,2 +701,5 @@ for i, name := range parts { | ||
| prefixed += prefixFor(name) + name | ||
| if placeholder != "" { | ||
| prefixed += " " + placeholder | ||
| } | ||
| if i < len(parts)-1 { | ||
@@ -557,3 +709,3 @@ prefixed += ", " | ||
| } | ||
| return | ||
| return prefixed | ||
| } | ||
@@ -576,1 +728,102 @@ | ||
| } | ||
| 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))) | ||
| case Int64SliceFlag: | ||
| return withEnvHint(fv.FieldByName("EnvVar").String(), | ||
| stringifyInt64SliceFlag(f.(Int64SliceFlag))) | ||
| case StringSliceFlag: | ||
| return withEnvHint(fv.FieldByName("EnvVar").String(), | ||
| stringifyStringSliceFlag(f.(StringSliceFlag))) | ||
| } | ||
| placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) | ||
| needsPlaceholder := false | ||
| defaultValueString := "" | ||
| if val := fv.FieldByName("Value"); val.IsValid() { | ||
| needsPlaceholder = true | ||
| defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface()) | ||
| if val.Kind() == reflect.String && val.String() != "" { | ||
| defaultValueString = fmt.Sprintf(" (default: %q)", val.String()) | ||
| } | ||
| } | ||
| if defaultValueString == " (default: )" { | ||
| defaultValueString = "" | ||
| } | ||
| if needsPlaceholder && placeholder == "" { | ||
| placeholder = defaultPlaceholder | ||
| } | ||
| 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)) | ||
| } | ||
| func stringifyIntSliceFlag(f IntSliceFlag) string { | ||
| defaultVals := []string{} | ||
| if f.Value != nil && len(f.Value.Value()) > 0 { | ||
| for _, i := range f.Value.Value() { | ||
| defaultVals = append(defaultVals, fmt.Sprintf("%d", i)) | ||
| } | ||
| } | ||
| return stringifySliceFlag(f.Usage, f.Name, defaultVals) | ||
| } | ||
| func stringifyInt64SliceFlag(f Int64SliceFlag) string { | ||
| defaultVals := []string{} | ||
| if f.Value != nil && len(f.Value.Value()) > 0 { | ||
| for _, i := range f.Value.Value() { | ||
| defaultVals = append(defaultVals, fmt.Sprintf("%d", i)) | ||
| } | ||
| } | ||
| return stringifySliceFlag(f.Usage, f.Name, defaultVals) | ||
| } | ||
| func stringifyStringSliceFlag(f StringSliceFlag) string { | ||
| defaultVals := []string{} | ||
| if f.Value != nil && len(f.Value.Value()) > 0 { | ||
| for _, s := range f.Value.Value() { | ||
| if len(s) > 0 { | ||
| defaultVals = append(defaultVals, fmt.Sprintf("%q", s)) | ||
| } | ||
| } | ||
| } | ||
| return stringifySliceFlag(f.Usage, f.Name, defaultVals) | ||
| } | ||
| func stringifySliceFlag(usage, name string, defaultVals []string) string { | ||
| placeholder, usage := unquoteUsage(usage) | ||
| if placeholder == "" { | ||
| placeholder = defaultPlaceholder | ||
| } | ||
| defaultVal := "" | ||
| if len(defaultVals) > 0 { | ||
| defaultVal = fmt.Sprintf(" (default: %s)", strings.Join(defaultVals, ", ")) | ||
| } | ||
| usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) | ||
| return fmt.Sprintf("%s\t%s", prefixedNames(name, placeholder), usageWithDefault) | ||
| } |
+344
-2
@@ -5,2 +5,6 @@ package cli | ||
| "bytes" | ||
| "flag" | ||
| "fmt" | ||
| "runtime" | ||
| "strings" | ||
| "testing" | ||
@@ -70,6 +74,7 @@ ) | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Bool("h") != true { | ||
| t.Errorf("custom help flag not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -100,6 +105,7 @@ } | ||
| }, | ||
| Action: func(ctx *Context) { | ||
| Action: func(ctx *Context) error { | ||
| if ctx.Bool("v") != true { | ||
| t.Errorf("custom version flag not set") | ||
| } | ||
| return nil | ||
| }, | ||
@@ -114,1 +120,337 @@ } | ||
| } | ||
| func Test_helpCommand_Action_ErrorIfNoTopic(t *testing.T) { | ||
| app := NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| set.Parse([]string{"foo"}) | ||
| c := NewContext(app, set, nil) | ||
| err := helpCommand.Action.(func(*Context) error)(c) | ||
| if err == nil { | ||
| t.Fatalf("expected error from helpCommand.Action(), but got nil") | ||
| } | ||
| exitErr, ok := err.(*ExitError) | ||
| if !ok { | ||
| t.Fatalf("expected ExitError from helpCommand.Action(), but instead got: %v", err.Error()) | ||
| } | ||
| if !strings.HasPrefix(exitErr.Error(), "No help topic for") { | ||
| t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error()) | ||
| } | ||
| if exitErr.exitCode != 3 { | ||
| t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode) | ||
| } | ||
| } | ||
| func Test_helpCommand_InHelpOutput(t *testing.T) { | ||
| app := NewApp() | ||
| output := &bytes.Buffer{} | ||
| app.Writer = output | ||
| app.Run([]string{"test", "--help"}) | ||
| s := output.String() | ||
| if strings.Contains(s, "\nCOMMANDS:\nGLOBAL OPTIONS:\n") { | ||
| t.Fatalf("empty COMMANDS section detected: %q", s) | ||
| } | ||
| if !strings.Contains(s, "help, h") { | ||
| t.Fatalf("missing \"help, h\": %q", s) | ||
| } | ||
| } | ||
| func Test_helpSubcommand_Action_ErrorIfNoTopic(t *testing.T) { | ||
| app := NewApp() | ||
| set := flag.NewFlagSet("test", 0) | ||
| set.Parse([]string{"foo"}) | ||
| c := NewContext(app, set, nil) | ||
| err := helpSubcommand.Action.(func(*Context) error)(c) | ||
| if err == nil { | ||
| t.Fatalf("expected error from helpCommand.Action(), but got nil") | ||
| } | ||
| exitErr, ok := err.(*ExitError) | ||
| if !ok { | ||
| t.Fatalf("expected ExitError from helpCommand.Action(), but instead got: %v", err.Error()) | ||
| } | ||
| if !strings.HasPrefix(exitErr.Error(), "No help topic for") { | ||
| t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error()) | ||
| } | ||
| if exitErr.exitCode != 3 { | ||
| t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode) | ||
| } | ||
| } | ||
| func TestShowAppHelp_CommandAliases(t *testing.T) { | ||
| app := &App{ | ||
| Commands: []Command{ | ||
| { | ||
| Name: "frobbly", | ||
| Aliases: []string{"fr", "frob"}, | ||
| Action: func(ctx *Context) error { | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| output := &bytes.Buffer{} | ||
| app.Writer = output | ||
| app.Run([]string{"foo", "--help"}) | ||
| if !strings.Contains(output.String(), "frobbly, fr, frob") { | ||
| t.Errorf("expected output to include all command aliases; got: %q", output.String()) | ||
| } | ||
| } | ||
| func TestShowCommandHelp_CommandAliases(t *testing.T) { | ||
| app := &App{ | ||
| Commands: []Command{ | ||
| { | ||
| Name: "frobbly", | ||
| Aliases: []string{"fr", "frob", "bork"}, | ||
| Action: func(ctx *Context) error { | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| output := &bytes.Buffer{} | ||
| app.Writer = output | ||
| app.Run([]string{"foo", "help", "fr"}) | ||
| if !strings.Contains(output.String(), "frobbly") { | ||
| t.Errorf("expected output to include command name; got: %q", output.String()) | ||
| } | ||
| if strings.Contains(output.String(), "bork") { | ||
| t.Errorf("expected output to exclude command aliases; got: %q", output.String()) | ||
| } | ||
| } | ||
| func TestShowSubcommandHelp_CommandAliases(t *testing.T) { | ||
| app := &App{ | ||
| Commands: []Command{ | ||
| { | ||
| Name: "frobbly", | ||
| Aliases: []string{"fr", "frob", "bork"}, | ||
| Action: func(ctx *Context) error { | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| output := &bytes.Buffer{} | ||
| app.Writer = output | ||
| app.Run([]string{"foo", "help"}) | ||
| if !strings.Contains(output.String(), "frobbly, fr, frob, bork") { | ||
| t.Errorf("expected output to include all command aliases; got: %q", output.String()) | ||
| } | ||
| } | ||
| func TestShowCommandHelp_Customtemplate(t *testing.T) { | ||
| app := &App{ | ||
| Commands: []Command{ | ||
| { | ||
| Name: "frobbly", | ||
| Action: func(ctx *Context) error { | ||
| return nil | ||
| }, | ||
| HelpName: "foo frobbly", | ||
| CustomHelpTemplate: `NAME: | ||
| {{.HelpName}} - {{.Usage}} | ||
| USAGE: | ||
| {{.HelpName}} [FLAGS] TARGET [TARGET ...] | ||
| FLAGS: | ||
| {{range .VisibleFlags}}{{.}} | ||
| {{end}} | ||
| EXAMPLES: | ||
| 1. Frobbly runs with this param locally. | ||
| $ {{.HelpName}} wobbly | ||
| `, | ||
| }, | ||
| }, | ||
| } | ||
| output := &bytes.Buffer{} | ||
| app.Writer = output | ||
| app.Run([]string{"foo", "help", "frobbly"}) | ||
| if strings.Contains(output.String(), "2. Frobbly runs without this param locally.") { | ||
| t.Errorf("expected output to exclude \"2. Frobbly runs without this param locally.\"; got: %q", output.String()) | ||
| } | ||
| if !strings.Contains(output.String(), "1. Frobbly runs with this param locally.") { | ||
| t.Errorf("expected output to include \"1. Frobbly runs with this param locally.\"; got: %q", output.String()) | ||
| } | ||
| if !strings.Contains(output.String(), "$ foo frobbly wobbly") { | ||
| t.Errorf("expected output to include \"$ foo frobbly wobbly\"; got: %q", output.String()) | ||
| } | ||
| } | ||
| func TestShowSubcommandHelp_CommandUsageText(t *testing.T) { | ||
| app := &App{ | ||
| Commands: []Command{ | ||
| { | ||
| Name: "frobbly", | ||
| UsageText: "this is usage text", | ||
| }, | ||
| }, | ||
| } | ||
| output := &bytes.Buffer{} | ||
| app.Writer = output | ||
| app.Run([]string{"foo", "frobbly", "--help"}) | ||
| if !strings.Contains(output.String(), "this is usage text") { | ||
| t.Errorf("expected output to include usage text; got: %q", output.String()) | ||
| } | ||
| } | ||
| func TestShowSubcommandHelp_SubcommandUsageText(t *testing.T) { | ||
| app := &App{ | ||
| Commands: []Command{ | ||
| { | ||
| Name: "frobbly", | ||
| Subcommands: []Command{ | ||
| { | ||
| Name: "bobbly", | ||
| UsageText: "this is usage text", | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| output := &bytes.Buffer{} | ||
| app.Writer = output | ||
| app.Run([]string{"foo", "frobbly", "bobbly", "--help"}) | ||
| if !strings.Contains(output.String(), "this is usage text") { | ||
| t.Errorf("expected output to include usage text; got: %q", output.String()) | ||
| } | ||
| } | ||
| func TestShowAppHelp_HiddenCommand(t *testing.T) { | ||
| app := &App{ | ||
| Commands: []Command{ | ||
| { | ||
| Name: "frobbly", | ||
| Action: func(ctx *Context) error { | ||
| return nil | ||
| }, | ||
| }, | ||
| { | ||
| Name: "secretfrob", | ||
| Hidden: true, | ||
| Action: func(ctx *Context) error { | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| output := &bytes.Buffer{} | ||
| app.Writer = output | ||
| app.Run([]string{"app", "--help"}) | ||
| if strings.Contains(output.String(), "secretfrob") { | ||
| t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String()) | ||
| } | ||
| if !strings.Contains(output.String(), "frobbly") { | ||
| t.Errorf("expected output to include \"frobbly\"; got: %q", output.String()) | ||
| } | ||
| } | ||
| func TestShowAppHelp_CustomAppTemplate(t *testing.T) { | ||
| app := &App{ | ||
| Commands: []Command{ | ||
| { | ||
| Name: "frobbly", | ||
| Action: func(ctx *Context) error { | ||
| return nil | ||
| }, | ||
| }, | ||
| { | ||
| Name: "secretfrob", | ||
| Hidden: true, | ||
| Action: func(ctx *Context) error { | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| ExtraInfo: func() map[string]string { | ||
| platform := fmt.Sprintf("OS: %s | Arch: %s", runtime.GOOS, runtime.GOARCH) | ||
| goruntime := fmt.Sprintf("Version: %s | CPUs: %d", runtime.Version(), runtime.NumCPU()) | ||
| return map[string]string{ | ||
| "PLATFORM": platform, | ||
| "RUNTIME": goruntime, | ||
| } | ||
| }, | ||
| CustomAppHelpTemplate: `NAME: | ||
| {{.Name}} - {{.Usage}} | ||
| USAGE: | ||
| {{.Name}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}} [COMMAND FLAGS | -h]{{end}} [ARGUMENTS...] | ||
| COMMANDS: | ||
| {{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} | ||
| {{end}}{{if .VisibleFlags}} | ||
| GLOBAL FLAGS: | ||
| {{range .VisibleFlags}}{{.}} | ||
| {{end}}{{end}} | ||
| VERSION: | ||
| 2.0.0 | ||
| {{"\n"}}{{range $key, $value := ExtraInfo}} | ||
| {{$key}}: | ||
| {{$value}} | ||
| {{end}}`, | ||
| } | ||
| output := &bytes.Buffer{} | ||
| app.Writer = output | ||
| app.Run([]string{"app", "--help"}) | ||
| if strings.Contains(output.String(), "secretfrob") { | ||
| t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String()) | ||
| } | ||
| if !strings.Contains(output.String(), "frobbly") { | ||
| t.Errorf("expected output to include \"frobbly\"; got: %q", output.String()) | ||
| } | ||
| if !strings.Contains(output.String(), "PLATFORM:") || | ||
| !strings.Contains(output.String(), "OS:") || | ||
| !strings.Contains(output.String(), "Arch:") { | ||
| t.Errorf("expected output to include \"PLATFORM:, OS: and Arch:\"; got: %q", output.String()) | ||
| } | ||
| if !strings.Contains(output.String(), "RUNTIME:") || | ||
| !strings.Contains(output.String(), "Version:") || | ||
| !strings.Contains(output.String(), "CPUs:") { | ||
| t.Errorf("expected output to include \"RUNTIME:, Version: and CPUs:\"; got: %q", output.String()) | ||
| } | ||
| if !strings.Contains(output.String(), "VERSION:") || | ||
| !strings.Contains(output.String(), "2.0.0") { | ||
| t.Errorf("expected output to include \"VERSION:, 2.0.0\"; got: %q", output.String()) | ||
| } | ||
| } |
+158
-73
@@ -6,2 +6,3 @@ package cli | ||
| "io" | ||
| "os" | ||
| "strings" | ||
@@ -12,30 +13,34 @@ "text/tabwriter" | ||
| // The text template for the Default help topic. | ||
| // AppHelpTemplate is the text template for the Default help topic. | ||
| // cli.go uses text/template to render templates. You can | ||
| // render custom help text by setting this variable. | ||
| var AppHelpTemplate = `NAME: | ||
| {{.Name}} - {{.Usage}} | ||
| {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} | ||
| USAGE: | ||
| {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} | ||
| {{if .Version}}{{if not .HideVersion}} | ||
| {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} | ||
| VERSION: | ||
| {{.Version}} | ||
| {{end}}{{end}}{{if len .Authors}} | ||
| AUTHOR(S): | ||
| {{range .Authors}}{{ . }}{{end}} | ||
| {{end}}{{if .Commands}} | ||
| COMMANDS:{{range .Categories}}{{if .Name}} | ||
| {{.Name}}{{ ":" }}{{end}}{{range .Commands}} | ||
| {{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}{{end}} | ||
| {{end}}{{end}}{{if .Flags}} | ||
| {{.Version}}{{end}}{{end}}{{if .Description}} | ||
| DESCRIPTION: | ||
| {{.Description}}{{end}}{{if len .Authors}} | ||
| AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: | ||
| {{range $index, $author := .Authors}}{{if $index}} | ||
| {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} | ||
| COMMANDS:{{range .VisibleCategories}}{{if .Name}} | ||
| {{.Name}}:{{end}}{{range .VisibleCommands}} | ||
| {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} | ||
| GLOBAL OPTIONS: | ||
| {{range .Flags}}{{.}} | ||
| {{end}}{{end}}{{if .Copyright }} | ||
| {{range $index, $option := .VisibleFlags}}{{if $index}} | ||
| {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} | ||
| COPYRIGHT: | ||
| {{.Copyright}} | ||
| {{end}} | ||
| {{.Copyright}}{{end}} | ||
| ` | ||
| // The text template for the command help topic. | ||
| // CommandHelpTemplate is the text template for the command help topic. | ||
| // cli.go uses text/template to render templates. You can | ||
@@ -47,3 +52,3 @@ // render custom help text by setting this variable. | ||
| USAGE: | ||
| {{.HelpName}}{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}} | ||
| {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} | ||
@@ -54,24 +59,24 @@ CATEGORY: | ||
| DESCRIPTION: | ||
| {{.Description}}{{end}}{{if .Flags}} | ||
| {{.Description}}{{end}}{{if .VisibleFlags}} | ||
| OPTIONS: | ||
| {{range .Flags}}{{.}} | ||
| {{end}}{{ end }} | ||
| {{range .VisibleFlags}}{{.}} | ||
| {{end}}{{end}} | ||
| ` | ||
| // The text template for the subcommand help topic. | ||
| // SubcommandHelpTemplate is the text template for the subcommand help topic. | ||
| // cli.go uses text/template to render templates. You can | ||
| // render custom help text by setting this variable. | ||
| var SubcommandHelpTemplate = `NAME: | ||
| {{.HelpName}} - {{.Usage}} | ||
| {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} | ||
| USAGE: | ||
| {{.HelpName}} command{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} | ||
| {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} | ||
| COMMANDS:{{range .Categories}}{{if .Name}} | ||
| {{.Name}}{{ ":" }}{{end}}{{range .Commands}} | ||
| {{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}{{end}} | ||
| {{end}}{{if .Flags}} | ||
| COMMANDS:{{range .VisibleCategories}}{{if .Name}} | ||
| {{.Name}}:{{end}}{{range .VisibleCommands}} | ||
| {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} | ||
| {{end}}{{if .VisibleFlags}} | ||
| OPTIONS: | ||
| {{range .Flags}}{{.}} | ||
| {{range .VisibleFlags}}{{.}} | ||
| {{end}}{{end}} | ||
@@ -85,9 +90,10 @@ ` | ||
| ArgsUsage: "[command]", | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| args := c.Args() | ||
| if args.Present() { | ||
| ShowCommandHelp(c, args.First()) | ||
| } else { | ||
| ShowAppHelp(c) | ||
| return ShowCommandHelp(c, args.First()) | ||
| } | ||
| ShowAppHelp(c) | ||
| return nil | ||
| }, | ||
@@ -101,9 +107,9 @@ } | ||
| ArgsUsage: "[command]", | ||
| Action: func(c *Context) { | ||
| Action: func(c *Context) error { | ||
| args := c.Args() | ||
| if args.Present() { | ||
| ShowCommandHelp(c, args.First()) | ||
| } else { | ||
| ShowSubcommandHelp(c) | ||
| return ShowCommandHelp(c, args.First()) | ||
| } | ||
| return ShowSubcommandHelp(c) | ||
| }, | ||
@@ -115,14 +121,47 @@ } | ||
| // Prints help for the App or Command with custom template function. | ||
| type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{}) | ||
| // HelpPrinter is a function that writes the help output. If not set a default | ||
| // is used. The function signature is: | ||
| // func(w io.Writer, templ string, data interface{}) | ||
| var HelpPrinter helpPrinter = printHelp | ||
| // Prints version for the App | ||
| // HelpPrinterCustom is same as HelpPrinter but | ||
| // takes a custom function for template function map. | ||
| var HelpPrinterCustom helpPrinterCustom = printHelpCustom | ||
| // VersionPrinter prints the version for the App | ||
| var VersionPrinter = printVersion | ||
| func ShowAppHelp(c *Context) { | ||
| HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) | ||
| // ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code. | ||
| func ShowAppHelpAndExit(c *Context, exitCode int) { | ||
| ShowAppHelp(c) | ||
| os.Exit(exitCode) | ||
| } | ||
| // Prints the list of subcommands as the default app completion method | ||
| // ShowAppHelp is an action that displays the help. | ||
| func ShowAppHelp(c *Context) (err error) { | ||
| if c.App.CustomAppHelpTemplate == "" { | ||
| HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) | ||
| return | ||
| } | ||
| customAppData := func() map[string]interface{} { | ||
| if c.App.ExtraInfo == nil { | ||
| return nil | ||
| } | ||
| return map[string]interface{}{ | ||
| "ExtraInfo": c.App.ExtraInfo, | ||
| } | ||
| } | ||
| HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData()) | ||
| return nil | ||
| } | ||
| // DefaultAppComplete prints the list of subcommands as the default app completion method | ||
| func DefaultAppComplete(c *Context) { | ||
| for _, command := range c.App.Commands { | ||
| if command.Hidden { | ||
| continue | ||
| } | ||
| for _, name := range command.Names() { | ||
@@ -134,8 +173,14 @@ fmt.Fprintln(c.App.Writer, name) | ||
| // Prints help for the given command | ||
| func ShowCommandHelp(ctx *Context, command string) { | ||
| // ShowCommandHelpAndExit - exits with code after showing help | ||
| func ShowCommandHelpAndExit(c *Context, command string, code int) { | ||
| ShowCommandHelp(c, command) | ||
| os.Exit(code) | ||
| } | ||
| // ShowCommandHelp prints help for the given command | ||
| func ShowCommandHelp(ctx *Context, command string) error { | ||
| // show the subcommand help for a command with subcommands | ||
| if command == "" { | ||
| HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) | ||
| return | ||
| return nil | ||
| } | ||
@@ -145,20 +190,25 @@ | ||
| if c.HasName(command) { | ||
| HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) | ||
| return | ||
| if c.CustomHelpTemplate != "" { | ||
| HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil) | ||
| } else { | ||
| HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) | ||
| } | ||
| return nil | ||
| } | ||
| } | ||
| if ctx.App.CommandNotFound != nil { | ||
| ctx.App.CommandNotFound(ctx, command) | ||
| } else { | ||
| fmt.Fprintf(ctx.App.Writer, "No help topic for '%v'\n", command) | ||
| if ctx.App.CommandNotFound == nil { | ||
| return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3) | ||
| } | ||
| ctx.App.CommandNotFound(ctx, command) | ||
| return nil | ||
| } | ||
| // Prints help for the given subcommand | ||
| func ShowSubcommandHelp(c *Context) { | ||
| ShowCommandHelp(c, c.Command.Name) | ||
| // ShowSubcommandHelp prints help for the given subcommand | ||
| func ShowSubcommandHelp(c *Context) error { | ||
| return ShowCommandHelp(c, c.Command.Name) | ||
| } | ||
| // Prints the version number of the App | ||
| // ShowVersion prints the version number of the App | ||
| func ShowVersion(c *Context) { | ||
@@ -172,3 +222,3 @@ VersionPrinter(c) | ||
| // Prints the lists of commands within a given context | ||
| // ShowCompletions prints the lists of commands within a given context | ||
| func ShowCompletions(c *Context) { | ||
@@ -181,3 +231,3 @@ a := c.App | ||
| // Prints the custom completions for a given command | ||
| // ShowCommandCompletions prints the custom completions for a given command | ||
| func ShowCommandCompletions(ctx *Context, command string) { | ||
@@ -190,8 +240,13 @@ c := ctx.App.Command(command) | ||
| func printHelp(out io.Writer, templ string, data interface{}) { | ||
| func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) { | ||
| funcMap := template.FuncMap{ | ||
| "join": strings.Join, | ||
| } | ||
| if customFunc != nil { | ||
| for key, value := range customFunc { | ||
| funcMap[key] = value | ||
| } | ||
| } | ||
| w := tabwriter.NewWriter(out, 0, 8, 1, '\t', 0) | ||
| w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) | ||
| t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) | ||
@@ -201,3 +256,6 @@ err := t.Execute(w, data) | ||
| // If the writer is closed, t.Execute will fail, and there's nothing | ||
| // we can do to recover. We could send this to os.Stderr if we need. | ||
| // we can do to recover. | ||
| if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { | ||
| fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) | ||
| } | ||
| return | ||
@@ -208,6 +266,10 @@ } | ||
| func printHelp(out io.Writer, templ string, data interface{}) { | ||
| printHelpCustom(out, templ, data, nil) | ||
| } | ||
| func checkVersion(c *Context) bool { | ||
| found := false | ||
| if VersionFlag.Name != "" { | ||
| eachName(VersionFlag.Name, func(name string) { | ||
| if VersionFlag.GetName() != "" { | ||
| eachName(VersionFlag.GetName(), func(name string) { | ||
| if c.GlobalBool(name) || c.Bool(name) { | ||
@@ -223,4 +285,4 @@ found = true | ||
| found := false | ||
| if HelpFlag.Name != "" { | ||
| eachName(HelpFlag.Name, func(name string) { | ||
| if HelpFlag.GetName() != "" { | ||
| eachName(HelpFlag.GetName(), func(name string) { | ||
| if c.GlobalBool(name) || c.Bool(name) { | ||
@@ -244,3 +306,3 @@ found = true | ||
| func checkSubcommandHelp(c *Context) bool { | ||
| if c.GlobalBool("h") || c.GlobalBool("help") { | ||
| if c.Bool("h") || c.Bool("help") { | ||
| ShowSubcommandHelp(c) | ||
@@ -253,18 +315,41 @@ return true | ||
| func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { | ||
| if !a.EnableBashCompletion { | ||
| return false, arguments | ||
| } | ||
| pos := len(arguments) - 1 | ||
| lastArg := arguments[pos] | ||
| if lastArg != "--"+BashCompletionFlag.GetName() { | ||
| return false, arguments | ||
| } | ||
| return true, arguments[:pos] | ||
| } | ||
| func checkCompletions(c *Context) bool { | ||
| if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion { | ||
| ShowCompletions(c) | ||
| return true | ||
| if !c.shellComplete { | ||
| return false | ||
| } | ||
| return false | ||
| if args := c.Args(); args.Present() { | ||
| name := args.First() | ||
| if cmd := c.App.Command(name); cmd != nil { | ||
| // let the command handle the completion | ||
| return false | ||
| } | ||
| } | ||
| ShowCompletions(c) | ||
| return true | ||
| } | ||
| func checkCommandCompletions(c *Context, name string) bool { | ||
| if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { | ||
| ShowCommandCompletions(c, name) | ||
| return true | ||
| if !c.shellComplete { | ||
| return false | ||
| } | ||
| return false | ||
| ShowCommandCompletions(c, name) | ||
| return true | ||
| } |
+11
-2
| package cli | ||
| import ( | ||
| "os" | ||
| "reflect" | ||
| "runtime" | ||
| "strings" | ||
| "testing" | ||
| ) | ||
| /* Test Helpers */ | ||
| var ( | ||
| wd, _ = os.Getwd() | ||
| ) | ||
| func expect(t *testing.T, a interface{}, b interface{}) { | ||
| _, fn, line, _ := runtime.Caller(1) | ||
| fn = strings.Replace(fn, wd+"/", "", -1) | ||
| if !reflect.DeepEqual(a, b) { | ||
| t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) | ||
| t.Errorf("(%s:%d) Expected %v (type %v) - Got %v (type %v)", fn, line, b, reflect.TypeOf(b), a, reflect.TypeOf(a)) | ||
| } | ||
@@ -13,0 +22,0 @@ } |
+14
-14
@@ -1,12 +0,11 @@ | ||
| Copyright (C) 2013 Jeremy Saenz | ||
| All Rights Reserved. | ||
| MIT License | ||
| MIT LICENSE | ||
| Copyright (c) 2016 Jeremy Saenz & Contributors | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| this software and associated documentation files (the "Software"), to deal in | ||
| the Software without restriction, including without limitation the rights to | ||
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
| the Software, and to permit persons to whom the Software is furnished to do so, | ||
| subject to the following conditions: | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
@@ -17,6 +16,7 @@ The above copyright notice and this permission notice shall be included in all | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
| FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
| COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
+1119
-183
| # CLI [](https://godoc.org/github.com/micro/cli) | ||
| CLI is a fork of `codegangsta/cli`. We use it to simplify flag parsing. | ||
| CLI is a fork of `urfave/cli`. We use it to simplify flag parsing. | ||
| <!-- toc --> | ||
| - [Overview](#overview) | ||
| - [Installation](#installation) | ||
| * [Supported platforms](#supported-platforms) | ||
| * [Using the `v2` branch](#using-the-v2-branch) | ||
| * [Pinning to the `v1` releases](#pinning-to-the-v1-releases) | ||
| - [Getting Started](#getting-started) | ||
| - [Examples](#examples) | ||
| * [Arguments](#arguments) | ||
| * [Flags](#flags) | ||
| + [Placeholder Values](#placeholder-values) | ||
| + [Alternate Names](#alternate-names) | ||
| + [Ordering](#ordering) | ||
| + [Values from the Environment](#values-from-the-environment) | ||
| + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others) | ||
| * [Subcommands](#subcommands) | ||
| * [Subcommands categories](#subcommands-categories) | ||
| * [Exit code](#exit-code) | ||
| * [Bash Completion](#bash-completion) | ||
| + [Enabling](#enabling) | ||
| + [Distribution](#distribution) | ||
| + [Customization](#customization) | ||
| * [Generated Help Text](#generated-help-text) | ||
| + [Customization](#customization-1) | ||
| * [Version Flag](#version-flag) | ||
| + [Customization](#customization-2) | ||
| + [Full API Example](#full-api-example) | ||
| - [Contribution Guidelines](#contribution-guidelines) | ||
| <!-- tocstop --> | ||
| ## Overview | ||
| Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app. | ||
| Command line apps are usually so tiny that there is absolutely no reason why | ||
| your code should *not* be self-documenting. Things like generating help text and | ||
| parsing command flags/options should not hinder productivity when writing a | ||
| command line app. | ||
| **This is where `cli.go` comes into play.** `cli.go` makes command line programming fun, organized, and expressive! | ||
| **This is where cli comes into play.** cli makes command line programming fun, | ||
| organized, and expressive! | ||
| ## Installation | ||
| Make sure you have a working Go environment (go 1.1+ is *required*). [See the install instructions](http://golang.org/doc/install.html). | ||
| Make sure you have a working Go environment. Go version 1.2+ is supported. [See | ||
| the install instructions for Go](http://golang.org/doc/install.html). | ||
| To install `cli.go`, simply run: | ||
| To install cli, simply run: | ||
| ``` | ||
@@ -20,3 +57,4 @@ $ go get github.com/micro/cli | ||
| Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used: | ||
| Make sure your `PATH` includes the `$GOPATH/bin` directory so your commands can | ||
| be easily used: | ||
| ``` | ||
@@ -26,6 +64,61 @@ export PATH=$PATH:$GOPATH/bin | ||
| ### Supported platforms | ||
| cli is tested against multiple versions of Go on Linux, and against the latest | ||
| released version of Go on OS X and Windows. For full details, see | ||
| [`./.travis.yml`](./.travis.yml) and [`./appveyor.yml`](./appveyor.yml). | ||
| ### Using the `v2` branch | ||
| **Warning**: The `v2` branch is currently unreleased and considered unstable. | ||
| There is currently a long-lived branch named `v2` that is intended to land as | ||
| the new `master` branch once development there has settled down. The current | ||
| `master` branch (mirrored as `v1`) is being manually merged into `v2` on | ||
| an irregular human-based schedule, but generally if one wants to "upgrade" to | ||
| `v2` *now* and accept the volatility (read: "awesomeness") that comes along with | ||
| that, please use whatever version pinning of your preference, such as via | ||
| `gopkg.in`: | ||
| ``` | ||
| $ go get gopkg.in/urfave/cli.v2 | ||
| ``` | ||
| ``` go | ||
| ... | ||
| import ( | ||
| "gopkg.in/urfave/cli.v2" // imports as package "cli" | ||
| ) | ||
| ... | ||
| ``` | ||
| ### Pinning to the `v1` releases | ||
| Similarly to the section above describing use of the `v2` branch, if one wants | ||
| to avoid any unexpected compatibility pains once `v2` becomes `master`, then | ||
| pinning to `v1` is an acceptable option, e.g.: | ||
| ``` | ||
| $ go get gopkg.in/urfave/cli.v1 | ||
| ``` | ||
| ``` go | ||
| ... | ||
| import ( | ||
| "gopkg.in/urfave/cli.v1" // imports as package "cli" | ||
| ) | ||
| ... | ||
| ``` | ||
| This will pull the latest tagged `v1` release (e.g. `v1.18.1` at the time of writing). | ||
| ## Getting Started | ||
| One of the philosophies behind `cli.go` is that an API should be playful and full of discovery. So a `cli.go` app can be as little as one line of code in `main()`. | ||
| One of the philosophies behind cli is that an API should be playful and full of | ||
| discovery. So a cli app can be as little as one line of code in `main()`. | ||
| <!-- { | ||
| "args": ["--help"], | ||
| "output": "A new cli application" | ||
| } --> | ||
| ``` go | ||
@@ -44,4 +137,8 @@ package main | ||
| This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation: | ||
| This app will run and show help text, but is not very useful. Let's give an | ||
| action to execute and some help documentation: | ||
| <!-- { | ||
| "output": "boom! I say!" | ||
| } --> | ||
| ``` go | ||
@@ -51,3 +148,5 @@ package main | ||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "github.com/micro/cli" | ||
@@ -60,4 +159,5 @@ ) | ||
| app.Usage = "make an explosive entrance" | ||
| app.Action = func(c *cli.Context) { | ||
| println("boom! I say!") | ||
| app.Action = func(c *cli.Context) error { | ||
| fmt.Println("boom! I say!") | ||
| return nil | ||
| } | ||
@@ -69,10 +169,17 @@ | ||
| Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below. | ||
| Running this already gives you a ton of functionality, plus support for things | ||
| like subcommands and flags, which are covered below. | ||
| ## Example | ||
| ## Examples | ||
| Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness! | ||
| Being a programmer can be a lonely job. Thankfully by the power of automation | ||
| that is not the case! Let's create a greeter app to fend off our demons of | ||
| loneliness! | ||
| Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it: | ||
| Start by creating a directory named `greet`, and within it, add a file, | ||
| `greet.go` with the following code in it: | ||
| <!-- { | ||
| "output": "Hello friend!" | ||
| } --> | ||
| ``` go | ||
@@ -82,3 +189,5 @@ package main | ||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "github.com/micro/cli" | ||
@@ -91,4 +200,5 @@ ) | ||
| app.Usage = "fight the loneliness!" | ||
| app.Action = func(c *cli.Context) { | ||
| println("Hello friend!") | ||
| app.Action = func(c *cli.Context) error { | ||
| fmt.Println("Hello friend!") | ||
| return nil | ||
| } | ||
@@ -113,3 +223,3 @@ | ||
| `cli.go` also generates neat help text: | ||
| cli also generates neat help text: | ||
@@ -131,3 +241,3 @@ ``` | ||
| GLOBAL OPTIONS | ||
| --version Shows version information | ||
| --version Shows version information | ||
| ``` | ||
@@ -137,10 +247,27 @@ | ||
| You can lookup arguments by calling the `Args` function on `cli.Context`. | ||
| You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.: | ||
| <!-- { | ||
| "output": "Hello \"" | ||
| } --> | ||
| ``` go | ||
| ... | ||
| app.Action = func(c *cli.Context) { | ||
| println("Hello", c.Args()[0]) | ||
| package main | ||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| app := cli.NewApp() | ||
| app.Action = func(c *cli.Context) error { | ||
| fmt.Printf("Hello %q", c.Args().Get(0)) | ||
| return nil | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ... | ||
| ``` | ||
@@ -152,50 +279,88 @@ | ||
| <!-- { | ||
| "output": "Hello Nefertiti" | ||
| } --> | ||
| ``` go | ||
| ... | ||
| app.Flags = []cli.Flag { | ||
| cli.StringFlag{ | ||
| Name: "lang", | ||
| Value: "english", | ||
| Usage: "language for the greeting", | ||
| }, | ||
| } | ||
| app.Action = func(c *cli.Context) { | ||
| name := "someone" | ||
| if c.NArg() > 0 { | ||
| name = c.Args()[0] | ||
| package main | ||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| app := cli.NewApp() | ||
| app.Flags = []cli.Flag { | ||
| cli.StringFlag{ | ||
| Name: "lang", | ||
| Value: "english", | ||
| Usage: "language for the greeting", | ||
| }, | ||
| } | ||
| if c.String("lang") == "spanish" { | ||
| println("Hola", name) | ||
| } else { | ||
| println("Hello", name) | ||
| app.Action = func(c *cli.Context) error { | ||
| name := "Nefertiti" | ||
| if c.NArg() > 0 { | ||
| name = c.Args().Get(0) | ||
| } | ||
| if c.String("lang") == "spanish" { | ||
| fmt.Println("Hola", name) | ||
| } else { | ||
| fmt.Println("Hello", name) | ||
| } | ||
| return nil | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ... | ||
| ``` | ||
| You can also set a destination variable for a flag, to which the content will be scanned. | ||
| You can also set a destination variable for a flag, to which the content will be | ||
| scanned. | ||
| <!-- { | ||
| "output": "Hello someone" | ||
| } --> | ||
| ``` go | ||
| ... | ||
| var language string | ||
| app.Flags = []cli.Flag { | ||
| cli.StringFlag{ | ||
| Name: "lang", | ||
| Value: "english", | ||
| Usage: "language for the greeting", | ||
| Destination: &language, | ||
| }, | ||
| } | ||
| app.Action = func(c *cli.Context) { | ||
| name := "someone" | ||
| if c.NArg() > 0 { | ||
| name = c.Args()[0] | ||
| package main | ||
| import ( | ||
| "os" | ||
| "fmt" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| var language string | ||
| app := cli.NewApp() | ||
| app.Flags = []cli.Flag { | ||
| cli.StringFlag{ | ||
| Name: "lang", | ||
| Value: "english", | ||
| Usage: "language for the greeting", | ||
| Destination: &language, | ||
| }, | ||
| } | ||
| if language == "spanish" { | ||
| println("Hola", name) | ||
| } else { | ||
| println("Hello", name) | ||
| app.Action = func(c *cli.Context) error { | ||
| name := "someone" | ||
| if c.NArg() > 0 { | ||
| name = c.Args()[0] | ||
| } | ||
| if language == "spanish" { | ||
| fmt.Println("Hola", name) | ||
| } else { | ||
| fmt.Println("Hello", name) | ||
| } | ||
| return nil | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ... | ||
| ``` | ||
@@ -205,18 +370,152 @@ | ||
| #### Placeholder Values | ||
| Sometimes it's useful to specify a flag's value within the usage string itself. | ||
| Such placeholders are indicated with back quotes. | ||
| For example this: | ||
| <!-- { | ||
| "args": ["--help"], | ||
| "output": "--config FILE, -c FILE" | ||
| } --> | ||
| ```go | ||
| package main | ||
| import ( | ||
| "os" | ||
| "github.com/micro/cli" | ||
| ) | ||
| func main() { | ||
| app := cli.NewApp() | ||
| app.Flags = []cli.Flag{ | ||
| cli.StringFlag{ | ||
| Name: "config, c", | ||
| Usage: "Load configuration from `FILE`", | ||
| }, | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ``` | ||
| Will result in help output like: | ||
| ``` | ||
| --config FILE, -c FILE Load configuration from FILE | ||
| ``` | ||
| Note that only the first placeholder is used. Subsequent back-quoted words will | ||
| be left as-is. | ||
| #### Alternate Names | ||
| You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g. | ||
| You can set alternate (or short) names for flags by providing a comma-delimited | ||
| list for the `Name`. e.g. | ||
| <!-- { | ||
| "args": ["--help"], | ||
| "output": "--lang value, -l value.*language for the greeting.*default: \"english\"" | ||
| } --> | ||
| ``` go | ||
| app.Flags = []cli.Flag { | ||
| cli.StringFlag{ | ||
| Name: "lang, l", | ||
| Value: "english", | ||
| Usage: "language for the greeting", | ||
| }, | ||
| package main | ||
| import ( | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| app := cli.NewApp() | ||
| app.Flags = []cli.Flag { | ||
| cli.StringFlag{ | ||
| Name: "lang, l", | ||
| Value: "english", | ||
| Usage: "language for the greeting", | ||
| }, | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ``` | ||
| That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error. | ||
| That flag can then be set with `--lang spanish` or `-l spanish`. Note that | ||
| giving two different forms of the same flag in the same command invocation is an | ||
| error. | ||
| #### Ordering | ||
| Flags for the application and commands are shown in the order they are defined. | ||
| However, it's possible to sort them from outside this library by using `FlagsByName` | ||
| or `CommandsByName` with `sort`. | ||
| For example this: | ||
| <!-- { | ||
| "args": ["--help"], | ||
| "output": "add a task to the list\n.*complete a task on the list\n.*\n\n.*\n.*Load configuration from FILE\n.*Language for the greeting.*" | ||
| } --> | ||
| ``` go | ||
| package main | ||
| import ( | ||
| "os" | ||
| "sort" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| app := cli.NewApp() | ||
| app.Flags = []cli.Flag { | ||
| cli.StringFlag{ | ||
| Name: "lang, l", | ||
| Value: "english", | ||
| Usage: "Language for the greeting", | ||
| }, | ||
| cli.StringFlag{ | ||
| Name: "config, c", | ||
| Usage: "Load configuration from `FILE`", | ||
| }, | ||
| } | ||
| app.Commands = []cli.Command{ | ||
| { | ||
| Name: "complete", | ||
| Aliases: []string{"c"}, | ||
| Usage: "complete a task on the list", | ||
| Action: func(c *cli.Context) error { | ||
| return nil | ||
| }, | ||
| }, | ||
| { | ||
| Name: "add", | ||
| Aliases: []string{"a"}, | ||
| Usage: "add a task to the list", | ||
| Action: func(c *cli.Context) error { | ||
| return nil | ||
| }, | ||
| }, | ||
| } | ||
| sort.Sort(cli.FlagsByName(app.Flags)) | ||
| sort.Sort(cli.CommandsByName(app.Commands)) | ||
| app.Run(os.Args) | ||
| } | ||
| ``` | ||
| Will result in help output like: | ||
| ``` | ||
| --config FILE, -c FILE Load configuration from FILE | ||
| --lang value, -l value Language for the greeting (default: "english") | ||
| ``` | ||
| #### Values from the Environment | ||
@@ -226,32 +525,75 @@ | ||
| <!-- { | ||
| "args": ["--help"], | ||
| "output": "language for the greeting.*APP_LANG" | ||
| } --> | ||
| ``` go | ||
| app.Flags = []cli.Flag { | ||
| cli.StringFlag{ | ||
| Name: "lang, l", | ||
| Value: "english", | ||
| Usage: "language for the greeting", | ||
| EnvVar: "APP_LANG", | ||
| }, | ||
| package main | ||
| import ( | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| app := cli.NewApp() | ||
| app.Flags = []cli.Flag { | ||
| cli.StringFlag{ | ||
| Name: "lang, l", | ||
| Value: "english", | ||
| Usage: "language for the greeting", | ||
| EnvVar: "APP_LANG", | ||
| }, | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ``` | ||
| The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default. | ||
| The `EnvVar` may also be given as a comma-delimited "cascade", where the first | ||
| environment variable that resolves is used as the default. | ||
| <!-- { | ||
| "args": ["--help"], | ||
| "output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG" | ||
| } --> | ||
| ``` go | ||
| app.Flags = []cli.Flag { | ||
| cli.StringFlag{ | ||
| Name: "lang, l", | ||
| Value: "english", | ||
| Usage: "language for the greeting", | ||
| EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG", | ||
| }, | ||
| package main | ||
| import ( | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| app := cli.NewApp() | ||
| app.Flags = []cli.Flag { | ||
| cli.StringFlag{ | ||
| Name: "lang, l", | ||
| Value: "english", | ||
| Usage: "language for the greeting", | ||
| EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG", | ||
| }, | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ``` | ||
| #### Values from alternate input sources (YAML and others) | ||
| #### Values from alternate input sources (YAML, TOML, and others) | ||
| There is a separate package altsrc that adds support for getting flag values from other input sources like YAML. | ||
| There is a separate package altsrc that adds support for getting flag values | ||
| from other file input sources. | ||
| 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: | ||
| Currently supported input source formats: | ||
| * YAML | ||
| * TOML | ||
| 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 | ||
@@ -261,3 +603,4 @@ 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. | ||
| Initialization must also occur for these flags. Below is an example initializing | ||
| getting data from a yaml file below. | ||
@@ -268,7 +611,10 @@ ``` go | ||
| The code above will use the "load" string as a flag name to get the file name of a yaml file from the cli.Context. | ||
| It will then use that file name to initialize the yaml input source for any flags that are defined on that command. | ||
| As a note the "load" flag used would also have to be defined on the command flags in order for this code snipped to work. | ||
| The code above will use the "load" string as a flag name to get the file name of | ||
| a yaml file from the cli.Context. It will then use that file name to initialize | ||
| the yaml input source for any flags that are defined on that command. As a note | ||
| the "load" flag used would also have to be defined on the command flags in order | ||
| for this code snipped to work. | ||
| Currently only YAML files are supported but developers can add support for other input sources by implementing the | ||
| Currently only the aboved specified formats are supported but developers can | ||
| add support for other input sources by implementing the | ||
| altsrc.InputSourceContext for their given sources. | ||
@@ -278,17 +624,35 @@ | ||
| <!-- { | ||
| "args": ["test-cmd", "--help"], | ||
| "output": "--test value.*default: 0" | ||
| } --> | ||
| ``` go | ||
| command := &cli.Command{ | ||
| Name: "test-cmd", | ||
| Aliases: []string{"tc"}, | ||
| Usage: "this is for testing", | ||
| Description: "testing", | ||
| Action: func(c *cli.Context) { | ||
| // Action to run | ||
| }, | ||
| Flags: []cli.Flag{ | ||
| NewIntFlag(cli.IntFlag{Name: "test"}), | ||
| cli.StringFlag{Name: "load"}}, | ||
| } | ||
| command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) | ||
| err := command.Run(c) | ||
| package notmain | ||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| "github.com/urfave/cli/altsrc" | ||
| ) | ||
| func main() { | ||
| app := cli.NewApp() | ||
| flags := []cli.Flag{ | ||
| altsrc.NewIntFlag(cli.IntFlag{Name: "test"}), | ||
| cli.StringFlag{Name: "load"}, | ||
| } | ||
| app.Action = func(c *cli.Context) error { | ||
| fmt.Println("yaml ist rad") | ||
| return nil | ||
| } | ||
| app.Before = altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load")) | ||
| app.Flags = flags | ||
| app.Run(os.Args) | ||
| } | ||
| ``` | ||
@@ -300,44 +664,65 @@ | ||
| <!-- { | ||
| "args": ["template", "add"], | ||
| "output": "new task template: .+" | ||
| } --> | ||
| ```go | ||
| ... | ||
| app.Commands = []cli.Command{ | ||
| { | ||
| Name: "add", | ||
| Aliases: []string{"a"}, | ||
| Usage: "add a task to the list", | ||
| Action: func(c *cli.Context) { | ||
| println("added task: ", c.Args().First()) | ||
| package main | ||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| app := cli.NewApp() | ||
| app.Commands = []cli.Command{ | ||
| { | ||
| Name: "add", | ||
| Aliases: []string{"a"}, | ||
| Usage: "add a task to the list", | ||
| Action: func(c *cli.Context) error { | ||
| fmt.Println("added task: ", c.Args().First()) | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| Name: "complete", | ||
| Aliases: []string{"c"}, | ||
| Usage: "complete a task on the list", | ||
| Action: func(c *cli.Context) { | ||
| println("completed task: ", c.Args().First()) | ||
| { | ||
| Name: "complete", | ||
| Aliases: []string{"c"}, | ||
| Usage: "complete a task on the list", | ||
| Action: func(c *cli.Context) error { | ||
| fmt.Println("completed task: ", c.Args().First()) | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| Name: "template", | ||
| Aliases: []string{"r"}, | ||
| Usage: "options for task templates", | ||
| Subcommands: []cli.Command{ | ||
| { | ||
| Name: "add", | ||
| Usage: "add a new template", | ||
| Action: func(c *cli.Context) { | ||
| println("new task template: ", c.Args().First()) | ||
| { | ||
| Name: "template", | ||
| Aliases: []string{"t"}, | ||
| Usage: "options for task templates", | ||
| Subcommands: []cli.Command{ | ||
| { | ||
| Name: "add", | ||
| Usage: "add a new template", | ||
| Action: func(c *cli.Context) error { | ||
| fmt.Println("new task template: ", c.Args().First()) | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| Name: "remove", | ||
| Usage: "remove an existing template", | ||
| Action: func(c *cli.Context) { | ||
| println("removed task template: ", c.Args().First()) | ||
| { | ||
| Name: "remove", | ||
| Usage: "remove an existing template", | ||
| Action: func(c *cli.Context) error { | ||
| fmt.Println("removed task template: ", c.Args().First()) | ||
| return nil | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ... | ||
| ``` | ||
@@ -354,17 +739,29 @@ | ||
| ```go | ||
| ... | ||
| app.Commands = []cli.Command{ | ||
| { | ||
| Name: "noop", | ||
| }, | ||
| { | ||
| Name: "add", | ||
| Category: "template", | ||
| }, | ||
| { | ||
| Name: "remove", | ||
| Category: "template", | ||
| }, | ||
| } | ||
| ... | ||
| package main | ||
| import ( | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| app := cli.NewApp() | ||
| app.Commands = []cli.Command{ | ||
| { | ||
| Name: "noop", | ||
| }, | ||
| { | ||
| Name: "add", | ||
| Category: "template", | ||
| }, | ||
| { | ||
| Name: "remove", | ||
| Category: "template", | ||
| }, | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ``` | ||
@@ -375,3 +772,2 @@ | ||
| ``` | ||
| ... | ||
| COMMANDS: | ||
@@ -383,5 +779,39 @@ noop | ||
| remove | ||
| ... | ||
| ``` | ||
| ### Exit code | ||
| Calling `App.Run` will not automatically call `os.Exit`, which means that by | ||
| default the exit code will "fall through" to being `0`. An explicit exit code | ||
| may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a | ||
| `cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.: | ||
| ``` go | ||
| package main | ||
| import ( | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| app := cli.NewApp() | ||
| app.Flags = []cli.Flag{ | ||
| cli.BoolTFlag{ | ||
| Name: "ginger-crouton", | ||
| Usage: "is it in the soup?", | ||
| }, | ||
| } | ||
| app.Action = func(ctx *cli.Context) error { | ||
| if !ctx.Bool("ginger-crouton") { | ||
| return cli.NewExitError("it is not in the soup", 86) | ||
| } | ||
| return nil | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ``` | ||
| ### Bash Completion | ||
@@ -394,30 +824,47 @@ | ||
| ```go | ||
| ... | ||
| var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"} | ||
| app := cli.NewApp() | ||
| app.EnableBashCompletion = true | ||
| app.Commands = []cli.Command{ | ||
| { | ||
| Name: "complete", | ||
| Aliases: []string{"c"}, | ||
| Usage: "complete a task on the list", | ||
| Action: func(c *cli.Context) { | ||
| println("completed task: ", c.Args().First()) | ||
| <!-- { | ||
| "args": ["complete", "--generate-bash-completion"], | ||
| "output": "laundry" | ||
| } --> | ||
| ``` go | ||
| package main | ||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"} | ||
| app := cli.NewApp() | ||
| app.EnableBashCompletion = true | ||
| app.Commands = []cli.Command{ | ||
| { | ||
| Name: "complete", | ||
| Aliases: []string{"c"}, | ||
| Usage: "complete a task on the list", | ||
| Action: func(c *cli.Context) error { | ||
| fmt.Println("completed task: ", c.Args().First()) | ||
| return nil | ||
| }, | ||
| BashComplete: func(c *cli.Context) { | ||
| // This will complete if no args are passed | ||
| if c.NArg() > 0 { | ||
| return | ||
| } | ||
| for _, t := range tasks { | ||
| fmt.Println(t) | ||
| } | ||
| }, | ||
| }, | ||
| BashComplete: func(c *cli.Context) { | ||
| // This will complete if no args are passed | ||
| if c.NArg() > 0 { | ||
| return | ||
| } | ||
| for _, t := range tasks { | ||
| fmt.Println(t) | ||
| } | ||
| }, | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ... | ||
| ``` | ||
| #### To Enable | ||
| #### Enabling | ||
@@ -429,3 +876,3 @@ Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while | ||
| #### To Distribute | ||
| #### Distribution | ||
@@ -446,8 +893,497 @@ Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename | ||
| #### Customization | ||
| The default bash completion flag (`--generate-bash-completion`) is defined as | ||
| `cli.BashCompletionFlag`, and may be redefined if desired, e.g.: | ||
| <!-- { | ||
| "args": ["--compgen"], | ||
| "output": "wat\nhelp\nh" | ||
| } --> | ||
| ``` go | ||
| package main | ||
| import ( | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| cli.BashCompletionFlag = cli.BoolFlag{ | ||
| Name: "compgen", | ||
| Hidden: true, | ||
| } | ||
| app := cli.NewApp() | ||
| app.EnableBashCompletion = true | ||
| app.Commands = []cli.Command{ | ||
| { | ||
| Name: "wat", | ||
| }, | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| ``` | ||
| ### Generated Help Text | ||
| The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked | ||
| by the cli internals in order to print generated help text for the app, command, | ||
| or subcommand, and break execution. | ||
| #### Customization | ||
| All of the help text generation may be customized, and at multiple levels. The | ||
| templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and | ||
| `SubcommandHelpTemplate` which may be reassigned or augmented, and full override | ||
| is possible by assigning a compatible func to the `cli.HelpPrinter` variable, | ||
| e.g.: | ||
| <!-- { | ||
| "output": "Ha HA. I pwnd the help!!1" | ||
| } --> | ||
| ``` go | ||
| package main | ||
| import ( | ||
| "fmt" | ||
| "io" | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| // EXAMPLE: Append to an existing template | ||
| cli.AppHelpTemplate = fmt.Sprintf(`%s | ||
| WEBSITE: http://awesometown.example.com | ||
| SUPPORT: support@awesometown.example.com | ||
| `, cli.AppHelpTemplate) | ||
| // EXAMPLE: Override a template | ||
| cli.AppHelpTemplate = `NAME: | ||
| {{.Name}} - {{.Usage}} | ||
| USAGE: | ||
| {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} | ||
| {{if len .Authors}} | ||
| AUTHOR: | ||
| {{range .Authors}}{{ . }}{{end}} | ||
| {{end}}{{if .Commands}} | ||
| COMMANDS: | ||
| {{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}} | ||
| GLOBAL OPTIONS: | ||
| {{range .VisibleFlags}}{{.}} | ||
| {{end}}{{end}}{{if .Copyright }} | ||
| COPYRIGHT: | ||
| {{.Copyright}} | ||
| {{end}}{{if .Version}} | ||
| VERSION: | ||
| {{.Version}} | ||
| {{end}} | ||
| ` | ||
| // EXAMPLE: Replace the `HelpPrinter` func | ||
| cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { | ||
| fmt.Println("Ha HA. I pwnd the help!!1") | ||
| } | ||
| cli.NewApp().Run(os.Args) | ||
| } | ||
| ``` | ||
| The default flag may be customized to something other than `-h/--help` by | ||
| setting `cli.HelpFlag`, e.g.: | ||
| <!-- { | ||
| "args": ["--halp"], | ||
| "output": "haaaaalp.*HALP" | ||
| } --> | ||
| ``` go | ||
| package main | ||
| import ( | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| cli.HelpFlag = cli.BoolFlag{ | ||
| Name: "halp, haaaaalp", | ||
| Usage: "HALP", | ||
| EnvVar: "SHOW_HALP,HALPPLZ", | ||
| } | ||
| cli.NewApp().Run(os.Args) | ||
| } | ||
| ``` | ||
| ### Version Flag | ||
| The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which | ||
| is checked by the cli internals in order to print the `App.Version` via | ||
| `cli.VersionPrinter` and break execution. | ||
| #### Customization | ||
| The default flag may be customized to something other than `-v/--version` by | ||
| setting `cli.VersionFlag`, e.g.: | ||
| <!-- { | ||
| "args": ["--print-version"], | ||
| "output": "partay version 19\\.99\\.0" | ||
| } --> | ||
| ``` go | ||
| package main | ||
| import ( | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func main() { | ||
| cli.VersionFlag = cli.BoolFlag{ | ||
| Name: "print-version, V", | ||
| Usage: "print only the version", | ||
| } | ||
| app := cli.NewApp() | ||
| app.Name = "partay" | ||
| app.Version = "19.99.0" | ||
| app.Run(os.Args) | ||
| } | ||
| ``` | ||
| Alternatively, the version printer at `cli.VersionPrinter` may be overridden, e.g.: | ||
| <!-- { | ||
| "args": ["--version"], | ||
| "output": "version=19\\.99\\.0 revision=fafafaf" | ||
| } --> | ||
| ``` go | ||
| package main | ||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| var ( | ||
| Revision = "fafafaf" | ||
| ) | ||
| func main() { | ||
| cli.VersionPrinter = func(c *cli.Context) { | ||
| fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision) | ||
| } | ||
| app := cli.NewApp() | ||
| app.Name = "partay" | ||
| app.Version = "19.99.0" | ||
| app.Run(os.Args) | ||
| } | ||
| ``` | ||
| #### Full API Example | ||
| **Notice**: This is a contrived (functioning) example meant strictly for API | ||
| demonstration purposes. Use of one's imagination is encouraged. | ||
| <!-- { | ||
| "output": "made it!\nPhew!" | ||
| } --> | ||
| ``` go | ||
| package main | ||
| import ( | ||
| "errors" | ||
| "flag" | ||
| "fmt" | ||
| "io" | ||
| "io/ioutil" | ||
| "os" | ||
| "time" | ||
| "github.com/urfave/cli" | ||
| ) | ||
| func init() { | ||
| cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n" | ||
| cli.CommandHelpTemplate += "\nYMMV\n" | ||
| cli.SubcommandHelpTemplate += "\nor something\n" | ||
| cli.HelpFlag = cli.BoolFlag{Name: "halp"} | ||
| cli.BashCompletionFlag = cli.BoolFlag{Name: "compgen", Hidden: true} | ||
| cli.VersionFlag = cli.BoolFlag{Name: "print-version, V"} | ||
| cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { | ||
| fmt.Fprintf(w, "best of luck to you\n") | ||
| } | ||
| cli.VersionPrinter = func(c *cli.Context) { | ||
| fmt.Fprintf(c.App.Writer, "version=%s\n", c.App.Version) | ||
| } | ||
| cli.OsExiter = func(c int) { | ||
| fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", c) | ||
| } | ||
| cli.ErrWriter = ioutil.Discard | ||
| cli.FlagStringer = func(fl cli.Flag) string { | ||
| return fmt.Sprintf("\t\t%s", fl.GetName()) | ||
| } | ||
| } | ||
| type hexWriter struct{} | ||
| func (w *hexWriter) Write(p []byte) (int, error) { | ||
| for _, b := range p { | ||
| fmt.Printf("%x", b) | ||
| } | ||
| fmt.Printf("\n") | ||
| return len(p), nil | ||
| } | ||
| type genericType struct{ | ||
| s string | ||
| } | ||
| func (g *genericType) Set(value string) error { | ||
| g.s = value | ||
| return nil | ||
| } | ||
| func (g *genericType) String() string { | ||
| return g.s | ||
| } | ||
| func main() { | ||
| app := cli.NewApp() | ||
| app.Name = "kənˈtrīv" | ||
| app.Version = "19.99.0" | ||
| app.Compiled = time.Now() | ||
| app.Authors = []cli.Author{ | ||
| cli.Author{ | ||
| Name: "Example Human", | ||
| Email: "human@example.com", | ||
| }, | ||
| } | ||
| app.Copyright = "(c) 1999 Serious Enterprise" | ||
| app.HelpName = "contrive" | ||
| app.Usage = "demonstrate available API" | ||
| app.UsageText = "contrive - demonstrating the available API" | ||
| app.ArgsUsage = "[args and such]" | ||
| app.Commands = []cli.Command{ | ||
| cli.Command{ | ||
| Name: "doo", | ||
| Aliases: []string{"do"}, | ||
| Category: "motion", | ||
| Usage: "do the doo", | ||
| UsageText: "doo - does the dooing", | ||
| Description: "no really, there is a lot of dooing to be done", | ||
| ArgsUsage: "[arrgh]", | ||
| Flags: []cli.Flag{ | ||
| cli.BoolFlag{Name: "forever, forevvarr"}, | ||
| }, | ||
| Subcommands: cli.Commands{ | ||
| cli.Command{ | ||
| Name: "wop", | ||
| Action: wopAction, | ||
| }, | ||
| }, | ||
| SkipFlagParsing: false, | ||
| HideHelp: false, | ||
| Hidden: false, | ||
| HelpName: "doo!", | ||
| BashComplete: func(c *cli.Context) { | ||
| fmt.Fprintf(c.App.Writer, "--better\n") | ||
| }, | ||
| Before: func(c *cli.Context) error { | ||
| fmt.Fprintf(c.App.Writer, "brace for impact\n") | ||
| return nil | ||
| }, | ||
| After: func(c *cli.Context) error { | ||
| fmt.Fprintf(c.App.Writer, "did we lose anyone?\n") | ||
| return nil | ||
| }, | ||
| Action: func(c *cli.Context) error { | ||
| c.Command.FullName() | ||
| c.Command.HasName("wop") | ||
| c.Command.Names() | ||
| c.Command.VisibleFlags() | ||
| fmt.Fprintf(c.App.Writer, "dodododododoodododddooooododododooo\n") | ||
| if c.Bool("forever") { | ||
| c.Command.Run(c) | ||
| } | ||
| return nil | ||
| }, | ||
| OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { | ||
| fmt.Fprintf(c.App.Writer, "for shame\n") | ||
| return err | ||
| }, | ||
| }, | ||
| } | ||
| app.Flags = []cli.Flag{ | ||
| cli.BoolFlag{Name: "fancy"}, | ||
| cli.BoolTFlag{Name: "fancier"}, | ||
| cli.DurationFlag{Name: "howlong, H", Value: time.Second * 3}, | ||
| cli.Float64Flag{Name: "howmuch"}, | ||
| cli.GenericFlag{Name: "wat", Value: &genericType{}}, | ||
| cli.Int64Flag{Name: "longdistance"}, | ||
| cli.Int64SliceFlag{Name: "intervals"}, | ||
| cli.IntFlag{Name: "distance"}, | ||
| cli.IntSliceFlag{Name: "times"}, | ||
| cli.StringFlag{Name: "dance-move, d"}, | ||
| cli.StringSliceFlag{Name: "names, N"}, | ||
| cli.UintFlag{Name: "age"}, | ||
| cli.Uint64Flag{Name: "bigage"}, | ||
| } | ||
| app.EnableBashCompletion = true | ||
| app.HideHelp = false | ||
| app.HideVersion = false | ||
| app.BashComplete = func(c *cli.Context) { | ||
| fmt.Fprintf(c.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n") | ||
| } | ||
| app.Before = func(c *cli.Context) error { | ||
| fmt.Fprintf(c.App.Writer, "HEEEERE GOES\n") | ||
| return nil | ||
| } | ||
| app.After = func(c *cli.Context) error { | ||
| fmt.Fprintf(c.App.Writer, "Phew!\n") | ||
| return nil | ||
| } | ||
| app.CommandNotFound = func(c *cli.Context, command string) { | ||
| fmt.Fprintf(c.App.Writer, "Thar be no %q here.\n", command) | ||
| } | ||
| app.OnUsageError = func(c *cli.Context, err error, isSubcommand bool) error { | ||
| if isSubcommand { | ||
| return err | ||
| } | ||
| fmt.Fprintf(c.App.Writer, "WRONG: %#v\n", err) | ||
| return nil | ||
| } | ||
| app.Action = func(c *cli.Context) error { | ||
| cli.DefaultAppComplete(c) | ||
| cli.HandleExitCoder(errors.New("not an exit coder, though")) | ||
| cli.ShowAppHelp(c) | ||
| cli.ShowCommandCompletions(c, "nope") | ||
| cli.ShowCommandHelp(c, "also-nope") | ||
| cli.ShowCompletions(c) | ||
| cli.ShowSubcommandHelp(c) | ||
| cli.ShowVersion(c) | ||
| categories := c.App.Categories() | ||
| categories.AddCommand("sounds", cli.Command{ | ||
| Name: "bloop", | ||
| }) | ||
| for _, category := range c.App.Categories() { | ||
| fmt.Fprintf(c.App.Writer, "%s\n", category.Name) | ||
| fmt.Fprintf(c.App.Writer, "%#v\n", category.Commands) | ||
| fmt.Fprintf(c.App.Writer, "%#v\n", category.VisibleCommands()) | ||
| } | ||
| fmt.Printf("%#v\n", c.App.Command("doo")) | ||
| if c.Bool("infinite") { | ||
| c.App.Run([]string{"app", "doo", "wop"}) | ||
| } | ||
| if c.Bool("forevar") { | ||
| c.App.RunAsSubcommand(c) | ||
| } | ||
| c.App.Setup() | ||
| fmt.Printf("%#v\n", c.App.VisibleCategories()) | ||
| fmt.Printf("%#v\n", c.App.VisibleCommands()) | ||
| fmt.Printf("%#v\n", c.App.VisibleFlags()) | ||
| fmt.Printf("%#v\n", c.Args().First()) | ||
| if len(c.Args()) > 0 { | ||
| fmt.Printf("%#v\n", c.Args()[1]) | ||
| } | ||
| fmt.Printf("%#v\n", c.Args().Present()) | ||
| fmt.Printf("%#v\n", c.Args().Tail()) | ||
| set := flag.NewFlagSet("contrive", 0) | ||
| nc := cli.NewContext(c.App, set, c) | ||
| fmt.Printf("%#v\n", nc.Args()) | ||
| fmt.Printf("%#v\n", nc.Bool("nope")) | ||
| fmt.Printf("%#v\n", nc.BoolT("nerp")) | ||
| fmt.Printf("%#v\n", nc.Duration("howlong")) | ||
| fmt.Printf("%#v\n", nc.Float64("hay")) | ||
| fmt.Printf("%#v\n", nc.Generic("bloop")) | ||
| fmt.Printf("%#v\n", nc.Int64("bonk")) | ||
| fmt.Printf("%#v\n", nc.Int64Slice("burnks")) | ||
| fmt.Printf("%#v\n", nc.Int("bips")) | ||
| fmt.Printf("%#v\n", nc.IntSlice("blups")) | ||
| fmt.Printf("%#v\n", nc.String("snurt")) | ||
| fmt.Printf("%#v\n", nc.StringSlice("snurkles")) | ||
| fmt.Printf("%#v\n", nc.Uint("flub")) | ||
| fmt.Printf("%#v\n", nc.Uint64("florb")) | ||
| fmt.Printf("%#v\n", nc.GlobalBool("global-nope")) | ||
| fmt.Printf("%#v\n", nc.GlobalBoolT("global-nerp")) | ||
| fmt.Printf("%#v\n", nc.GlobalDuration("global-howlong")) | ||
| fmt.Printf("%#v\n", nc.GlobalFloat64("global-hay")) | ||
| fmt.Printf("%#v\n", nc.GlobalGeneric("global-bloop")) | ||
| fmt.Printf("%#v\n", nc.GlobalInt("global-bips")) | ||
| fmt.Printf("%#v\n", nc.GlobalIntSlice("global-blups")) | ||
| fmt.Printf("%#v\n", nc.GlobalString("global-snurt")) | ||
| fmt.Printf("%#v\n", nc.GlobalStringSlice("global-snurkles")) | ||
| fmt.Printf("%#v\n", nc.FlagNames()) | ||
| fmt.Printf("%#v\n", nc.GlobalFlagNames()) | ||
| fmt.Printf("%#v\n", nc.GlobalIsSet("wat")) | ||
| fmt.Printf("%#v\n", nc.GlobalSet("wat", "nope")) | ||
| fmt.Printf("%#v\n", nc.NArg()) | ||
| fmt.Printf("%#v\n", nc.NumFlags()) | ||
| fmt.Printf("%#v\n", nc.Parent()) | ||
| nc.Set("wat", "also-nope") | ||
| ec := cli.NewExitError("ohwell", 86) | ||
| fmt.Fprintf(c.App.Writer, "%d", ec.ExitCode()) | ||
| fmt.Printf("made it!\n") | ||
| return ec | ||
| } | ||
| if os.Getenv("HEXY") != "" { | ||
| app.Writer = &hexWriter{} | ||
| app.ErrWriter = &hexWriter{} | ||
| } | ||
| app.Metadata = map[string]interface{}{ | ||
| "layers": "many", | ||
| "explicable": false, | ||
| "whatever-values": 19.99, | ||
| } | ||
| app.Run(os.Args) | ||
| } | ||
| func wopAction(c *cli.Context) error { | ||
| fmt.Fprintf(c.App.Writer, ":wave: over here, eh\n") | ||
| return nil | ||
| } | ||
| ``` | ||
| ## Contribution Guidelines | ||
| Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch. | ||
| Feel free to put up a pull request to fix a bug or maybe add a feature. I will | ||
| give it a code review and make sure that it does not break backwards | ||
| compatibility. If I or any other collaborators agree that it is in line with | ||
| the vision of the project, we will work with you to get the code into | ||
| a mergeable state and merge it into the master branch. | ||
| If you have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @micro/cli to the issue in question and we can review it together. | ||
| If you have contributed something significant to the project, we will most | ||
| likely add you as a collaborator. As a collaborator you are given the ability | ||
| to merge others pull requests. It is very important that new code does not | ||
| break existing code, so be careful about what code you do choose to merge. | ||
| If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @micro/cli up over email and we will get it figured out. | ||
| If you feel like you have contributed to the project but have not yet been | ||
| added as a collaborator, we probably forgot to add you, please open an issue. |
Sorry, the diff of this file is not supported yet