Merge remote-tracking branch 'origin/master' into v1
This commit is contained in:
commit
f6e8084112
@ -7,12 +7,9 @@ cache:
|
||||
- node_modules
|
||||
|
||||
go:
|
||||
- 1.2.x
|
||||
- 1.3.x
|
||||
- 1.4.2
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- master
|
||||
|
||||
matrix:
|
||||
@ -23,10 +20,12 @@ matrix:
|
||||
os: osx
|
||||
- go: 1.7.x
|
||||
os: osx
|
||||
- go: 1.8.x
|
||||
os: osx
|
||||
|
||||
before_script:
|
||||
- go get github.com/urfave/gfmrun/... || true
|
||||
- go get golang.org/x/tools/... || true
|
||||
- go get golang.org/x/tools/cmd/goimports
|
||||
- if [ ! -f node_modules/.bin/markdown-toc ] ; then
|
||||
npm install markdown-toc ;
|
||||
fi
|
||||
|
33
README.md
33
README.md
@ -455,13 +455,13 @@ error.
|
||||
|
||||
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`
|
||||
with `sort`.
|
||||
or `CommandsByName` with `sort`.
|
||||
|
||||
For example this:
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "Load configuration from FILE\n.*Language for the greeting.*"
|
||||
"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
|
||||
@ -488,7 +488,27 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@ -940,16 +960,13 @@ SUPPORT: support@awesometown.example.com
|
||||
cli.AppHelpTemplate = `NAME:
|
||||
{{.Name}} - {{.Usage}}
|
||||
USAGE:
|
||||
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command
|
||||
[command options]{{end}} {{if
|
||||
.ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
||||
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
||||
{{if len .Authors}}
|
||||
AUTHOR(S):
|
||||
AUTHOR:
|
||||
{{range .Authors}}{{ . }}{{end}}
|
||||
{{end}}{{if .Commands}}
|
||||
COMMANDS:
|
||||
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"
|
||||
}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
||||
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
||||
GLOBAL OPTIONS:
|
||||
{{range .VisibleFlags}}{{.}}
|
||||
{{end}}{{end}}{{if .Copyright }}
|
||||
|
@ -59,7 +59,7 @@ func TestStringSliceApplyInputSourceValue(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: []string{"hello", "world"},
|
||||
MapValue: []interface{}{"hello", "world"},
|
||||
})
|
||||
expect(t, c.StringSlice("test"), []string{"hello", "world"})
|
||||
}
|
||||
@ -68,7 +68,7 @@ func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: []string{"hello", "world"},
|
||||
MapValue: []interface{}{"hello", "world"},
|
||||
ContextValueString: "ohno",
|
||||
})
|
||||
expect(t, c.StringSlice("test"), []string{"ohno"})
|
||||
@ -78,7 +78,7 @@ func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test", EnvVar: "TEST"}),
|
||||
FlagName: "test",
|
||||
MapValue: []string{"hello", "world"},
|
||||
MapValue: []interface{}{"hello", "world"},
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: "oh,no",
|
||||
})
|
||||
@ -89,7 +89,7 @@ func TestIntSliceApplyInputSourceValue(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: []int{1, 2},
|
||||
MapValue: []interface{}{1, 2},
|
||||
})
|
||||
expect(t, c.IntSlice("test"), []int{1, 2})
|
||||
}
|
||||
@ -98,7 +98,7 @@ func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: []int{1, 2},
|
||||
MapValue: []interface{}{1, 2},
|
||||
ContextValueString: "3",
|
||||
})
|
||||
expect(t, c.IntSlice("test"), []int{3})
|
||||
@ -108,7 +108,7 @@ func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test", EnvVar: "TEST"}),
|
||||
FlagName: "test",
|
||||
MapValue: []int{1, 2},
|
||||
MapValue: []interface{}{1, 2},
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: "3,4",
|
||||
})
|
||||
|
@ -130,45 +130,59 @@ func (fsm *MapInputSource) String(name string) (string, error) {
|
||||
// StringSlice returns an []string from the map if it exists otherwise returns nil
|
||||
func (fsm *MapInputSource) StringSlice(name string) ([]string, error) {
|
||||
otherGenericValue, exists := fsm.valueMap[name]
|
||||
if exists {
|
||||
otherValue, isType := otherGenericValue.([]string)
|
||||
if !isType {
|
||||
return nil, incorrectTypeForFlagError(name, "[]string", otherGenericValue)
|
||||
if !exists {
|
||||
otherGenericValue, exists = nestedVal(name, fsm.valueMap)
|
||||
if !exists {
|
||||
return nil, nil
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
|
||||
if exists {
|
||||
otherValue, isType := nestedGenericValue.([]string)
|
||||
if !isType {
|
||||
return nil, incorrectTypeForFlagError(name, "[]string", nestedGenericValue)
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
|
||||
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(fmt.Sprintf("%s[%d]", name, i), "string", v)
|
||||
}
|
||||
|
||||
stringSlice = append(stringSlice, stringValue)
|
||||
}
|
||||
|
||||
return stringSlice, nil
|
||||
}
|
||||
|
||||
// IntSlice returns an []int from the map if it exists otherwise returns nil
|
||||
func (fsm *MapInputSource) IntSlice(name string) ([]int, error) {
|
||||
otherGenericValue, exists := fsm.valueMap[name]
|
||||
if exists {
|
||||
otherValue, isType := otherGenericValue.([]int)
|
||||
if !isType {
|
||||
return nil, incorrectTypeForFlagError(name, "[]int", otherGenericValue)
|
||||
if !exists {
|
||||
otherGenericValue, exists = nestedVal(name, fsm.valueMap)
|
||||
if !exists {
|
||||
return nil, nil
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
|
||||
if exists {
|
||||
otherValue, isType := nestedGenericValue.([]int)
|
||||
if !isType {
|
||||
return nil, incorrectTypeForFlagError(name, "[]int", nestedGenericValue)
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
|
||||
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(fmt.Sprintf("%s[%d]", name, i), "int", v)
|
||||
}
|
||||
|
||||
intSlice = append(intSlice, intValue)
|
||||
}
|
||||
|
||||
return intSlice, nil
|
||||
}
|
||||
|
||||
// Generic returns an cli.Generic from the map if it exists otherwise returns nil
|
||||
|
@ -57,8 +57,8 @@ func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) {
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
case reflect.Array:
|
||||
fallthrough // [todo] - Support array type
|
||||
case reflect.Array, reflect.Slice:
|
||||
ret[key] = val.([]interface{})
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported: type = %#v", v.Kind())
|
||||
}
|
||||
|
21
command.go
21
command.go
@ -61,6 +61,20 @@ type Command struct {
|
||||
commandNamePath []string
|
||||
}
|
||||
|
||||
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
|
||||
func (c Command) FullName() string {
|
||||
@ -230,11 +244,9 @@ func (c Command) startApp(ctx *Context) error {
|
||||
app.HelpName = app.Name
|
||||
}
|
||||
|
||||
if c.Description != "" {
|
||||
app.Usage = c.Description
|
||||
} else {
|
||||
app.Usage = c.Usage
|
||||
}
|
||||
app.Description = c.Description
|
||||
app.ArgsUsage = c.ArgsUsage
|
||||
|
||||
// set CommandNotFound
|
||||
app.CommandNotFound = ctx.App.CommandNotFound
|
||||
@ -250,6 +262,7 @@ func (c Command) startApp(ctx *Context) error {
|
||||
app.Author = ctx.App.Author
|
||||
app.Email = ctx.App.Email
|
||||
app.Writer = ctx.App.Writer
|
||||
app.ErrWriter = ctx.App.ErrWriter
|
||||
|
||||
app.categories = CommandCategories{}
|
||||
for _, command := range c.Subcommands {
|
||||
|
@ -153,3 +153,32 @@ func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
23
errors.go
23
errors.go
@ -74,7 +74,7 @@ func (ee *ExitError) ExitCode() int {
|
||||
// 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.
|
||||
// called on all members of the Errors slice and calls OsExiter with the last exit code.
|
||||
func HandleExitCoder(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
@ -93,18 +93,23 @@ func HandleExitCoder(err error) {
|
||||
}
|
||||
|
||||
if multiErr, ok := err.(MultiError); ok {
|
||||
for _, merr := range multiErr.Errors {
|
||||
HandleExitCoder(merr)
|
||||
}
|
||||
code := handleMultiError(multiErr)
|
||||
OsExiter(code)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err.Error() != "" {
|
||||
if _, ok := err.(ErrorFormatter); ok {
|
||||
fmt.Fprintf(ErrWriter, "%+v\n", err)
|
||||
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, err)
|
||||
fmt.Fprintln(ErrWriter, merr)
|
||||
if exitErr, ok := merr.(ExitCoder); ok {
|
||||
code = exitErr.ExitCode()
|
||||
}
|
||||
}
|
||||
OsExiter(1)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
@ -12,9 +12,11 @@ func TestHandleExitCoder_nil(t *testing.T) {
|
||||
called := false
|
||||
|
||||
OsExiter = func(rc int) {
|
||||
if !called {
|
||||
exitCode = rc
|
||||
called = true
|
||||
}
|
||||
}
|
||||
|
||||
defer func() { OsExiter = fakeOsExiter }()
|
||||
|
||||
@ -29,9 +31,11 @@ func TestHandleExitCoder_ExitCoder(t *testing.T) {
|
||||
called := false
|
||||
|
||||
OsExiter = func(rc int) {
|
||||
if !called {
|
||||
exitCode = rc
|
||||
called = true
|
||||
}
|
||||
}
|
||||
|
||||
defer func() { OsExiter = fakeOsExiter }()
|
||||
|
||||
@ -46,66 +50,23 @@ func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) {
|
||||
called := false
|
||||
|
||||
OsExiter = func(rc int) {
|
||||
if !called {
|
||||
exitCode = rc
|
||||
called = true
|
||||
}
|
||||
}
|
||||
|
||||
defer func() { OsExiter = fakeOsExiter }()
|
||||
|
||||
exitErr := NewExitError("galactic perimeter breach", 9)
|
||||
err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr)
|
||||
exitErr2 := NewExitError("last ExitCoder", 11)
|
||||
err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr, exitErr2)
|
||||
HandleExitCoder(err)
|
||||
|
||||
expect(t, exitCode, 9)
|
||||
expect(t, exitCode, 11)
|
||||
expect(t, called, true)
|
||||
}
|
||||
|
||||
func TestHandleExitCoder_ErrorWithMessage(t *testing.T) {
|
||||
exitCode := 0
|
||||
called := false
|
||||
|
||||
OsExiter = func(rc int) {
|
||||
exitCode = rc
|
||||
called = true
|
||||
}
|
||||
ErrWriter = &bytes.Buffer{}
|
||||
|
||||
defer func() {
|
||||
OsExiter = fakeOsExiter
|
||||
ErrWriter = fakeErrWriter
|
||||
}()
|
||||
|
||||
err := errors.New("gourd havens")
|
||||
HandleExitCoder(err)
|
||||
|
||||
expect(t, exitCode, 1)
|
||||
expect(t, called, true)
|
||||
expect(t, ErrWriter.(*bytes.Buffer).String(), "gourd havens\n")
|
||||
}
|
||||
|
||||
func TestHandleExitCoder_ErrorWithoutMessage(t *testing.T) {
|
||||
exitCode := 0
|
||||
called := false
|
||||
|
||||
OsExiter = func(rc int) {
|
||||
exitCode = rc
|
||||
called = true
|
||||
}
|
||||
ErrWriter = &bytes.Buffer{}
|
||||
|
||||
defer func() {
|
||||
OsExiter = fakeOsExiter
|
||||
ErrWriter = fakeErrWriter
|
||||
}()
|
||||
|
||||
err := errors.New("")
|
||||
HandleExitCoder(err)
|
||||
|
||||
expect(t, exitCode, 1)
|
||||
expect(t, called, true)
|
||||
expect(t, ErrWriter.(*bytes.Buffer).String(), "")
|
||||
}
|
||||
|
||||
// make a stub to not import pkg/errors
|
||||
type ErrorWithFormat struct {
|
||||
error
|
||||
@ -123,8 +84,10 @@ func TestHandleExitCoder_ErrorWithFormat(t *testing.T) {
|
||||
called := false
|
||||
|
||||
OsExiter = func(rc int) {
|
||||
if !called {
|
||||
called = true
|
||||
}
|
||||
}
|
||||
ErrWriter = &bytes.Buffer{}
|
||||
|
||||
defer func() {
|
||||
@ -132,7 +95,7 @@ func TestHandleExitCoder_ErrorWithFormat(t *testing.T) {
|
||||
ErrWriter = fakeErrWriter
|
||||
}()
|
||||
|
||||
err := NewErrorWithFormat("I am formatted")
|
||||
err := NewExitError(NewErrorWithFormat("I am formatted"), 1)
|
||||
HandleExitCoder(err)
|
||||
|
||||
expect(t, called, true)
|
||||
@ -143,8 +106,10 @@ func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) {
|
||||
called := false
|
||||
|
||||
OsExiter = func(rc int) {
|
||||
if !called {
|
||||
called = true
|
||||
}
|
||||
}
|
||||
ErrWriter = &bytes.Buffer{}
|
||||
|
||||
defer func() { OsExiter = fakeOsExiter }()
|
||||
|
86
flag_test.go
86
flag_test.go
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -34,54 +35,54 @@ func TestFlagsFromEnv(t *testing.T) {
|
||||
input string
|
||||
output interface{}
|
||||
flag Flag
|
||||
err error
|
||||
errRegexp string
|
||||
}{
|
||||
{"", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
||||
{"1", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
||||
{"false", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
||||
{"foobar", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Errorf(`could not parse foobar as bool value for flag debug: strconv.ParseBool: parsing "foobar": invalid syntax`)},
|
||||
{"", 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"}, nil},
|
||||
{"1", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
||||
{"false", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
||||
{"foobar", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Errorf(`could not parse foobar as bool value for flag debug: strconv.ParseBool: parsing "foobar": invalid syntax`)},
|
||||
{"", 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"}, nil},
|
||||
{"foobar", false, DurationFlag{Name: "time", EnvVar: "TIME"}, fmt.Errorf(`could not parse foobar as duration for flag time: time: invalid duration foobar`)},
|
||||
{"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"}, nil},
|
||||
{"1", 1.0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
||||
{"foobar", 0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as float64 value for flag seconds: strconv.ParseFloat: parsing "foobar": invalid syntax`)},
|
||||
{"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"}, nil},
|
||||
{"1.2", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2 as int value for flag seconds: strconv.ParseInt: parsing "1.2": invalid syntax`)},
|
||||
{"foobar", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as int value for flag seconds: strconv.ParseInt: parsing "foobar": invalid syntax`)},
|
||||
{"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"}, nil},
|
||||
{"1.2", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2 as int value for flag seconds: strconv.ParseInt: parsing "1.2": invalid syntax`)},
|
||||
{"foobar", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as int value for flag seconds: strconv.ParseInt: parsing "foobar": invalid syntax`)},
|
||||
{"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"}, nil},
|
||||
{"1.2,2", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2,2 as int slice value for flag seconds: strconv.ParseInt: parsing "1.2": invalid syntax`)},
|
||||
{"foobar", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as int slice value for flag seconds: strconv.ParseInt: parsing "foobar": invalid syntax`)},
|
||||
{"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"}, nil},
|
||||
{"1.2,2", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2,2 as int64 slice value for flag seconds: strconv.ParseInt: parsing "1.2": invalid syntax`)},
|
||||
{"foobar", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as int64 slice value for flag seconds: strconv.ParseInt: parsing "foobar": invalid syntax`)},
|
||||
{"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"}, nil},
|
||||
{"foo", "foo", StringFlag{Name: "name", EnvVar: "NAME"}, ""},
|
||||
|
||||
{"foo,bar", StringSlice{"foo", "bar"}, StringSliceFlag{Name: "names", EnvVar: "NAMES"}, nil},
|
||||
{"foo,bar", StringSlice{"foo", "bar"}, StringSliceFlag{Name: "names", EnvVar: "NAMES"}, ""},
|
||||
|
||||
{"1", uint(1), UintFlag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
||||
{"1.2", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2 as uint value for flag seconds: strconv.ParseUint: parsing "1.2": invalid syntax`)},
|
||||
{"foobar", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as uint value for flag seconds: strconv.ParseUint: parsing "foobar": invalid syntax`)},
|
||||
{"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"}, nil},
|
||||
{"1.2", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2 as uint64 value for flag seconds: strconv.ParseUint: parsing "1.2": invalid syntax`)},
|
||||
{"foobar", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as uint64 value for flag seconds: strconv.ParseUint: parsing "foobar": invalid syntax`)},
|
||||
{"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"}, nil},
|
||||
{"foo,bar", &Parser{"foo", "bar"}, GenericFlag{Name: "names", Value: &Parser{}, EnvVar: "NAMES"}, ""},
|
||||
}
|
||||
|
||||
for _, test := range flagTests {
|
||||
@ -98,8 +99,19 @@ func TestFlagsFromEnv(t *testing.T) {
|
||||
}
|
||||
|
||||
err := a.Run([]string{"run"})
|
||||
if !reflect.DeepEqual(test.err, err) {
|
||||
t.Errorf("expected error %s, got error %s", test.err, err)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
help.go
2
help.go
@ -64,7 +64,7 @@ OPTIONS:
|
||||
// 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 .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
||||
|
Loading…
Reference in New Issue
Block a user