diff --git a/viper.go b/viper.go index b658ffd..bb43c14 100644 --- a/viper.go +++ b/viper.go @@ -1149,19 +1149,11 @@ func unmarshalPostProcess(input any, opts ...DecoderConfigOption) error { return err } - v.postProcessingSliceFields(map[string]bool{}, structKeyMap, "") + v.postProcessingSliceFields(structKeyMap, "") return nil } -// TODO remove shadow -func (v *Viper) postProcessingSliceFields(shadow map[string]bool, m map[string]any, prefix string) map[string]bool { - if shadow != nil && prefix != "" && shadow[prefix] { - // prefix is shadowed => nothing more to flatten - return shadow - } - if shadow == nil { - shadow = make(map[string]bool) - } +func (v *Viper) postProcessingSliceFields(m map[string]any, prefix string) { var m2 map[string]any if prefix != "" { @@ -1173,21 +1165,29 @@ func (v *Viper) postProcessingSliceFields(shadow map[string]bool, m map[string]a if valValue.Kind() == reflect.Slice { for i := 0; i < valValue.Len(); i++ { item := valValue.Index(i) - if item.Kind() != reflect.Struct || !item.CanSet() { + iStr := strconv.FormatInt(int64(i), 10) + + fmt.Printf("item %v\n", item) + if !item.CanSet() { continue } - itemType := item.Type() - for j := 0; j < item.NumField(); j++ { - field := itemType.Field(j) - // fmt.Printf("Field %d: Name=%s, Type=%v, Value=%v\n", j, field.Name, field.Type, item.Field(j).Interface()) - - sliceKey := fmt.Sprintf("%s%s%s%d%s%s", prefix, k, v.keyDelim, i, v.keyDelim, field.Name) - shadow[strings.ToLower(sliceKey)] = true - // fmt.Printf("%s is slice\n", sliceKey) + if item.Kind() == reflect.Struct { + itemType := item.Type() + for j := 0; j < item.NumField(); j++ { + field := itemType.Field(j) + sliceKey := prefix + k + v.keyDelim + iStr + v.keyDelim + field.Name + // fmt.Printf("%s is slice\n", sliceKey) + if val, ok := v.getEnv(v.mergeWithEnvPrefix(sliceKey)); ok { + // fmt.Printf("Val is %v\n", val) + item.Field(j).SetString(val) + } + } + } else { + sliceKey := prefix + k + v.keyDelim + iStr if val, ok := v.getEnv(v.mergeWithEnvPrefix(sliceKey)); ok { - // fmt.Printf("Val is %v\n", val) - item.Field(j).SetString(val) + intValue, _ := strconv.ParseInt(val, 10, 32) + item.SetInt(intValue) } } } @@ -1202,9 +1202,8 @@ func (v *Viper) postProcessingSliceFields(shadow map[string]bool, m map[string]a continue } // recursively merge to shadow map - shadow = v.postProcessingSliceFields(shadow, m2, fullKey) + v.postProcessingSliceFields(m2, fullKey) } - return shadow } func (v *Viper) decodeStructKeys(input any, opts ...DecoderConfigOption) ([]string, error) { diff --git a/viper_test.go b/viper_test.go index 30460b0..0375300 100644 --- a/viper_test.go +++ b/viper_test.go @@ -2611,6 +2611,10 @@ name: Steve port: 8080 auth: secret: 88888-88888 +modes: + - 1 + - 2 + - 3 clients: - name: foo - name: bar @@ -2641,6 +2645,7 @@ func TestSliceIndexAutomaticEnv(t *testing.T) { Port int Name string Auth AuthConfig + Modes []int Clients []ClientConfig Proxy ProxyConfig } @@ -2655,10 +2660,12 @@ func TestSliceIndexAutomaticEnv(t *testing.T) { assert.Equal(t, "foo", v.GetString("clients.0.name")) assert.Equal(t, "bar", v.GetString("clients.1.name")) assert.Equal(t, "proxy_foo", v.GetString("proxy.clients.0.name")) + assert.Equal(t, []int{1, 2, 3}, v.GetIntSlice("modes")) // Override with env variable t.Setenv("NAME", "Steven") t.Setenv("AUTH_SECRET", "99999-99999") + t.Setenv("MODES_2", "300") t.Setenv("CLIENTS_1_NAME", "baz") t.Setenv("PROXY_CLIENTS_0_NAME", "ProxyFoo") @@ -2672,6 +2679,7 @@ func TestSliceIndexAutomaticEnv(t *testing.T) { assert.Equal(t, "Steven", config.Name) assert.Equal(t, 8080, config.Port) assert.Equal(t, "99999-99999", config.Auth.Secret) + assert.Equal(t, []int{1, 2, 300}, config.Modes) assert.Equal(t, "foo", config.Clients[0].Name) assert.Equal(t, "baz", config.Clients[1].Name) assert.Equal(t, "ProxyFoo", config.Proxy.Clients[0].Name)