67 lines
1.8 KiB
Go
67 lines
1.8 KiB
Go
package config
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/mitchellh/mapstructure"
|
|
"github.com/spf13/cast"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
// BindStructEnv binds struct using environment variables.
|
|
// Note: borrowed, see https://github.com/spf13/viper/pull/1429
|
|
func BindStructEnv(input interface{}) error {
|
|
envKeysMap := map[string]interface{}{}
|
|
if err := mapstructure.Decode(input, &envKeysMap); err != nil {
|
|
return err
|
|
}
|
|
|
|
structKeys := flattenAndMergeMap(map[string]bool{}, envKeysMap, "")
|
|
for key := range structKeys {
|
|
if err := viper.BindEnv(key); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// FlattenAndMergeMap recursively flattens the given map into a map[string]bool
|
|
// of key paths (used as a set, easier to manipulate than a []string):
|
|
// - each path is merged into a single key string, delimited with v.keyDelim
|
|
// - if a path is shadowed by an earlier value in the initial shadow map,
|
|
// it is skipped.
|
|
//
|
|
// The resulting set of paths is merged to the given shadow set at the same time.
|
|
// Note: borrowed, see https://github.com/spf13/viper/pull/1429
|
|
func flattenAndMergeMap(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)
|
|
}
|
|
|
|
var m2 map[string]any
|
|
if prefix != "" {
|
|
prefix += "." // assuming that delimiter is a dot.
|
|
}
|
|
for k, val := range m {
|
|
fullKey := prefix + k
|
|
switch val := val.(type) {
|
|
case map[string]any:
|
|
m2 = val
|
|
case map[any]any:
|
|
m2 = cast.ToStringMap(val)
|
|
default:
|
|
// immediate value
|
|
shadow[strings.ToLower(fullKey)] = true
|
|
continue
|
|
}
|
|
// recursively merge to shadow map
|
|
shadow = flattenAndMergeMap(shadow, m2, fullKey)
|
|
}
|
|
return shadow
|
|
}
|