feedizer/cmd/feedizer/config/env.go

78 lines
1.8 KiB
Go

package config
import (
"fmt"
"os"
"reflect"
"strconv"
"strings"
)
func ReadEnv(c *Config, prefix string) (namesAvailable, namesUsed []string) {
return structFromEnv(reflect.ValueOf(c), reflect.TypeOf(c), prefix)
}
func structFromEnv(v reflect.Value, t reflect.Type, prefix string) (namesAvailable, namesUsed []string) {
if v.Kind() == reflect.Ptr {
v = v.Elem()
t = t.Elem()
}
for i := 0; i < v.NumField(); i++ {
fv := v.Field(i)
ft := t.Field(i)
tag := ft.Tag.Get("env")
if !ft.IsExported() || tag == "-" {
continue
}
name := ft.Name
if tag != "" {
name = tag
}
if fv.Kind() == reflect.Struct {
na, nu := structFromEnv(fv, ft.Type, prefix+"_"+name)
namesAvailable = append(namesAvailable, na...)
namesUsed = append(namesUsed, nu...)
} else if !ft.Anonymous {
envName := strings.ToUpper(prefix + "_" + name)
namesAvailable = append(namesAvailable, envName)
val, ok := os.LookupEnv(envName)
if !ok {
continue
}
if !fv.CanSet() {
panic("cannot set field for environment variable " + envName)
}
switch fk := fv.Kind(); fk {
case reflect.String:
fv.SetString(val)
case reflect.Int:
x, err := strconv.ParseInt(val, 10, 0)
if err != nil {
panic(err)
}
fv.SetInt(x)
case reflect.Bool:
switch strings.ToLower(val) {
case "1", "true", "t", "on":
fv.SetBool(true)
case "0", "false", "f", "off":
fv.SetBool(false)
default:
panic(fmt.Sprintf("cannot set field for environment variable %s: cannot parse %s as boolean. Please use one of: 1, true, t, on / 0, false, f, off", envName, fk))
}
default:
panic(fmt.Sprintf("cannot set field for environment variable %s: type %s is not supported", envName, fk))
}
namesUsed = append(namesUsed, envName)
}
}
return namesAvailable, namesUsed
}