-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlitedi.go
107 lines (85 loc) · 2.83 KB
/
litedi.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
//Package litedi provides simple dependency injection
package litedi
import (
"reflect"
)
//Lifetime can be Trasient or Singleton
type Lifetime int
const (
//Trasient Lifetime scope
Trasient Lifetime = 0
//Singleton Lifetime scope
Singleton Lifetime = 1
)
type regTraget struct {
regType reflect.Type
lifetime Lifetime
}
//ContainerBuilder is use to build the container
type ContainerBuilder struct {
reg map[reflect.Type]regTraget
}
//Container is use to resolve the concrete
type Container struct {
reg map[reflect.Type]regTraget
singletons map[reflect.Type]reflect.Value
}
//CreateContainerBuilder creates ContainerBuilder
func CreateContainerBuilder() *ContainerBuilder {
reg := make(map[reflect.Type]regTraget)
return &ContainerBuilder{reg}
}
//Build builds the container
func (cb *ContainerBuilder) Build() *Container {
singletons := make(map[reflect.Type]reflect.Value)
return &Container{cb.reg, singletons}
}
//Register - registers new concret to give inteface
func (cb *ContainerBuilder) Register(from, to interface{}, lifetimeArgs ...Lifetime) *ContainerBuilder {
lifetime := Trasient
if len(lifetimeArgs) == 1 {
lifetime = lifetimeArgs[0]
}
if lifetime != Trasient && lifetime != Singleton {
panic("lifetime not supported")
}
cb.reg[reflect.TypeOf(from).Elem()] = regTraget{reflect.TypeOf(to), lifetime}
return cb
}
func (c *Container) createInstace(instaceType reflect.Type) (reflect.Value, reflect.Type) {
concreteType := c.reg[instaceType]
if concreteType.regType == nil {
return reflect.Value{}, nil
}
concreteInstace, ok := c.singletons[instaceType]
if !ok {
concreteInstace = reflect.New(concreteType.regType).Elem()
c.singletons[instaceType] = concreteInstace
}
return concreteInstace, concreteType.regType
}
func (c *Container) populateFields(concretType reflect.Type, val reflect.Value) {
for i := 0; i < concretType.NumField(); i++ {
fieldInfo := concretType.Field(i)
if fieldInfo.Type.Kind() == reflect.Ptr {
concreteInstace, concreteType := c.createInstace(fieldInfo.Type.Elem())
//if there is concrete that is register to this interface
if concreteType != nil {
//get the field value
fieldValue := val.Addr().Elem().FieldByName(fieldInfo.Name)
c.populateFields(concreteType, concreteInstace)
interfaceInstace := reflect.New(fieldInfo.Type.Elem())
interfaceInstace.Elem().Set(concreteInstace)
fieldValue.Set(interfaceInstace)
}
}
}
}
//Resolve resolves give interface to the concrete type
func (c *Container) Resolve(entity interface{}) {
ti := reflect.TypeOf(entity).Elem()
concreteVal, concretType := c.createInstace(ti)
c.populateFields(concretType, concreteVal)
valueOfInterface := reflect.ValueOf(entity)
valueOfInterface.Elem().Set(concreteVal)
}