diff --git a/kit/cli/viper.go b/kit/cli/viper.go index 225877885d..118a2c8300 100644 --- a/kit/cli/viper.go +++ b/kit/cli/viper.go @@ -150,6 +150,58 @@ func BindOptions(v *viper.Viper, cmd *cobra.Command, opts []Opt) { } mustBindPFlag(v, o.Flag, flagset) *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: var d bool if o.Default != nil { diff --git a/kit/cli/viper_test.go b/kit/cli/viper_test.go index 28b5010fad..440ae0b425 100644 --- a/kit/cli/viper_test.go +++ b/kit/cli/viper_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "math" "os" "path" "testing" @@ -40,6 +41,8 @@ func (c *customFlag) Type() string { func ExampleNewCommand() { var monitorHost string var number int + var smallerNumber int32 + var longerNumber int64 var sleep bool var duration time.Duration var stringSlice []string @@ -50,6 +53,7 @@ func ExampleNewCommand() { for i := 0; i < number; i++ { fmt.Printf("%d\n", i) } + fmt.Println(longerNumber - int64(smallerNumber)) fmt.Println(sleep) fmt.Println(duration) fmt.Println(stringSlice) @@ -70,6 +74,18 @@ func ExampleNewCommand() { Default: 2, 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, Flag: "sleep", @@ -104,6 +120,7 @@ func ExampleNewCommand() { // http://localhost:8086 // 0 // 1 + // 9223372034707292160 // true // 1m0s // [foo bar] @@ -113,8 +130,10 @@ func ExampleNewCommand() { func Test_NewProgram(t *testing.T) { testFilePath, cleanup := newConfigFile(t, map[string]string{ // config values should be same as flags - "foo": "bar", - "shoe-fly": "yadon", + "foo": "bar", + "shoe-fly": "yadon", + "number": "2147483647", + "long-number": "9223372036854775807", }) defer cleanup() defer setEnvVar("TEST_CONFIG_PATH", testFilePath)() @@ -155,6 +174,8 @@ func Test_NewProgram(t *testing.T) { var testVar string var testFly string + var testNumber int32 + var testLongNumber int64 program := &Program{ Name: "test", Opts: []Opt{ @@ -167,6 +188,14 @@ func Test_NewProgram(t *testing.T) { DestP: &testFly, Flag: "shoe-fly", }, + { + DestP: &testNumber, + Flag: "number", + }, + { + DestP: &testLongNumber, + Flag: "long-number", + }, }, Run: func() error { return nil }, } @@ -177,6 +206,8 @@ func Test_NewProgram(t *testing.T) { require.Equal(t, tt.expected, testVar) assert.Equal(t, "yadon", testFly) + assert.Equal(t, int32(math.MaxInt32), testNumber) + assert.Equal(t, int64(math.MaxInt64), testLongNumber) } t.Run(tt.name, fn)