-
Notifications
You must be signed in to change notification settings - Fork 0
/
descriptor.go
178 lines (154 loc) · 5.09 KB
/
descriptor.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
package di
import (
"fmt"
"reflect"
"github.com/fluffy-bunny/fluffy-dozm-di/reflectx"
)
type Lifetime byte
const (
Lifetime_Singleton Lifetime = iota
Lifetime_Scoped
Lifetime_Transient
)
type Factory func(Container) any
type ConstructorInfo struct {
FuncType reflect.Type
FuncValue reflect.Value
// input parameter types
In []reflect.Type
// output parameter types
Out []reflect.Type
}
func (c *ConstructorInfo) Call(params []reflect.Value) []reflect.Value {
return c.FuncValue.Call(params)
}
func newConstructorInfo(ctor any) *ConstructorInfo {
ft := reflect.TypeOf(ctor)
return &ConstructorInfo{
FuncValue: reflect.ValueOf(ctor),
FuncType: ft,
In: reflectx.GetInParameters(ft),
Out: reflectx.GetOutParameters(ft),
}
}
// service descriptor
type Descriptor struct {
ServiceType reflect.Type
Lifetime Lifetime
Ctor *ConstructorInfo
Instance any
Factory func(Container) any
ImplementedInterfaceTypes []reflect.Type
LookupKeys []string
Metadata map[string]interface{}
}
func (d *Descriptor) String() string {
s := fmt.Sprintf("ServiceType: %v Lifetime: %v ", d.ServiceType, d.Lifetime)
if d.Ctor != nil {
s += fmt.Sprintf("Constructor: %v", d.Ctor.FuncType)
} else {
s += fmt.Sprintf("Instance: %v", d.Instance)
}
return s
}
// validateServiceType validates the service type.
// panics if implementedInterfaceTypes is passed then the serviceType MUST be a struct.
// panics if implementedInterfaceTypes must be interfaces and the serviceType must implement them.
func validateServiceType(serviceType reflect.Type, implementedInterfaceTypes ...reflect.Type) {
if len(implementedInterfaceTypes) > 0 {
kind := serviceType.Kind()
// if serviceType is a pointer, get the element type
if kind != reflect.Ptr {
panic(fmt.Errorf("if implementedInterfaceTypes is passed then the serviceType MUST be a struct ptr. i.e. *MyStruct"))
}
serviceTypeElem := serviceType.Elem()
if serviceTypeElem.Kind() != reflect.Struct {
panic(fmt.Errorf("if implementedInterfaceTypes is passed then the serviceType MUST be a struct ptr. i.e. *MyStruct"))
}
for _, t := range implementedInterfaceTypes {
kind := t.Kind()
// if t is a pointer, get the element type
if kind == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Interface {
panic(fmt.Errorf("implementedInterfaceTypes must be interfaces. i.e. reflect.TypeOf((*ITime)(nil))"))
}
if !serviceType.Implements(t) {
panic(fmt.Errorf("the serviceType must implement the interface '%v'", t))
}
}
}
}
func NewInstanceDescriptor(serviceType reflect.Type, instance any, implementedInterfaceTypes ...reflect.Type) *Descriptor {
if err := instanceAssignable(instance, serviceType); err != nil {
panic(err)
}
validateServiceType(serviceType, implementedInterfaceTypes...)
var implementedInterfaceTypesElem []reflect.Type
for _, t := range implementedInterfaceTypes {
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
implementedInterfaceTypesElem = append(implementedInterfaceTypesElem, t)
}
return &Descriptor{
ServiceType: serviceType,
Lifetime: Lifetime_Singleton,
Instance: instance,
ImplementedInterfaceTypes: implementedInterfaceTypesElem,
}
}
func NewConstructorDescriptor(serviceType reflect.Type, lifetime Lifetime, ctor any, implementedInterfaceTypes ...reflect.Type) *Descriptor {
ci := newConstructorInfo(ctor)
err := checkConstructor(ci, serviceType)
if err != nil {
panic(err)
}
validateServiceType(serviceType, implementedInterfaceTypes...)
var implementedInterfaceTypesElem []reflect.Type
for _, t := range implementedInterfaceTypes {
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
implementedInterfaceTypesElem = append(implementedInterfaceTypesElem, t)
}
return &Descriptor{
ServiceType: serviceType,
Lifetime: lifetime,
Ctor: ci,
ImplementedInterfaceTypes: implementedInterfaceTypesElem,
}
}
func checkConstructor(ctor *ConstructorInfo, serviceType reflect.Type) (err error) {
if ctor.FuncType.Kind() != reflect.Func {
return fmt.Errorf("the constructor of the service '%v' is not a function", serviceType)
}
out := ctor.Out
numOut := len(out)
if (numOut == 0 || numOut > 2) ||
!out[0].AssignableTo(serviceType) ||
(numOut == 2 && !reflectx.IsErrorType(out[1])) {
return fmt.Errorf("the constructor must returns a '%v' and an optional error", serviceType)
}
return
}
func instanceAssignable(instance any, to reflect.Type) (err error) {
if t := reflect.TypeOf(instance); !t.AssignableTo(to) {
err = fmt.Errorf("the instance of type '%v' can not assignable to type '%v'", t, to)
}
return
}
func NewFactoryDescriptor(serviceType reflect.Type, lifetime Lifetime, factory Factory) *Descriptor {
return &Descriptor{
ServiceType: serviceType,
Lifetime: lifetime,
Factory: factory,
}
}
func hashTypeAndString(t reflect.Type, s string) string {
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
return fmt.Sprintf("%s-%s", t.Name(), s)
}