mediamtx/internal/conf/env.go

156 lines
3.0 KiB
Go
Raw Normal View History

2021-09-26 15:07:48 +00:00
package conf
import (
"fmt"
"os"
"reflect"
"strconv"
"strings"
)
type envUnmarshaler interface {
unmarshalEnv(string) error
}
2021-09-26 15:07:48 +00:00
func loadEnvInternal(env map[string]string, prefix string, rv reflect.Value) error {
rt := rv.Type()
if i, ok := rv.Addr().Interface().(envUnmarshaler); ok {
2021-05-09 15:51:38 +00:00
if ev, ok := env[prefix]; ok {
err := i.unmarshalEnv(ev)
if err != nil {
2021-05-09 15:51:38 +00:00
return fmt.Errorf("%s: %s", prefix, err)
}
}
return nil
}
switch rt {
case reflect.TypeOf(""):
2021-05-09 15:51:38 +00:00
if ev, ok := env[prefix]; ok {
rv.SetString(ev)
}
return nil
case reflect.TypeOf(int(0)):
2021-05-09 15:51:38 +00:00
if ev, ok := env[prefix]; ok {
iv, err := strconv.ParseInt(ev, 10, 64)
if err != nil {
2021-05-09 15:51:38 +00:00
return fmt.Errorf("%s: %s", prefix, err)
}
rv.SetInt(iv)
}
return nil
case reflect.TypeOf(uint64(0)):
2021-05-09 15:51:38 +00:00
if ev, ok := env[prefix]; ok {
2021-01-10 11:55:53 +00:00
iv, err := strconv.ParseUint(ev, 10, 64)
if err != nil {
2021-05-09 15:51:38 +00:00
return fmt.Errorf("%s: %s", prefix, err)
2021-01-10 11:55:53 +00:00
}
rv.SetUint(iv)
}
return nil
case reflect.TypeOf(float64(0)):
if ev, ok := env[prefix]; ok {
iv, err := strconv.ParseFloat(ev, 64)
if err != nil {
return fmt.Errorf("%s: %s", prefix, err)
}
rv.SetFloat(iv)
}
return nil
case reflect.TypeOf(bool(false)):
2021-05-09 15:51:38 +00:00
if ev, ok := env[prefix]; ok {
switch strings.ToLower(ev) {
case "yes", "true":
rv.SetBool(true)
case "no", "false":
rv.SetBool(false)
default:
2021-05-09 15:51:38 +00:00
return fmt.Errorf("%s: invalid value '%s'", prefix, ev)
}
}
return nil
}
switch rt.Kind() {
case reflect.Map:
for k := range env {
2021-05-09 15:51:38 +00:00
if !strings.HasPrefix(k, prefix+"_") {
continue
}
2021-05-09 15:51:38 +00:00
mapKey := strings.Split(k[len(prefix+"_"):], "_")[0]
2020-10-14 07:48:54 +00:00
if len(mapKey) == 0 {
continue
}
2020-10-30 22:08:09 +00:00
// allow only keys in uppercase
if mapKey != strings.ToUpper(mapKey) {
continue
}
// initialize only if there's at least one key
if rv.IsNil() {
rv.Set(reflect.MakeMap(rt))
}
2020-10-30 22:08:09 +00:00
mapKeyLower := strings.ToLower(mapKey)
nv := rv.MapIndex(reflect.ValueOf(mapKeyLower))
zero := reflect.Value{}
if nv == zero {
nv = reflect.New(rt.Elem().Elem())
2020-10-30 22:08:09 +00:00
rv.SetMapIndex(reflect.ValueOf(mapKeyLower), nv)
}
2021-09-26 15:07:48 +00:00
err := loadEnvInternal(env, prefix+"_"+mapKey, nv.Elem())
if err != nil {
return err
}
}
return nil
case reflect.Struct:
flen := rt.NumField()
for i := 0; i < flen; i++ {
2020-10-13 22:50:08 +00:00
f := rt.Field(i)
2020-10-19 20:17:48 +00:00
// load only public fields
2021-09-26 15:07:48 +00:00
if f.Tag.Get("json") == "-" {
continue
}
2021-09-26 15:07:48 +00:00
err := loadEnvInternal(env, prefix+"_"+strings.ToUpper(f.Name), rv.Field(i))
if err != nil {
return err
}
}
return nil
2022-12-15 23:50:47 +00:00
case reflect.Slice:
if rt.Elem() == reflect.TypeOf("") {
if ev, ok := env[prefix]; ok {
rv.Set(reflect.ValueOf(strings.Split(ev, ",")))
}
return nil
}
}
return fmt.Errorf("unsupported type: %v", rt)
}
2021-09-26 15:07:48 +00:00
func loadFromEnvironment(prefix string, v interface{}) error {
env := make(map[string]string)
for _, kv := range os.Environ() {
tmp := strings.SplitN(kv, "=", 2)
env[tmp[0]] = tmp[1]
}
2021-09-26 15:07:48 +00:00
return loadEnvInternal(env, prefix, reflect.ValueOf(v).Elem())
}