Merge pull request #458 from urfave/merging-from-v1

Merging from v1
This commit is contained in:
Dan Buch 2016-06-18 17:05:59 -04:00 committed by GitHub
commit a65711bb38
10 changed files with 689 additions and 22 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*.coverprofile *.coverprofile
node_modules/

View File

@ -2,6 +2,10 @@ language: go
sudo: false sudo: false
cache:
directories:
- node_modules
go: go:
- 1.2.2 - 1.2.2
- 1.3.3 - 1.3.3
@ -21,14 +25,20 @@ matrix:
env: pip_install="sudo pip install" env: pip_install="sudo pip install"
- go: 1.1.2 - go: 1.1.2
install: go get -v . install: go get -v .
before_script: echo skipping gfmxr on $TRAVIS_GO_VERSION before_script: echo skipping gfmxr on ${TRAVIS_GO_VERSION}
script: script:
- ./runtests vet - ./runtests vet
- ./runtests test - ./runtests test
before_script: before_script:
- go get github.com/urfave/gfmxr/...
- $pip_install flake8 - $pip_install flake8
- if [ ! -f node_modules/.bin/markdown-toc ] ; then
npm install markdown-toc ;
fi
- mkdir -p ${GOPATH%%:*}/src/gopkg.in/urfave
- rm -rvf ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2
- ln -sv ${TRAVIS_BUILD_DIR} ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2
- go get github.com/urfave/gfmxr/...
script: script:
- flake8 runtests cli-v1-to-v2 - flake8 runtests cli-v1-to-v2
@ -37,3 +47,4 @@ script:
- ./runtests gfmxr - ./runtests gfmxr
- ./cli-v1-to-v2 --selftest - ./cli-v1-to-v2 --selftest
- ./runtests migrations - ./runtests migrations
- ./runtests toc

View File

@ -36,6 +36,7 @@
- `./runtests` test runner with coverage tracking by default - `./runtests` test runner with coverage tracking by default
- testing on OS X - testing on OS X
- testing on Windows - testing on Windows
- `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code
### Changed ### Changed
- Use spaces for alignment in help/usage output instead of tabs, making the - Use spaces for alignment in help/usage output instead of tabs, making the

View File

@ -1,3 +1,6 @@
cli
===
[![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli) [![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli)
[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli) [![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli)
@ -6,9 +9,6 @@
[![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) / [![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) /
[![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc) [![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc)
# cli
**Notice:** This is the library formerly known as **Notice:** This is the library formerly known as
`github.com/codegangsta/cli` -- Github will automatically redirect requests `github.com/codegangsta/cli` -- Github will automatically redirect requests
to this repository, but we recommend updating your references for clarity. to this repository, but we recommend updating your references for clarity.
@ -17,6 +17,37 @@ cli is a simple, fast, and fun package for building command line apps in Go. The
goal is to enable developers to write fast and distributable command line goal is to enable developers to write fast and distributable command line
applications in an expressive way. applications in an expressive way.
<!-- toc -->
- [Overview](#overview)
- [Installation](#installation)
* [Supported platforms](#supported-platforms)
* [Using the `v2` branch](#using-the-v2-branch)
* [Pinning to the `v1` branch](#pinning-to-the-v1-branch)
- [Getting Started](#getting-started)
- [Examples](#examples)
* [Arguments](#arguments)
* [Flags](#flags)
+ [Placeholder Values](#placeholder-values)
+ [Alternate Names](#alternate-names)
+ [Values from the Environment](#values-from-the-environment)
+ [Values from alternate input sources (YAML and others)](#values-from-alternate-input-sources-yaml-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 ## Overview
Command line apps are usually so tiny that there is absolutely no reason why Command line apps are usually so tiny that there is absolutely no reason why
@ -986,7 +1017,7 @@ func main() {
} }
``` ```
#### Full API Example ### Full API Example
**NOTE**: This is a contrived (functioning) example meant strictly for API **NOTE**: This is a contrived (functioning) example meant strictly for API
demonstration purposes. Use of one's imagination is encouraged. demonstration purposes. Use of one's imagination is encouraged.
@ -1016,7 +1047,7 @@ func init() {
cli.HelpFlag = &cli.BoolFlag{Name: "halp"} cli.HelpFlag = &cli.BoolFlag{Name: "halp"}
cli.BashCompletionFlag = &cli.BoolFlag{Name: "compgen", Hidden: true} cli.BashCompletionFlag = &cli.BoolFlag{Name: "compgen", Hidden: true}
cli.VersionFlag = &cli.BoolFlag{Name: "print-version, V"} cli.VersionFlag = &cli.BoolFlag{Name: "print-version", Aliases: []string{"V"}}
cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
fmt.Fprintf(w, "best of luck to you\n") fmt.Fprintf(w, "best of luck to you\n")
@ -1044,6 +1075,19 @@ func (w *hexWriter) Write(p []byte) (int, error) {
return len(p), nil 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() { func main() {
app := cli.NewApp() app := cli.NewApp()
app.Name = "kənˈtrīv" app.Name = "kənˈtrīv"
@ -1070,7 +1114,7 @@ func main() {
Description: "no really, there is a lot of dooing to be done", Description: "no really, there is a lot of dooing to be done",
ArgsUsage: "[arrgh]", ArgsUsage: "[arrgh]",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{Name: "forever, forevvarr"}, &cli.BoolFlag{Name: "forever", Aliases: []string{"forevvarr"}},
}, },
Subcommands: []*cli.Command{ Subcommands: []*cli.Command{
&cli.Command{ &cli.Command{
@ -1113,7 +1157,17 @@ func main() {
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
&cli.BoolFlag{Name: "fancy"}, &cli.BoolFlag{Name: "fancy"},
&cli.BoolFlag{Value: true, Name: "fancier"}, &cli.BoolFlag{Value: true, Name: "fancier"},
&cli.DurationFlag{Name: "howlong", Aliases: []string{"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", Aliases: []string{"d"}}, &cli.StringFlag{Name: "dance-move", Aliases: []string{"d"}},
&cli.StringSliceFlag{Name: "names", Aliases: []string{"N"}},
&cli.UintFlag{Name: "age"},
&cli.Uint64Flag{Name: "bigage"},
} }
app.EnableBashCompletion = true app.EnableBashCompletion = true
app.HideHelp = false app.HideHelp = false
@ -1190,10 +1244,14 @@ func main() {
fmt.Printf("%#v\n", nc.Duration("howlong")) fmt.Printf("%#v\n", nc.Duration("howlong"))
fmt.Printf("%#v\n", nc.Float64("hay")) fmt.Printf("%#v\n", nc.Float64("hay"))
fmt.Printf("%#v\n", nc.Generic("bloop")) 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.Int("bips"))
fmt.Printf("%#v\n", nc.IntSlice("blups")) fmt.Printf("%#v\n", nc.IntSlice("blups"))
fmt.Printf("%#v\n", nc.String("snurt")) fmt.Printf("%#v\n", nc.String("snurt"))
fmt.Printf("%#v\n", nc.StringSlice("snurkles")) 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.FlagNames()) fmt.Printf("%#v\n", nc.FlagNames())
fmt.Printf("%#v\n", nc.IsSet("wat")) fmt.Printf("%#v\n", nc.IsSet("wat"))

View File

@ -4,20 +4,24 @@ os: Windows Server 2012 R2
clone_folder: c:\gopath\src\github.com\urfave\cli clone_folder: c:\gopath\src\github.com\urfave\cli
cache:
- node_modules
environment: environment:
GOPATH: C:\gopath GOPATH: C:\gopath
GOVERSION: 1.6 GOVERSION: 1.6
PYTHON: C:\Python27-x64 PYTHON: C:\Python27-x64
PYTHON_VERSION: 2.7.x PYTHON_VERSION: 2.7.x
PYTHON_ARCH: 64 PYTHON_ARCH: 64
GFMXR_DEBUG: 1
install: install:
- set PATH=%GOPATH%\bin;C:\go\bin;%PATH% - set PATH=%GOPATH%\bin;C:\go\bin;%PATH%
- go version - go version
- go env - go env
- git clone . c:\gopath\src\gopkg.in\urfave\cli.v2
- go get github.com/urfave/gfmxr/... - go get github.com/urfave/gfmxr/...
- go get -v -t ./... - go get -v -t ./...
- if not exist node_modules\.bin\markdown-toc npm install markdown-toc
build_script: build_script:
- python runtests vet - python runtests vet
@ -25,3 +29,4 @@ build_script:
- python runtests gfmxr - python runtests gfmxr
- python cli-v1-to-v2 --selftest - python cli-v1-to-v2 --selftest
- python runtests migrations - python runtests migrations
- python runtests toc

View File

@ -33,6 +33,30 @@ func (c *Context) Int(name string) int {
return 0 return 0
} }
// Int64 looks up the value of a local int flag, returns 0 if no int flag exists
func (c *Context) Int64(name string) int64 {
if fs := lookupFlagSet(name, c); fs != nil {
return lookupInt64(name, fs)
}
return 0
}
// Uint looks up the value of a local int flag, returns 0 if no int flag exists
func (c *Context) Uint(name string) uint {
if fs := lookupFlagSet(name, c); fs != nil {
return lookupUint(name, fs)
}
return 0
}
// Uint64 looks up the value of a local int flag, returns 0 if no int flag exists
func (c *Context) Uint64(name string) uint64 {
if fs := lookupFlagSet(name, c); fs != nil {
return lookupUint64(name, fs)
}
return 0
}
// Duration looks up the value of a local time.Duration flag, returns 0 if no // Duration looks up the value of a local time.Duration flag, returns 0 if no
// time.Duration flag exists // time.Duration flag exists
func (c *Context) Duration(name string) time.Duration { func (c *Context) Duration(name string) time.Duration {
@ -51,6 +75,15 @@ func (c *Context) Float64(name string) float64 {
return 0 return 0
} }
// Int64Slice looks up the value of a local int slice flag, returns nil if no int
// slice flag exists
func (c *Context) Int64Slice(name string) []int64 {
if fs := lookupFlagSet(name, c); fs != nil {
return lookupInt64Slice(name, fs)
}
return nil
}
// Bool looks up the value of a local bool flag, returns false if no bool flag exists // Bool looks up the value of a local bool flag, returns false if no bool flag exists
func (c *Context) Bool(name string) bool { func (c *Context) Bool(name string) bool {
if fs := lookupFlagSet(name, c); fs != nil { if fs := lookupFlagSet(name, c); fs != nil {
@ -171,7 +204,46 @@ func lookupFlagSet(name string, ctx *Context) *flag.FlagSet {
func lookupInt(name string, set *flag.FlagSet) int { func lookupInt(name string, set *flag.FlagSet) int {
f := set.Lookup(name) f := set.Lookup(name)
if f != nil { if f != nil {
val, err := strconv.Atoi(f.Value.String()) val, err := strconv.ParseInt(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return int(val)
}
return 0
}
func lookupInt64(name string, set *flag.FlagSet) int64 {
f := set.Lookup(name)
if f != nil {
val, err := strconv.ParseInt(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return val
}
return 0
}
func lookupUint(name string, set *flag.FlagSet) uint {
f := set.Lookup(name)
if f != nil {
val, err := strconv.ParseUint(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return uint(val)
}
return 0
}
func lookupUint64(name string, set *flag.FlagSet) uint64 {
f := set.Lookup(name)
if f != nil {
val, err := strconv.ParseUint(f.Value.String(), 0, 64)
if err != nil { if err != nil {
return 0 return 0
} }
@ -235,6 +307,16 @@ func lookupIntSlice(name string, set *flag.FlagSet) []int {
return nil return nil
} }
func lookupInt64Slice(name string, set *flag.FlagSet) []int64 {
f := set.Lookup(name)
if f != nil {
return (f.Value.(*Int64Slice)).Value()
}
return nil
}
func lookupGeneric(name string, set *flag.FlagSet) interface{} { func lookupGeneric(name string, set *flag.FlagSet) interface{} {
f := set.Lookup(name) f := set.Lookup(name)
if f != nil { if f != nil {

View File

@ -10,15 +10,24 @@ import (
func TestNewContext(t *testing.T) { func TestNewContext(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Int("myflag", 12, "doc") 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") set.Float64("myflag64", float64(17), "doc")
globalSet := flag.NewFlagSet("test", 0) globalSet := flag.NewFlagSet("test", 0)
globalSet.Int("myflag", 42, "doc") 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") globalSet.Float64("myflag64", float64(47), "doc")
globalCtx := NewContext(nil, globalSet, nil) globalCtx := NewContext(nil, globalSet, nil)
command := &Command{Name: "mycommand"} command := &Command{Name: "mycommand"}
c := NewContext(nil, set, globalCtx) c := NewContext(nil, set, globalCtx)
c.Command = command c.Command = command
expect(t, c.Int("myflag"), 12) 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.Float64("myflag64"), float64(17))
expect(t, c.Command.Name, "mycommand") expect(t, c.Command.Name, "mycommand")
} }
@ -30,6 +39,27 @@ func TestContext_Int(t *testing.T) {
expect(t, c.Int("myflag"), 12) expect(t, c.Int("myflag"), 12)
} }
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_Float64(t *testing.T) { func TestContext_Float64(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Float64("myflag", float64(17), "doc") set.Float64("myflag", float64(17), "doc")

265
flag.go
View File

@ -219,6 +219,11 @@ func NewIntSlice(defaults ...int) *IntSlice {
return &IntSlice{slice: append([]int{}, defaults...)} return &IntSlice{slice: append([]int{}, defaults...)}
} }
// NewInt64Slice makes an *Int64Slice with default values
func NewInt64Slice(defaults ...int64) *Int64Slice {
return &Int64Slice{slice: append([]int64{}, defaults...)}
}
// SetInt directly adds an integer to the list of values // SetInt directly adds an integer to the list of values
func (i *IntSlice) SetInt(value int) { func (i *IntSlice) SetInt(value int) {
if !i.hasBeenSet { if !i.hasBeenSet {
@ -243,18 +248,18 @@ func (i *IntSlice) Set(value string) error {
return nil return nil
} }
tmp, err := strconv.Atoi(value) tmp, err := strconv.ParseInt(value, 0, 64)
if err != nil { if err != nil {
return err return err
} }
i.slice = append(i.slice, tmp) i.slice = append(i.slice, int(tmp))
return nil return nil
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
func (i *IntSlice) String() string { func (f *IntSlice) String() string {
return fmt.Sprintf("%v", i.slice) return fmt.Sprintf("%#v", f.slice)
} }
// Serialized allows IntSlice to fulfill Serializeder // Serialized allows IntSlice to fulfill Serializeder
@ -317,6 +322,100 @@ func (f *IntSliceFlag) Names() []string {
return flagNames(f) return flagNames(f)
} }
// Int64Slice is an opaque type for []int to satisfy flag.Value
type Int64Slice struct {
slice []int64
hasBeenSet bool
}
// Set parses the value into an integer and appends it to the list of values
func (f *Int64Slice) Set(value string) error {
if !f.hasBeenSet {
f.slice = []int64{}
f.hasBeenSet = true
}
if strings.HasPrefix(value, slPfx) {
// Deserializing assumes overwrite
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &f.slice)
f.hasBeenSet = true
return nil
}
tmp, err := strconv.ParseInt(value, 0, 64)
if err != nil {
return err
}
f.slice = append(f.slice, tmp)
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (f *Int64Slice) String() string {
return fmt.Sprintf("%#v", f.slice)
}
// Serialized allows Int64Slice to fulfill Serializeder
func (f *Int64Slice) Serialized() string {
jsonBytes, _ := json.Marshal(f.slice)
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
}
// Value returns the slice of ints set by this flag
func (f *Int64Slice) Value() []int64 {
return f.slice
}
// Int64SliceFlag is an int flag that can be specified multiple times on the
// command-line
type Int64SliceFlag struct {
Name string
Aliases []string
Value *Int64Slice
Usage string
EnvVars []string
Hidden bool
}
// String returns the usage
func (f *Int64SliceFlag) String() string {
return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
func (f *Int64SliceFlag) Apply(set *flag.FlagSet) {
if f.EnvVars != nil {
for _, envVar := range f.EnvVars {
if envVal := os.Getenv(envVar); envVal != "" {
newVal := NewInt64Slice()
for _, s := range strings.Split(envVal, ",") {
s = strings.TrimSpace(s)
err := newVal.Set(s)
if err != nil {
fmt.Fprintf(ErrWriter, err.Error())
}
}
f.Value = newVal
break
}
}
}
if f.Value == nil {
f.Value = NewInt64Slice()
}
for _, name := range f.Names() {
set.Var(f.Value, name, f.Usage)
}
}
// Names returns the names of the flag.
func (f *Int64SliceFlag) Names() []string {
return flagNames(f)
}
// BoolFlag is a switch that defaults to false // BoolFlag is a switch that defaults to false
type BoolFlag struct { type BoolFlag struct {
Name string Name string
@ -403,7 +502,6 @@ func (f *StringFlag) Names() []string {
} }
// IntFlag is a flag that takes an integer // IntFlag is a flag that takes an integer
// Errors if the value provided cannot be parsed
type IntFlag struct { type IntFlag struct {
Name string Name string
Aliases []string Aliases []string
@ -447,6 +545,138 @@ func (f *IntFlag) Names() []string {
return flagNames(f) return flagNames(f)
} }
// Int64Flag is a flag that takes a 64-bit integer
type Int64Flag struct {
Name string
Aliases []string
Value int64
Usage string
EnvVars []string
Destination *int64
Hidden bool
}
// String returns the usage
func (f *Int64Flag) String() string {
return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
func (f *Int64Flag) Apply(set *flag.FlagSet) {
if f.EnvVars != nil {
for _, envVar := range f.EnvVars {
if envVal := os.Getenv(envVar); envVal != "" {
envValInt, err := strconv.ParseInt(envVal, 0, 64)
if err == nil {
f.Value = envValInt
break
}
}
}
}
for _, name := range f.Names() {
if f.Destination != nil {
set.Int64Var(f.Destination, name, f.Value, f.Usage)
return
}
set.Int64(name, f.Value, f.Usage)
}
}
// Names returns the names of the flag.
func (f *Int64Flag) Names() []string {
return flagNames(f)
}
// UintFlag is a flag that takes an unsigned integer
type UintFlag struct {
Name string
Aliases []string
Value uint
Usage string
EnvVars []string
Destination *uint
Hidden bool
}
// String returns the usage
func (f *UintFlag) String() string {
return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
func (f *UintFlag) Apply(set *flag.FlagSet) {
if f.EnvVars != nil {
for _, envVar := range f.EnvVars {
if envVal := os.Getenv(envVar); envVal != "" {
envValInt, err := strconv.ParseUint(envVal, 0, 64)
if err == nil {
f.Value = uint(envValInt)
break
}
}
}
}
for _, name := range f.Names() {
if f.Destination != nil {
set.UintVar(f.Destination, name, f.Value, f.Usage)
return
}
set.Uint(name, f.Value, f.Usage)
}
}
// Names returns the names of the flag.
func (f *UintFlag) Names() []string {
return flagNames(f)
}
// Uint64Flag is a flag that takes an unsigned 64-bit integer
type Uint64Flag struct {
Name string
Aliases []string
Value uint64
Usage string
EnvVars []string
Destination *uint64
Hidden bool
}
// String returns the usage
func (f *Uint64Flag) String() string {
return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
func (f *Uint64Flag) Apply(set *flag.FlagSet) {
if f.EnvVars != nil {
for _, envVar := range f.EnvVars {
if envVal := os.Getenv(envVar); envVal != "" {
envValInt, err := strconv.ParseUint(envVal, 0, 64)
if err == nil {
f.Value = uint64(envValInt)
break
}
}
}
}
for _, name := range f.Names() {
if f.Destination != nil {
set.Uint64Var(f.Destination, name, f.Value, f.Usage)
return
}
set.Uint64(name, f.Value, f.Usage)
}
}
// Names returns the names of the flag.
func (f *Uint64Flag) Names() []string {
return flagNames(f)
}
// DurationFlag is a flag that takes a duration specified in Go's duration // DurationFlag is a flag that takes a duration specified in Go's duration
// format: https://golang.org/pkg/time/#ParseDuration // format: https://golang.org/pkg/time/#ParseDuration
type DurationFlag struct { type DurationFlag struct {
@ -493,7 +723,6 @@ func (f *DurationFlag) Names() []string {
} }
// Float64Flag is a flag that takes an float value // Float64Flag is a flag that takes an float value
// Errors if the value provided cannot be parsed
type Float64Flag struct { type Float64Flag struct {
Name string Name string
Aliases []string Aliases []string
@ -659,9 +888,14 @@ func stringifyFlag(f Flag) string {
switch f.(type) { switch f.(type) {
case *IntSliceFlag: case *IntSliceFlag:
return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyIntSliceFlag(f.(*IntSliceFlag))) return withEnvHint(flagStringSliceField(f, "EnvVars"),
stringifyIntSliceFlag(f.(*IntSliceFlag)))
case *Int64SliceFlag:
return withEnvHint(flagStringSliceField(f, "EnvVars"),
stringifyInt64SliceFlag(f.(*Int64SliceFlag)))
case *StringSliceFlag: case *StringSliceFlag:
return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyStringSliceFlag(f.(*StringSliceFlag))) return withEnvHint(flagStringSliceField(f, "EnvVars"),
stringifyStringSliceFlag(f.(*StringSliceFlag)))
} }
placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String())
@ -701,7 +935,18 @@ func stringifyIntSliceFlag(f *IntSliceFlag) string {
} }
} }
return stringifySliceFlag(f.Usage, append([]string{f.Name}, f.Aliases...), defaultVals) return stringifySliceFlag(f.Usage, f.Names(), 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.Names(), defaultVals)
} }
func stringifyStringSliceFlag(f *StringSliceFlag) string { func stringifyStringSliceFlag(f *StringSliceFlag) string {
@ -714,7 +959,7 @@ func stringifyStringSliceFlag(f *StringSliceFlag) string {
} }
} }
return stringifySliceFlag(f.Usage, append([]string{f.Name}, f.Aliases...), defaultVals) return stringifySliceFlag(f.Usage, f.Names(), defaultVals)
} }
func stringifySliceFlag(usage string, names, defaultVals []string) string { func stringifySliceFlag(usage string, names, defaultVals []string) string {

View File

@ -192,6 +192,114 @@ func TestIntFlagApply_SetsAllNames(t *testing.T) {
expect(t, v, 5) expect(t, v, 5)
} }
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, EnvVars: []string{"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, EnvVars: []string{"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, EnvVars: []string{"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 { var durationFlagTests = []struct {
name string name string
expected string expected string
@ -287,6 +395,46 @@ func TestIntSliceFlagApply_SetsAllNames(t *testing.T) {
expect(t, err, nil) expect(t, err, nil)
} }
var int64SliceFlagTests = []struct {
name string
aliases []string
value *Int64Slice
expected string
}{
{"heads", nil, NewInt64Slice(), "--heads value\t"},
{"H", nil, NewInt64Slice(), "-H value\t"},
{"heads", []string{"H"}, NewInt64Slice(int64(2), int64(17179869184)),
"--heads value, -H value\t(default: 2, 17179869184)"},
}
func TestInt64SliceFlagHelpOutput(t *testing.T) {
for _, test := range int64SliceFlagTests {
flag := Int64SliceFlag{Name: test.name, Aliases: test.aliases, 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, EnvVars: []string{"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 { var float64FlagTests = []struct {
name string name string
expected string expected string
@ -775,6 +923,63 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
}).Run([]string{"run"}) }).Run([]string{"run"})
} }
func TestParseMultiInt64Slice(t *testing.T) {
(&App{
Flags: []Flag{
&Int64SliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewInt64Slice()},
},
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", Aliases: []string{"i"}, Value: NewInt64Slice(), EnvVars: []string{"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", Aliases: []string{"i"}, Value: NewInt64Slice(), EnvVars: []string{"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) { func TestParseMultiFloat64(t *testing.T) {
a := App{ a := App{
Flags: []Flag{ Flags: []Flag{
@ -1131,3 +1336,19 @@ func TestIntSlice_Serialized_Set(t *testing.T) {
t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1) t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
} }
} }
func TestInt64Slice_Serialized_Set(t *testing.T) {
sl0 := NewInt64Slice(int64(1), int64(2))
ser0 := sl0.Serialized()
if len(ser0) < len(slPfx) {
t.Fatalf("serialized shorter than expected: %q", ser0)
}
sl1 := NewInt64Slice(int64(3), int64(4))
sl1.Set(ser0)
if sl0.String() != sl1.String() {
t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
}
}

View File

@ -4,6 +4,7 @@ from __future__ import print_function
import argparse import argparse
import glob import glob
import os import os
import platform
import shutil import shutil
import sys import sys
import tempfile import tempfile
@ -11,6 +12,7 @@ import tempfile
from subprocess import check_call, check_output from subprocess import check_call, check_output
_WINDOWS = platform.system().lower() == 'windows'
_PACKAGE_NAME = os.environ.get( _PACKAGE_NAME = os.environ.get(
'CLI_PACKAGE_NAME', 'github.com/urfave/cli' 'CLI_PACKAGE_NAME', 'github.com/urfave/cli'
) )
@ -92,8 +94,19 @@ def _migrations():
shutil.rmtree(tmpdir, ignore_errors=True) shutil.rmtree(tmpdir, ignore_errors=True)
@_target
def _toc():
exe = ['bash'] if _WINDOWS else []
_run(exe + [
os.path.join('node_modules', '.bin', 'markdown-toc'),
'-i', 'README.md'
])
_run(['git', 'diff', '--quiet'])
def _run(command): def _run(command):
print('runtests: {}'.format(' '.join(command)), file=sys.stderr) print('runtests: {}'.format(' '.join(command)), file=sys.stderr)
sys.stderr.flush()
check_call(command) check_call(command)