-
Notifications
You must be signed in to change notification settings - Fork 0
/
env.go
129 lines (115 loc) · 3.42 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 envtags
import (
"fmt"
"os"
"reflect"
"strconv"
)
const tagName = "env"
func getIntParser(bitSize int) func(envVarValue string, v reflect.Value) error {
return func(envVarValue string, v reflect.Value) error {
intValue, err := strconv.ParseInt(envVarValue, 0, bitSize)
if err != nil {
return getError(ErrInvalidTypeConversion, err)
}
v.SetInt(intValue)
return nil
}
}
func getUIntParser(bitSize int) func(envVarValue string, v reflect.Value) error {
return func(envVarValue string, v reflect.Value) error {
uintValue, err := strconv.ParseUint(envVarValue, 0, bitSize)
if err != nil {
return getError(ErrInvalidTypeConversion, err)
}
v.SetUint(uintValue)
return nil
}
}
func getFloatParser(bitSize int) func(envVarValue string, v reflect.Value) error {
return func(envVarValue string, v reflect.Value) error {
floatValue, err := strconv.ParseFloat(envVarValue, bitSize)
if err != nil {
return getError(ErrInvalidTypeConversion, err)
}
v.SetFloat(floatValue)
return nil
}
}
func getComplexParser(bitSize int) func(envVarValue string, v reflect.Value) error {
return func(envVarValue string, v reflect.Value) error {
complexValue, err := strconv.ParseComplex(envVarValue, bitSize)
if err != nil {
return getError(ErrInvalidTypeConversion, err)
}
v.SetComplex(complexValue)
return nil
}
}
var parserByKindMap = map[reflect.Kind]func(envVarValue string, v reflect.Value) error{
reflect.Bool: func(envVarValue string, v reflect.Value) error {
if envVarValue == "" {
v.SetBool(false)
return nil
}
boolValue, err := strconv.ParseBool(envVarValue)
if err != nil {
return getError(ErrInvalidTypeConversion, err)
}
v.SetBool(boolValue)
return nil
},
reflect.String: func(envVarValue string, v reflect.Value) error {
v.SetString(envVarValue)
return nil
},
reflect.Int: getIntParser(64),
reflect.Int8: getIntParser(8),
reflect.Int16: getIntParser(16),
reflect.Int32: getIntParser(32),
reflect.Int64: getIntParser(64),
reflect.Uint: getUIntParser(64),
reflect.Uint8: getUIntParser(8),
reflect.Uint16: getUIntParser(16),
reflect.Uint32: getUIntParser(32),
reflect.Uint64: getUIntParser(32),
reflect.Float32: getFloatParser(32),
reflect.Float64: getFloatParser(64),
reflect.Complex64: getComplexParser(64),
reflect.Complex128: getComplexParser(128),
}
/*
Set receives a struct pointer and sets its fields using the value from
environment variables defined in the struct tag `env`.
*/
func Set(s interface{}) error {
return set(s, tagDetails{})
}
func set(s interface{}, details tagDetails) error {
value := reflect.ValueOf(s)
elem := value.Elem()
typeSpec := elem.Type()
for i := 0; i < elem.NumField(); i++ {
f := elem.Field(i)
fType := typeSpec.Field(i)
tagValue := fType.Tag.Get(tagName)
details := parseTagValue(tagValue).Update(details)
k := fType.Type.Kind()
if k == reflect.Struct {
if err := set(f.Addr().Interface(), details); err != nil {
return err
}
continue
}
if envVarValue, ok := os.LookupEnv(details.GetEnvVar()); ok {
parser, parserExists := parserByKindMap[fType.Type.Kind()]
if !parserExists {
return getError(ErrParserNotAvailable, fmt.Errorf("parser for %s not found", fType.Type.Kind()))
}
if err := parser(envVarValue, f); err != nil {
return fmt.Errorf("failed to parse value for field %s: %w", elem.Type().Field(i).Name, err)
}
}
}
return nil
}