feat(kit/cli): add support for int32 and int64 CLI flags

pull/20109/head
Dan Moran 2020-11-24 10:13:50 -05:00 committed by Daniel Moran
parent affb64c888
commit a952dff92d
2 changed files with 85 additions and 2 deletions

View File

@ -150,6 +150,58 @@ func BindOptions(v *viper.Viper, cmd *cobra.Command, opts []Opt) {
} }
mustBindPFlag(v, o.Flag, flagset) mustBindPFlag(v, o.Flag, flagset)
*destP = v.GetInt(envVar) *destP = v.GetInt(envVar)
case *int32:
var d int32
if o.Default != nil {
// N.B. since our CLI kit types default values as interface{} and
// literal numbers get typed as int by default, it's very easy to
// create an int32 CLI flag with an int default value.
//
// The compiler doesn't know to complain in that case, so you end up
// with a runtime panic when trying to bind the CLI options.
//
// To avoid that headache, we support both int32 and int defaults
// for int32 fields. This introduces a new runtime bomb if somebody
// specifies an int default > math.MaxInt32, but that's hopefully
// less likely.
var ok bool
d, ok = o.Default.(int32)
if !ok {
d = int32(o.Default.(int))
}
}
if hasShort {
flagset.Int32VarP(destP, o.Flag, string(o.Short), d, o.Desc)
} else {
flagset.Int32Var(destP, o.Flag, d, o.Desc)
}
mustBindPFlag(v, o.Flag, flagset)
*destP = v.GetInt32(envVar)
case *int64:
var d int64
if o.Default != nil {
// N.B. since our CLI kit types default values as interface{} and
// literal numbers get typed as int by default, it's very easy to
// create an int64 CLI flag with an int default value.
//
// The compiler doesn't know to complain in that case, so you end up
// with a runtime panic when trying to bind the CLI options.
//
// To avoid that headache, we support both int64 and int defaults
// for int64 fields.
var ok bool
d, ok = o.Default.(int64)
if !ok {
d = int64(o.Default.(int))
}
}
if hasShort {
flagset.Int64VarP(destP, o.Flag, string(o.Short), d, o.Desc)
} else {
flagset.Int64Var(destP, o.Flag, d, o.Desc)
}
mustBindPFlag(v, o.Flag, flagset)
*destP = v.GetInt64(envVar)
case *bool: case *bool:
var d bool var d bool
if o.Default != nil { if o.Default != nil {

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math"
"os" "os"
"path" "path"
"testing" "testing"
@ -40,6 +41,8 @@ func (c *customFlag) Type() string {
func ExampleNewCommand() { func ExampleNewCommand() {
var monitorHost string var monitorHost string
var number int var number int
var smallerNumber int32
var longerNumber int64
var sleep bool var sleep bool
var duration time.Duration var duration time.Duration
var stringSlice []string var stringSlice []string
@ -50,6 +53,7 @@ func ExampleNewCommand() {
for i := 0; i < number; i++ { for i := 0; i < number; i++ {
fmt.Printf("%d\n", i) fmt.Printf("%d\n", i)
} }
fmt.Println(longerNumber - int64(smallerNumber))
fmt.Println(sleep) fmt.Println(sleep)
fmt.Println(duration) fmt.Println(duration)
fmt.Println(stringSlice) fmt.Println(stringSlice)
@ -70,6 +74,18 @@ func ExampleNewCommand() {
Default: 2, Default: 2,
Desc: "number of times to loop", Desc: "number of times to loop",
}, },
{
DestP: &smallerNumber,
Flag: "smaller-number",
Default: math.MaxInt32,
Desc: "limited size number",
},
{
DestP: &longerNumber,
Flag: "longer-number",
Default: math.MaxInt64,
Desc: "explicitly expanded-size number",
},
{ {
DestP: &sleep, DestP: &sleep,
Flag: "sleep", Flag: "sleep",
@ -104,6 +120,7 @@ func ExampleNewCommand() {
// http://localhost:8086 // http://localhost:8086
// 0 // 0
// 1 // 1
// 9223372034707292160
// true // true
// 1m0s // 1m0s
// [foo bar] // [foo bar]
@ -113,8 +130,10 @@ func ExampleNewCommand() {
func Test_NewProgram(t *testing.T) { func Test_NewProgram(t *testing.T) {
testFilePath, cleanup := newConfigFile(t, map[string]string{ testFilePath, cleanup := newConfigFile(t, map[string]string{
// config values should be same as flags // config values should be same as flags
"foo": "bar", "foo": "bar",
"shoe-fly": "yadon", "shoe-fly": "yadon",
"number": "2147483647",
"long-number": "9223372036854775807",
}) })
defer cleanup() defer cleanup()
defer setEnvVar("TEST_CONFIG_PATH", testFilePath)() defer setEnvVar("TEST_CONFIG_PATH", testFilePath)()
@ -155,6 +174,8 @@ func Test_NewProgram(t *testing.T) {
var testVar string var testVar string
var testFly string var testFly string
var testNumber int32
var testLongNumber int64
program := &Program{ program := &Program{
Name: "test", Name: "test",
Opts: []Opt{ Opts: []Opt{
@ -167,6 +188,14 @@ func Test_NewProgram(t *testing.T) {
DestP: &testFly, DestP: &testFly,
Flag: "shoe-fly", Flag: "shoe-fly",
}, },
{
DestP: &testNumber,
Flag: "number",
},
{
DestP: &testLongNumber,
Flag: "long-number",
},
}, },
Run: func() error { return nil }, Run: func() error { return nil },
} }
@ -177,6 +206,8 @@ func Test_NewProgram(t *testing.T) {
require.Equal(t, tt.expected, testVar) require.Equal(t, tt.expected, testVar)
assert.Equal(t, "yadon", testFly) assert.Equal(t, "yadon", testFly)
assert.Equal(t, int32(math.MaxInt32), testNumber)
assert.Equal(t, int64(math.MaxInt64), testLongNumber)
} }
t.Run(tt.name, fn) t.Run(tt.name, fn)