-
Notifications
You must be signed in to change notification settings - Fork 0
/
injector.go
112 lines (99 loc) · 2.14 KB
/
injector.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
package refinject
import (
"fmt"
"reflect"
)
type objEntry struct {
l labelSet
rv reflect.Value
}
// injector is a context of injection.
type injector struct {
reg *Registry
omap map[reflect.Type][]*objEntry
}
func newInjector(reg *Registry) *injector {
return &injector{
reg: reg,
omap: make(map[reflect.Type][]*objEntry),
}
}
func (j *injector) inject(rv reflect.Value) error {
rv = reflect.Indirect(rv)
typ := rv.Type()
if typ.Kind() != reflect.Struct {
return nil
}
num := typ.NumField()
for i := 0; i < num; i++ {
f := typ.Field(i)
ityp, l, ok, err := needInject(f)
if err != nil {
return err
}
if !ok {
// check embedded field.
if fv, ok := isEmbedded(rv, f); ok {
err := j.inject(fv)
if err != nil {
return err
}
}
continue
}
if !rv.Field(i).CanSet() {
return errorFunc(func() string {
return fmt.Sprintf("won't be set field: %s type=%s", f.Name, typ)
})
}
fv, err := j.materialize(ityp, l)
if err != nil {
// FIXME: include context of the error.
return err
}
rv.Field(i).Set(fv)
}
return nil
}
func (j *injector) materialize(ityp reflect.Type, l labelSet) (reflect.Value, error) {
if ityp.Kind() != reflect.Interface {
panic("type is not interface")
}
// find a type which implement ityp interface.
typ, labels, err := j.reg.find(ityp, l)
if err != nil {
return reflect.Value{}, err
}
// create or re-use an object for typ.
if rv, ok := j.cacheGet(typ, labels); ok {
return rv, nil
}
rv := reflect.New(typ)
j.cachePut(typ, labels, rv)
// inject dependencies.
err = j.inject(rv)
if err != nil {
return reflect.Value{}, err
}
// complete object
err = initiateComponent(rv)
if err != nil {
return reflect.Value{}, err
}
return rv, nil
}
func (j *injector) cacheGet(ityp reflect.Type, l labelSet) (reflect.Value, bool) {
entries, ok := j.omap[ityp]
if !ok {
return reflect.Value{}, false
}
for _, e := range entries {
if l.isSubset(e.l) {
return e.rv, true
}
}
return reflect.Value{}, false
}
func (j *injector) cachePut(ityp reflect.Type, l labelSet, rv reflect.Value) {
j.omap[ityp] = append(j.omap[ityp], &objEntry{l: l, rv: rv})
}