-
Notifications
You must be signed in to change notification settings - Fork 64
/
env.go
129 lines (107 loc) · 3.28 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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package multiconfig
import (
"fmt"
"os"
"sort"
"strings"
"github.com/fatih/camelcase"
"github.com/fatih/structs"
)
// EnvironmentLoader satisifies the loader interface. It loads the
// configuration from the environment variables in the form of
// STRUCTNAME_FIELDNAME.
type EnvironmentLoader struct {
// Prefix prepends given string to every environment variable
// {STRUCTNAME}_FIELDNAME will be {PREFIX}_FIELDNAME
Prefix string
// CamelCase adds a separator for field names in camelcase form. A
// fieldname of "AccessKey" would generate a environment name of
// "STRUCTNAME_ACCESSKEY". If CamelCase is enabled, the environment name
// will be generated in the form of "STRUCTNAME_ACCESS_KEY"
CamelCase bool
}
func (e *EnvironmentLoader) getPrefix(s *structs.Struct) string {
if e.Prefix != "" {
return e.Prefix
}
return s.Name()
}
// Load loads the source into the config defined by struct s
func (e *EnvironmentLoader) Load(s interface{}) error {
strct := structs.New(s)
strctMap := strct.Map()
prefix := e.getPrefix(strct)
for key, val := range strctMap {
field := strct.Field(key)
if err := e.processField(prefix, field, key, val); err != nil {
return err
}
}
return nil
}
// processField gets leading name for the env variable and combines the current
// field's name and generates environment variable names recursively
func (e *EnvironmentLoader) processField(prefix string, field *structs.Field, name string, strctMap interface{}) error {
fieldName := e.generateFieldName(prefix, name)
switch strctMap.(type) {
case map[string]interface{}:
for key, val := range strctMap.(map[string]interface{}) {
field := field.Field(key)
if err := e.processField(fieldName, field, key, val); err != nil {
return err
}
}
default:
v := os.Getenv(fieldName)
if v == "" {
return nil
}
if err := fieldSet(field, v); err != nil {
return err
}
}
return nil
}
// PrintEnvs prints the generated environment variables to the std out.
func (e *EnvironmentLoader) PrintEnvs(s interface{}) {
strct := structs.New(s)
strctMap := strct.Map()
prefix := e.getPrefix(strct)
keys := make([]string, 0, len(strctMap))
for key := range strctMap {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
field := strct.Field(key)
e.printField(prefix, field, key, strctMap[key])
}
}
// printField prints the field of the config struct for the flag.Usage
func (e *EnvironmentLoader) printField(prefix string, field *structs.Field, name string, strctMap interface{}) {
fieldName := e.generateFieldName(prefix, name)
switch strctMap.(type) {
case map[string]interface{}:
smap := strctMap.(map[string]interface{})
keys := make([]string, 0, len(smap))
for key := range smap {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
field := field.Field(key)
e.printField(fieldName, field, key, smap[key])
}
default:
fmt.Println(" ", fieldName)
}
}
// generateFieldName generates the field name combined with the prefix and the
// struct's field name
func (e *EnvironmentLoader) generateFieldName(prefix string, name string) string {
fieldName := strings.ToUpper(name)
if e.CamelCase {
fieldName = strings.ToUpper(strings.Join(camelcase.Split(name), "_"))
}
return strings.ToUpper(prefix) + "_" + fieldName
}