forked from BoRuDar/configuration
/
configurator.go
117 lines (98 loc) · 2.86 KB
/
configurator.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
// Package configuration provides ability to initialize your custom configuration struct from: flags, environment variables, `default` tag, files (toml)
package configuration
import (
"errors"
"fmt"
"log"
"reflect"
)
// New creates a new instance of the configurator.
func New(
cfgPtr interface{}, // must be a pointer to a struct
providers ...Provider, // providers will be executed in order of their declaration
) (configurator, error) {
if len(providers) == 0 {
return configurator{}, errors.New("providers not found")
}
if reflect.TypeOf(cfgPtr).Kind() != reflect.Ptr {
return configurator{}, errors.New("not a pointer to the struct")
}
return configurator{
config: cfgPtr,
providers: providers,
loggerFn: log.Printf,
onFailToSetField: func(err error) {
log.Fatal(err)
},
loggingEnabled: false,
}, nil
}
type configurator struct {
config interface{}
providers []Provider
onFailToSetField func(err error)
loggerFn func(format string, v ...interface{})
loggingEnabled bool
}
// InitValues sets values into struct field using given set of providers
// respecting their order: first defined -> first executed
func (c configurator) InitValues() {
c.fillUp(c.config)
}
// SetLogger changes logger
func (c *configurator) SetLogger(l func(format string, v ...interface{})) {
c.loggerFn = l
return
}
// SetLogger changes logger
func (c *configurator) EnableLogging(enable bool) {
c.loggingEnabled = enable
return
}
// SetOnFailFn sets function which will be called when no value set into the field
func (c *configurator) SetOnFailFn(fn func(error)) {
c.onFailToSetField = fn
}
func (c configurator) fillUp(i interface{}, parentPath ...string) {
var (
t = reflect.TypeOf(i)
v = reflect.ValueOf(i)
)
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
for i := 0; i < t.NumField(); i++ {
var (
tField = t.Field(i)
vField = v.Field(i)
currentPath = append(parentPath, tField.Name)
)
if tField.Type.Kind() == reflect.Struct {
c.fillUp(vField.Addr().Interface(), currentPath...)
continue
}
if tField.Type.Kind() == reflect.Ptr && tField.Type.Elem().Kind() == reflect.Struct {
vField.Set(reflect.New(tField.Type.Elem()))
c.fillUp(vField.Interface(), currentPath...)
continue
}
c.applyProviders(tField, vField, currentPath)
}
}
func (c configurator) applyProviders(field reflect.StructField, v reflect.Value, currentPath []string) {
c.logf("configurator: current path: %v", currentPath)
for _, provider := range c.providers {
err := provider.Provide(field, v, currentPath...)
if err == nil {
return
}
c.logf("configurator: %v", err)
}
c.onFailToSetField(fmt.Errorf("configurator: field [%s] with tags [%v] cannot be set", field.Name, field.Tag))
}
func (c configurator) logf(format string, v ...interface{}) {
if c.loggingEnabled {
c.loggerFn(format, v...)
}
}