-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinjection.go
212 lines (178 loc) · 5.33 KB
/
injection.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
210
211
212
package genjector
import (
"fmt"
)
// Key is a struct that contains information for Binding keys
// inside a Container.
//
// It is meant to be used only for internal purposes.
type Key struct {
Annotation string
Value interface{}
}
// Generate delivers a final Binding key for the Container.
func (k Key) Generate() interface{} {
if len(k.Annotation) > 0 {
return [2]interface{}{k.Annotation, k.Value}
}
return k.Value
}
// KeySource represents an interface that builds a Key for Binding.
type KeySource interface {
Key() Key
}
// KeyOption represents an interface that overrides creation of Key
// and Container.
type KeyOption interface {
Key(key Key) Key
Container(container Container) Container
}
// Binding represents an interface that delivers new instance for
// particular interface (or a struct).
type Binding interface {
Instance(initialize bool) (interface{}, error)
}
// BindingSource represents an interface that delivers starting Key and
// Binding instances, that later could be overridden by KeyOption or
// BindingOption.
type BindingSource[T any] interface {
Key() Key
Binding() (Binding, error)
}
// FollowingBindingSource represents an interface for a Binding instance
// that requires to get previous instance of Binding inside Container,
// before the new one should be stored on that place.
type FollowingBindingSource[T any] interface {
SetPrevious(binding Binding)
}
// BindingOption represents an interface that overrides creation of Key,
// Binding and Container.
type BindingOption interface {
Key(key Key) Key
Container(container Container) Container
Binding(binding Binding) (Binding, error)
}
// Container is a child type used for storing all Binding instances.
type Container map[interface{}]Binding
// global is a concrete global Container
var global = NewContainer()
// NewContainer delivers a new instance of Container.
func NewContainer() Container {
return map[interface{}]Binding{}
}
// Bind executes complete logic for binding particular value (or pointer) to
// desired interface (or struct). By default, it stores all Binding instances
// into default inner Container.
//
// It requires only BindingSource to be passed as an argument and all other
// instances of BindingOption are optional.
//
// At this point, if Binding for particular interface (or struct) is not defined,
// it uses its own fallback Binding. Still, it works fully only for values, not pointers,
// as for pointers it returns nil value. That means that pointer Binding
// should be always defined.
func Bind[T any](source BindingSource[T], options ...BindingOption) error {
key := source.Key()
internal := global
for _, option := range options {
key = option.Key(key)
internal = option.Container(internal)
}
generated := key.Generate()
if child, ok := source.(FollowingBindingSource[T]); ok {
parent, ok := internal[generated]
if ok {
child.SetPrevious(parent)
}
}
binding, err := source.Binding()
if err != nil {
return err
}
for _, option := range options {
binding, err = option.Binding(binding)
if err != nil {
return err
}
}
internal[generated] = binding
return nil
}
// MustBind wraps Bind method, by making sure error is not returned as an argument.
//
// Still, in case of error, it panics.
func MustBind[T any](source BindingSource[T], options ...BindingOption) {
err := Bind(source, options...)
if err != nil {
panic(err)
}
}
// NewInstance executes complete logic for initializing value (or pointer) for
// desired interface (or struct). By default, it uses Binding instance from default
// inner Container. If such Binding can not be found, it tries to make its own
// fallback Binding.
//
// All instances of BindingOption are optional.
//
// At this point, if Binding for particular interface (or struct) is not defined,
// it uses its own fallback Binding. Still, it works fully only for values, not pointers,
// as for pointers it returns nil value. That means that pointer Binding
// should be always defined.
func NewInstance[T any](options ...KeyOption) (T, error) {
var empty T
source := &baseKeySource[T]{}
key := source.Key()
internal := global
for _, option := range options {
key = option.Key(key)
internal = option.Container(internal)
}
generated := key.Generate()
binding, ok := internal[generated]
if !ok {
var err error
binding, err = getFallbackBinding[T]()
if err != nil {
return empty, err
}
}
instance, err := binding.Instance(true)
if err != nil {
return empty, err
}
result, ok := instance.(T)
if !ok {
return empty, fmt.Errorf(`invalid binding is defined for key "%v"`, generated)
}
return result, nil
}
// MustNewInstance wraps NewInstance method, by making sure error is not returned as an argument.
//
// Still, in case of error, it panics.
func MustNewInstance[T any](options ...KeyOption) T {
instance, err := NewInstance[T](options...)
if err != nil {
panic(err)
}
return instance
}
// Clean creates a new instance of inner Container.
func Clean() {
global = NewContainer()
}
// getFallbackBinding creates a new instance of fallback Binding.
func getFallbackBinding[T any]() (Binding, error) {
var binding Binding
var err error
source := AsValue[T, T]()
binding, err = source.Binding()
if err == nil {
instance, err := binding.Instance(false)
if err == nil {
if _, ok := instance.(T); ok {
return binding, nil
}
}
}
return nil, err
}