Files
brianmcgee e3a7790e57
buildbot/nix-eval Build done.
PR Size Review Check / pr-size-review-gate (pull_request) Successful in 24s
sizelint / sizelint (pull_request) Successful in 2m12s
buildbot/nix-build Build done.
gitea-mq Already up to date with target branch
fix breaks in gofumpt and golangci-lint from updating nixpkgs
2026-07-07 16:32:14 +01:00

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)
}
}
}