-
Notifications
You must be signed in to change notification settings - Fork 0
/
definitions_verification.go
138 lines (99 loc) · 3.34 KB
/
definitions_verification.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
package simpledi
import (
"errors"
"fmt"
)
type definitionsVerificatorFunc func(d definitionsContainer) error
func verifyDefinitionsDependencies(d definitionsContainer) error {
errDef := "container verification error"
// Verify that all dependencies are exist
for cName, cDef := range d {
for _, dep := range cDef.dependencies {
if _, depFound := d[dep]; !depFound {
return errors.New(fmt.Sprintf("%s. component '%s' cannot be initialized, dependepcy '%s' has not found",
errDef,
cName,
dep,
))
}
}
}
return nil
}
func verifyCircularDependencyInjections(d definitionsContainer) error {
injectedByMap := make(map[string]map[string]bool, 0)
// Constructing a map which will contain map of component and deps which will inject this component
for _, def := range d {
if _, found := injectedByMap[def.fullName]; !found {
injectedByMap[def.fullName] = make(map[string]bool, 0)
}
for _, def2 := range d {
for _, dep := range def2.dependencies {
if dep == def.fullName {
injectedByMap[def.fullName][def2.fullName] = true
}
}
}
}
// Circular injections search loop start
for definitionName, injectedBy := range injectedByMap {
// Buffer for processing deps
injectedByBuf := make([]string, 0)
// Add first iteration to buffer
for reverseDepName := range injectedBy {
injectedByBuf = append(injectedByBuf, reverseDepName)
}
// Iterate when elements are exist in buffer
for len(injectedByBuf) > 0 {
newBuf := make([]string, 0)
for _, injectedByInner := range injectedByBuf {
// If some dep (or dep of dep) of initial component contain this initial component as dependency
if injectedByInner == definitionName {
// Path for print where actual loop is
path := append(make([]string, 0), injectedByInner)
// Running recursive search of loop point, like DFS
fullPath, found := findLoopPath(0, injectedByInner, path, injectedByMap)
if !found {
return errors.New(fmt.Sprintf("circular dependency detected in component '%s'", definitionName))
}
// Pretty format found path
formattedFullPath := ""
for i := len(fullPath) - 1; i >= 0; i-- {
separator := " -> "
if i == 0 {
separator = ""
}
formattedFullPath += fullPath[i] + separator
}
return errors.New(fmt.Sprintf("circular dependency detected: %s", formattedFullPath))
}
// Fill new buf by current component deps
for outerInjectedByDepName := range injectedByMap[injectedByInner] {
newBuf = append(newBuf, outerInjectedByDepName)
}
}
// Updating a buf if it has not found on current iteration
injectedByBuf = newBuf
}
}
return nil
}
func findLoopPath(currentIteration int, loopDetectedAt string, path []string, injectedBy map[string]map[string]bool) ([]string, bool) {
errDef := "injection loop detector error"
if currentIteration > 25 {
panic(fmt.Sprintf("%s: max injection loop find hops (%d hops) exceeded", errDef, 25))
}
for k := range injectedBy[path[len(path)-1]] {
if k == loopDetectedAt {
return append(path, loopDetectedAt), true
}
newPath := make([]string, 0)
newPath = append(newPath, path...)
newPath = append(newPath, k)
foundPath, found := findLoopPath(currentIteration+1, loopDetectedAt, newPath, injectedBy)
if found {
return foundPath, true
}
}
return nil, false
}