-
Notifications
You must be signed in to change notification settings - Fork 0
/
viperenv.go
96 lines (90 loc) · 3.34 KB
/
viperenv.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package viperenv
import (
"fmt"
"reflect"
"strings"
"github.com/spf13/viper"
)
// BindOptions represents various options for binding env vars to the config.
type BindOptions struct {
AutoEnv bool // Whether to call viper.AutomaticEnv() or not.
EnvPrefix string // The env prefix; calls viper.SetEnvPrefix() if not empty.
AllowEmptyEnv bool // Whether to call viper.AllowEmptyEnv() or not.
EnvKeyReplacer *strings.Replacer // The env key replacer; calls viper.SetEnvKeyReplacer() if not nil.
}
// Bind binds the env vars to the config.
//
// Parameters:
// - outPtr: The pointer to the config struct.
// - v: The viper instance.
// - options: The options.
func Bind(outPtr interface{}, v *viper.Viper, options BindOptions) error {
// Viper defaults.
if options.AutoEnv {
v.AutomaticEnv()
}
if options.EnvPrefix != "" {
v.SetEnvPrefix(options.EnvPrefix)
}
if options.AllowEmptyEnv {
v.AllowEmptyEnv(true)
}
if options.EnvKeyReplacer != nil {
v.SetEnvKeyReplacer(options.EnvKeyReplacer)
}
// Bind the env vars to the config.
return bind(outPtr, v)
}
// bind binds the env vars to the config.
func bind(cfg interface{}, v *viper.Viper) error {
val := reflect.ValueOf(cfg).Elem() // Get the value of the config struct.
t := val.Type() // Get the type of the config struct.
switch t.Kind() { // Switch on the kind of the config struct.
case reflect.Ptr, reflect.Interface: // If the config struct is a pointer or interface, call the function recursively.
return bind(val.Interface(), v)
case reflect.Struct: // If the config struct is a struct, iterate over its fields.
for i := 0; i < t.NumField(); i++ { // Iterate over the fields of the config struct.
field := t.Field(i) // Get the field.
if field.Type.Kind() == reflect.Struct { // If the field is a struct, call the function recursively.
if err := bind(val.Field(i).Addr().Interface(), v); err != nil {
return err
}
} else {
// If the field is not a struct, get the env var and bind it to the config.
// Get the env tag e.g. `env:"DATABASE_URL,required,default=default-value"`
envTag := field.Tag.Get("env")
// Split the env tag by comma.
envTagSplit := strings.Split(envTag, ",")
envVar := envTagSplit[0] // Get the env var e.g. `DATABASE_URL`
required, defaultValue := false, ""
if len(envTagSplit) > 1 {
for _, tag := range envTagSplit[1:] {
switch tag {
case "required": // If the tag is `required`, set required to true.
required = true
default: // If the tag is not `required`, check if it starts with `default=`. If so, set the default value.
if strings.HasPrefix(tag, "default=") {
defaultValue = strings.TrimPrefix(tag, "default=")
}
}
}
}
switch envVar {
case "-", "": // If the env var is "-" or "", skip it.
continue
default: // If the env var is not "-" or "", bind it to the config.
v.BindEnv(envVar)
if required && v.GetString(envVar) == "" {
return fmt.Errorf("required environment variable %s is not set", envVar)
}
if v.GetString(envVar) == "" && defaultValue != "" {
v.Set(envVar, defaultValue)
}
}
}
}
default: // If the config struct is not a struct, pointer, or interface, return.
return nil
}
return nil
}