Skip to content

Commit

Permalink
Add UintFlag, Uint64Flag types and supporting code
Browse files Browse the repository at this point in the history
  • Loading branch information
meatballhat committed Jun 16, 2016
1 parent b7750a0 commit a0e694e
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 7 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
- `./runtests` test runner with coverage tracking by default
- testing on OS X
- testing on Windows
- `Int64Flag` type and supporting code
- `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code

### Changed
- Use spaces for alignment in help/usage output instead of tabs, making the
Expand Down
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS
--version Shows version information
--version Shows version information
```

### Arguments
Expand Down Expand Up @@ -984,7 +984,7 @@ func main() {
demonstration purposes. Use of one's imagination is encouraged.

<!-- {
"output": "made it!\nPhew!"
"output": "made it!\nPhew!"
} -->
``` go
package main
Expand Down Expand Up @@ -1036,6 +1036,19 @@ func (w *hexWriter) Write(p []byte) (int, error) {
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"
Expand Down Expand Up @@ -1105,7 +1118,17 @@ func main() {
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
Expand Down Expand Up @@ -1182,10 +1205,14 @@ func main() {
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"))
Expand Down
58 changes: 55 additions & 3 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ func (c *Context) Int64(name string) int64 {
return lookupInt64(name, c.flagSet)
}

// Uint looks up the value of a local int flag, returns 0 if no int flag exists
func (c *Context) Uint(name string) uint {
return lookupUint(name, c.flagSet)
}

// Uint64 looks up the value of a local int flag, returns 0 if no int flag exists
func (c *Context) Uint64(name string) uint64 {
return lookupUint64(name, c.flagSet)
}

// Duration 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 {
Expand Down Expand Up @@ -103,6 +113,22 @@ func (c *Context) GlobalInt64(name string) int64 {
return 0
}

// GlobalUint looks up the value of a global int flag, returns 0 if no int flag exists
func (c *Context) GlobalUint(name string) uint {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupUint(name, fs)
}
return 0
}

// GlobalUint64 looks up the value of a global int flag, returns 0 if no int flag exists
func (c *Context) GlobalUint64(name string) uint64 {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupUint64(name, fs)
}
return 0
}

// GlobalFloat64 looks up the value of a global float64 flag, returns float64(0)
// if no float64 flag exists
func (c *Context) GlobalFloat64(name string) float64 {
Expand Down Expand Up @@ -334,11 +360,11 @@ func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet {
func lookupInt(name string, set *flag.FlagSet) int {
f := set.Lookup(name)
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 val
return int(val)
}

return 0
Expand All @@ -347,7 +373,33 @@ func lookupInt(name string, set *flag.FlagSet) int {
func lookupInt64(name string, set *flag.FlagSet) int64 {
f := set.Lookup(name)
if f != nil {
val, err := strconv.ParseInt(f.Value.String(), 10, 64)
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 {
return 0
}
Expand Down
24 changes: 23 additions & 1 deletion context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,29 @@ import (
func TestNewContext(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Int("myflag", 12, "doc")
set.Int("myflagInt64", 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)
command := Command{Name: "mycommand"}
c := NewContext(nil, set, globalCtx)
c.Command = command
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")
}
Expand All @@ -42,6 +50,20 @@ func TestContext_Int64(t *testing.T) {
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")
Expand Down
88 changes: 88 additions & 0 deletions flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,94 @@ func (f Int64Flag) GetName() string {
return f.Name
}

// UintFlag is a flag that takes an unsigned integer
type UintFlag struct {
Name string
Value uint
Usage string
EnvVar 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.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
envValInt, err := strconv.ParseUint(envVal, 0, 64)
if err == nil {
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)
})
}

// GetName returns the name of the flag.
func (f UintFlag) GetName() string {
return f.Name
}

// Uint64Flag is a flag that takes an unsigned 64-bit integer
type Uint64Flag struct {
Name string
Value uint64
Usage string
EnvVar 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.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
envValInt, err := strconv.ParseUint(envVal, 0, 64)
if err == nil {
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)
})
}

// GetName returns the name of the flag.
func (f Uint64Flag) GetName() string {
return f.Name
}

// DurationFlag is a flag that takes a duration specified in Go's duration
// format: https://golang.org/pkg/time/#ParseDuration
type DurationFlag struct {
Expand Down
72 changes: 72 additions & 0 deletions flag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,78 @@ func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) {
}
}

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 {
name string
expected string
Expand Down

0 comments on commit a0e694e

Please sign in to comment.