forked from koding/multiconfig
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinterface.go
96 lines (85 loc) · 2.41 KB
/
interface.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 multiconfig
import (
"errors"
"reflect"
)
// InterfaceLoader satisfies the loader interface. It recursively checks if a
// struct implements the DefaultValues interface and applies the function
// depth first, if it does
// This is useful in case you want to overwrite default values set by tags
// in a nested struct
type InterfaceLoader struct {
}
type DefaultValues interface {
ApplyDefaults()
}
// structFields returns the exported fields of a struct value or pointer
// returns nil if the input is not a struct
func structFields(v reflect.Value) []reflect.Value {
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return nil
}
t := v.Type()
fields := []reflect.Value{}
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
// Saves us a bunch of checks later
if !field.IsExported() {
continue
}
fv := v.FieldByName(field.Name)
fields = append(fields, fv)
}
return fields
}
// Load will populate s by recursively calling the `ApplyDefaults` method on it
func (l *InterfaceLoader) Load(s interface{}) error {
v := reflect.ValueOf(s)
if v.Kind() != reflect.Pointer {
return errors.New("cannot load into a value: target must be a pointer")
}
if v.IsNil() {
return errors.New("cannot load into a nil pointer")
}
l.processValue(v)
return nil
}
// processValue is the actual implementation of Load
// It was split out so that the signature is more amenable for the recursion that it does
func (l *InterfaceLoader) processValue(v reflect.Value) {
for _, field := range structFields(v) {
switch field.Kind() {
case reflect.Struct:
l.processValue(field)
case reflect.Pointer:
if field.IsNil() {
field.Set(reflect.New(field.Type().Elem()))
}
if field.Elem().Kind() == reflect.Struct {
l.processValue(field)
} else {
if applyDefaults := field.MethodByName("ApplyDefaults"); applyDefaults.IsValid() {
applyDefaults.Call([]reflect.Value{})
}
}
default:
if field.CanAddr() {
if applyDefaults := field.Addr().MethodByName("ApplyDefaults"); applyDefaults.IsValid() {
applyDefaults.Call([]reflect.Value{})
}
}
}
}
if v.Kind() == reflect.Pointer {
if applyDefaults := v.MethodByName("ApplyDefaults"); applyDefaults.IsValid() {
applyDefaults.Call([]reflect.Value{})
}
} else {
if applyDefaults := v.Addr().MethodByName("ApplyDefaults"); applyDefaults.IsValid() {
applyDefaults.Call([]reflect.Value{})
}
}
}