From f8347a97c68c1b2a11f418da0225953c9b007708 Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Sun, 8 Jan 2017 18:11:26 -0500 Subject: [PATCH 1/2] Fix altsrc slice inputs so that they correctly parse Was previously attempting to cast directly from []interface{} to []string or []int which Go doesn't support. Instead, we iterate over and cast each value (error'ing if the value is not the correct format). Fixes #580 --- altsrc/flag_test.go | 12 +++---- altsrc/map_input_source.go | 70 +++++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index 9e9c96d..cd18294 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -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", }) diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index b720995..b3169e0 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -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 From e87dfbb6bb85250206841a9576e51b1323910356 Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Mon, 16 Jan 2017 17:34:12 -0800 Subject: [PATCH 2/2] altsrc: Support slices for TOML --- altsrc/toml_file_loader.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/altsrc/toml_file_loader.go b/altsrc/toml_file_loader.go index 39c124f..37870fc 100644 --- a/altsrc/toml_file_loader.go +++ b/altsrc/toml_file_loader.go @@ -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()) }