-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathenv.go
100 lines (81 loc) · 2.57 KB
/
env.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
97
98
99
100
package alligotor
import (
"os"
"strings"
)
const (
envKey = "env"
defaultEnvSeparator = "_"
)
// EnvSource is used to read the configuration from environment variables.
// prefix can be defined to look for environment variables with a certain prefix.
// separator is used for nested structs and also for the Prefix.
// As an example:
// If prefix is set to "example", the separator is set to "_" and the config struct's field is named Port,
// it will by default look for the environment variable "EXAMPLE_PORT".
type EnvSource struct {
prefix string
separator string
envMap map[string]string
}
// NewEnvSource returns a new EnvSource.
// prefix defines the prefix to be prepended to the automatically generated names when looking for
// the environment variables.
// prefix can be empty.
// It accepts an EnvOption to override the default env separator.
func NewEnvSource(prefix string, opts ...EnvOption) *EnvSource {
env := &EnvSource{
prefix: prefix,
separator: defaultEnvSeparator,
}
for _, opt := range opts {
opt(env)
}
return env
}
// EnvOption takes an EnvSource as input and modifies it.
type EnvOption func(*EnvSource)
// WithEnvSeparator adds a custom separator to an EnvSource struct.
func WithEnvSeparator(separator string) EnvOption {
return func(env *EnvSource) {
env.separator = separator
}
}
// Init initializes the envMap property.
// It should be used right before calling the Read method to load the latest environment variables.
func (s *EnvSource) Init(_ []Field) error {
s.envMap = getEnvAsMap()
return nil
}
// Read reads the saved environment variables from the Init function and returns the set value for a certain field.
// If not value is set in the flags it returns nil.
func (s *EnvSource) Read(field *Field) (interface{}, error) {
return readEnv(field, s.prefix, s.envMap, s.separator), nil
}
func readEnv(f *Field, prefix string, envMap map[string]string, separator string) []byte {
name := extractEnvName(f)
distinctEnvName := strings.Join(append(f.BaseNames(extractEnvName), name), separator)
if prefix != "" {
distinctEnvName = prefix + separator + distinctEnvName
}
envVal, ok := envMap[strings.ToUpper(distinctEnvName)]
if !ok {
return nil
}
return []byte(envVal)
}
func extractEnvName(f *Field) string {
if f.Configs()[envKey] != "" {
return f.Configs()[envKey]
}
return f.Name()
}
func getEnvAsMap() map[string]string {
envMap := map[string]string{}
envKeyVal := os.Environ()
for _, keyVal := range envKeyVal {
split := strings.SplitN(keyVal, "=", 2)
envMap[split[0]] = split[1]
}
return envMap
}