70 lines
1.9 KiB
Go
70 lines
1.9 KiB
Go
package config
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
// BindEnvVars walks the config struct and binds each field to an environment variable.
|
|
// This allows single-underscore env vars like `$DATA_MESHER_CLUSTER_JOIN_INTERVAL` instead of
|
|
// `$DATA_MESHER_CLUSTER__JOIN_INTERVAL`.
|
|
func BindEnvVars(v *viper.Viper, prefix string, cfg any) {
|
|
bindEnvVarsRecursive(v, prefix, "", reflect.TypeOf(cfg))
|
|
}
|
|
|
|
// hasMapstructureTags returns true if the struct type has at least one field with a mapstructure tag.
|
|
// This distinguishes config sub-sections (HTTP, Cluster) from domain types (crypto.PrivateKey, crypto.Certificate)
|
|
// that should be treated as leaf values when binding env vars.
|
|
func hasMapstructureTags(t reflect.Type) bool {
|
|
for field := range t.Fields() {
|
|
tag := field.Tag.Get("mapstructure")
|
|
if tag != "" && tag != "-" {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func bindEnvVarsRecursive(v *viper.Viper, envPrefix, keyPrefix string, t reflect.Type) {
|
|
if t.Kind() == reflect.Pointer {
|
|
t = t.Elem()
|
|
}
|
|
|
|
if t.Kind() != reflect.Struct {
|
|
return
|
|
}
|
|
|
|
for field := range t.Fields() {
|
|
// get the mapstructure tag, skip if not present
|
|
tag := field.Tag.Get("mapstructure")
|
|
if tag == "" || tag == "-" {
|
|
continue
|
|
}
|
|
|
|
// build the viper key path (dot-separated)
|
|
viperKey := tag
|
|
if keyPrefix != "" {
|
|
viperKey = keyPrefix + "." + tag
|
|
}
|
|
|
|
// build the env var name (underscore-separated, uppercase)
|
|
envVar := strings.ToUpper(envPrefix + "_" + strings.ReplaceAll(viperKey, ".", "_"))
|
|
|
|
fieldType := field.Type
|
|
if fieldType.Kind() == reflect.Pointer {
|
|
fieldType = fieldType.Elem()
|
|
}
|
|
|
|
if fieldType.Kind() == reflect.Struct && hasMapstructureTags(fieldType) {
|
|
// recurse into config sub-sections (structs with mapstructure tags)
|
|
bindEnvVarsRecursive(v, envPrefix, viperKey, fieldType)
|
|
} else {
|
|
// bind leaf field to env var
|
|
_ = v.BindEnv(viperKey, envVar)
|
|
}
|
|
}
|
|
}
|