forked from sebastianMurdoch/di-injector
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdi_container.go
147 lines (133 loc) · 3.83 KB
/
di_container.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
package di_injector
import (
"errors"
"fmt"
"log"
"reflect"
)
type DiContainer interface {
AddToDependencies(dependencies ...interface{}) error
InjectWithDependencies(object interface{}) error
}
type diContainer struct {
dependencies []interface{}
}
/* NewDiContainer returns an empty container for your dependencies */
func NewDiContainer() DiContainer {
return &diContainer{
dependencies: []interface{}{},
}
}
/* AddToDependencies lets you add dependencies to your container. To match a dependency to a field on the struct, the
dependency must ether implement the interface specified on the field or be the exact type as the field element */
func (dc *diContainer) AddToDependencies(dependencies ...interface{}) error {
for _, dependency := range dependencies {
err := validateDependency(dependency)
if err != nil {
return errors.New("Cannot add the dependency " + fmt.Sprint(dependency) + "because " + err.Error())
}
// Skip nil dependency
if reflect.TypeOf(dependency) == nil {
continue
}
dc.dependencies = append(dc.dependencies, dependency)
}
// Removes duplicates types keeping the last insert
types := map[reflect.Type]int{}
for i, d := range dc.dependencies {
if index_e, ok := types[reflect.TypeOf(d)]; ok{
// Erase element index_e
dc.dependencies[index_e] = dc.dependencies[i]
dc.dependencies[i] = dc.dependencies[len(dc.dependencies)-1]
dc.dependencies[len(dc.dependencies)-1] = nil
dc.dependencies = dc.dependencies[:len(dc.dependencies)-1]
continue
} else {
types[reflect.TypeOf(d)]=i
}
}
return nil
}
/* InjecWithDepedencies receives a pointer to the object you want to inject with dependencies */
func (dc *diContainer) InjectWithDependencies(object interface{}) error {
err := validateObject(object)
if err != nil {
return err
}
var result error
f := func() {
defer func() {
if err := recover(); err != nil {
result = errors.New("Fatal Error at Injection")
}
}()
err := injectObjectWithDependencies(object, dc.dependencies)
if err != nil {
result = err
}
}
f()
return result
}
/* Basic validation over the object that will receive the dependencies */
func validateObject(object interface{}) error {
/*
TODO: Validate if fields are exported to deliver a more specific error message
TODO: Validate if more than one dependency can be injected into one field
*/
return nil
}
/* Basic validation over the dependency */
func validateDependency(dependency interface{}) error {
/*
TODO: Add validations
*/
return nil
}
func injectObjectWithDependencies(object interface{}, dependencies []interface{}) error {
obj := reflect.ValueOf(object).Elem()
typ := reflect.TypeOf(object).Elem()
for i := 0; i < obj.NumField(); i++ {
f := obj.Field(i)
t := typ.Field(i)
if t.Type.Kind() == reflect.Interface && t.Type.Name() == "" {
return errors.New("Cannot inject into interface{}")
}
if t.Tag.Get("inject") == "auto" {
injectOk := false
for _, dependency := range dependencies {
if f.Kind() == reflect.Interface && reflect.TypeOf(dependency).Implements(f.Type()) ||
reflect.TypeOf(dependency) == f.Type() {
// Add recursive injection
if needsInjection(dependency){
err := injectObjectWithDependencies(dependency, dependencies)
if err != nil {
return errors.New("Coudnt inject inner dependency -- " + err.Error())
}
}
value := reflect.ValueOf(dependency)
f.Set(value)
injectOk = true
break
}
}
if !injectOk {
// Log warning
log.Println("Warning: No dependencies injected")
}
}
}
return nil
}
func needsInjection(dependency interface{}) bool {
if reflect.TypeOf(dependency).Kind() != reflect.Ptr {
return false
}
typ := reflect.TypeOf(dependency).Elem()
for i := 0; i < typ.NumField(); i++ {
if typ.Field(i).Tag.Get("inject") == "auto" {
return true
}
}
return false
}