-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathautowire.go
209 lines (164 loc) · 6.44 KB
/
autowire.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package nibbler
import (
"errors"
"reflect"
"sort"
"unsafe"
)
type dependency struct {
parents []*dependency
extension *Extension
typeName string // mostly here for debugging, at the moment
}
var interfaceWiringEnabled = true
var otherFieldWiringEnabled = true
// get the type of Extension, as it will be checked against often
var extensionInterfaceType = reflect.TypeOf(new(Extension)).Elem()
// TODO: this will blow up if there's a cycle
func AutoWireExtensions(extensions *[]Extension, logger *Logger) ([]Extension, error) {
// make a map to store dependency records by name
treeMap := make(map[reflect.Type]*dependency)
// dereference extensions for ease of use
extensionValues := *extensions
// build a map of type name -> node
for _, e := range extensionValues {
thisExt := e
typeVal := reflect.TypeOf(e)
typeName := typeVal.String()
treeMap[typeVal] = &dependency{
extension: &thisExt,
typeName: typeName,
}
}
// go through the list of extensions again to assign fields and attach dependents to extensions
for extIndex, ext := range extensionValues {
extensionType := reflect.TypeOf(ext)
extensionValue := reflect.ValueOf(ext).Elem()
fieldCount := extensionValue.NumField()
thisExtensionDependency := treeMap[reflect.TypeOf(ext)]
// loop through the fields for this extension
for i := 0; i < fieldCount; i++ {
fieldTypeAssignable := extensionType.Elem().Field(i)
fieldValue := extensionValue.Field(i)
// if we've encountered a pointer field or an interface field that isn't an extension
if fieldValue.Kind() == reflect.Ptr {
// if we've encountered a field that implements Extension
if fieldValue.Type().AssignableTo(extensionInterfaceType) {
(*logger).Debug("autowiring " + fieldTypeAssignable.Name + " " + fieldTypeAssignable.Type.String() +
" into " + extensionType.Elem().Name() + " " + extensionValue.Type().String())
// TODO: this section looks repeated below
// get the tree node by name
mapExt := treeMap[fieldTypeAssignable.Type]
// if it's not found, something very bad happened
if mapExt == nil {
return nil, errors.New("could not autowire " + fieldValue.Type().Name() + " into " + extensionType.Name())
}
// if the value isn't set, populate it
if fieldValue.IsNil() {
unsafeExt := unsafe.Pointer(mapExt.extension)
ptr := reflect.NewAt(fieldValue.Type(), unsafeExt)
fieldValue.Set(ptr.Elem())
}
thisExtensionDependency.parents = append(thisExtensionDependency.parents, mapExt)
} else if otherFieldWiringEnabled {
err := wireFieldToAnotherExtensionType(extensionValues, extIndex, treeMap, thisExtensionDependency, i, logger)
if err != nil {
return nil, err
}
}
} else if interfaceWiringEnabled && fieldValue.Kind() == reflect.Interface && fieldValue.Type() != extensionInterfaceType {
err := wireFieldToAnotherExtensionType(extensionValues, extIndex, treeMap, thisExtensionDependency, i, logger)
if err != nil {
return nil, err
}
}
}
}
return orderExtensions(treeMap), nil
}
func wireFieldToAnotherExtensionType(
extensions []Extension,
extIndex int,
treeMap map[reflect.Type]*dependency,
thisExtensionDependency *dependency,
fieldIndex int,
logger *Logger,
) error {
ext := extensions[extIndex]
extensionType := reflect.TypeOf(ext)
extensionValue := reflect.ValueOf(ext).Elem()
fieldTypeAssignable := extensionType.Elem().Field(fieldIndex)
fieldValue := extensionValue.Field(fieldIndex)
// look through all extensions to see if one of them implements the interface in question
for compareIndex, compareExt := range extensions {
// if the value is unset and not the one we're comparing against
if compareIndex != extIndex && fieldValue.IsNil() {
// the extension is assignable to the field
compareExtensionType := reflect.TypeOf(compareExt)
compareExtensionTypeKind := compareExtensionType.Kind()
if compareExtensionTypeKind == reflect.Interface || compareExtensionTypeKind == reflect.Ptr {
// check to see if either the extension or the dereferenced extension implement the type of the field
assignable := compareExtensionType.AssignableTo(fieldValue.Type())
assignable = assignable || (fieldValue.Type().Kind() == reflect.Ptr && compareExtensionType.AssignableTo(fieldValue.Type().Elem()))
if assignable {
(*logger).Debug("autowiring instance of " + compareExtensionType.String() +
" as " + fieldTypeAssignable.Name + " " + fieldTypeAssignable.Type.String() +
" into " + extensionType.Elem().Name() + " " + extensionValue.Type().String())
// get the tree node by name
mapExt := treeMap[compareExtensionType]
// if it's not found, something very bad happened
if mapExt == nil {
return errors.New("could not autowire " + fieldValue.Type().Name() + " into " + extensionType.Name())
}
// if the value isn't set, populate it
if fieldValue.IsNil() {
unsafeExt := unsafe.Pointer(mapExt.extension)
ptr := reflect.NewAt(fieldValue.Type(), unsafeExt)
fieldValue.Set(ptr.Elem())
}
thisExtensionDependency.parents = append(thisExtensionDependency.parents, mapExt)
}
}
}
}
return nil
}
// reorder extensions based on dependencies
func orderExtensions(treeMap map[reflect.Type]*dependency) []Extension {
// convert treemap to a slice of dependencies
var dependencyList []*dependency
for _, v := range treeMap {
dependencyList = append(dependencyList, v)
}
// use sort to sort the new slice
sort.SliceStable(dependencyList, func(a, b int) bool {
extA := dependencyList[a]
extB := dependencyList[b]
return !isDescendant(extA, extB)
})
// convert the slice of dependencies to a slice of extensions
var sortedExtensions []Extension
for _, v := range dependencyList {
sortedExtensions = append(sortedExtensions, *v.extension)
}
return sortedExtensions
}
func isDescendant(candidateChild *dependency, candidateAncestor *dependency) bool {
// the base case is that no parents are left
if candidateChild.parents == nil || len(candidateChild.parents) == 0 {
return false
}
// loop through parents
for _, parent := range candidateChild.parents {
// if one is the candidate, we've proven the child to be a descendant
if parent == candidateAncestor {
return true
}
// if there are parents to traverse, run this function against that and the candidate child
if isDescendant(parent, candidateAncestor) {
return true
}
}
// no ancestor was found
return false
}