-
Notifications
You must be signed in to change notification settings - Fork 208
/
container.go
301 lines (239 loc) · 8.93 KB
/
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
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
// Copyright (c) 2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package dig
import (
"fmt"
"math/rand"
"reflect"
"go.uber.org/dig/internal/digclock"
"go.uber.org/dig/internal/dot"
)
const (
_optionalTag = "optional"
_nameTag = "name"
_ignoreUnexportedTag = "ignore-unexported"
)
// Unique identification of an object in the graph.
type key struct {
t reflect.Type
// Only one of name or group will be set.
name string
group string
}
func (k key) String() string {
if k.name != "" {
return fmt.Sprintf("%v[name=%q]", k.t, k.name)
}
if k.group != "" {
return fmt.Sprintf("%v[group=%q]", k.t, k.group)
}
return k.t.String()
}
// Option configures a Container.
type Option interface {
applyOption(*Container)
}
// Container is a directed acyclic graph of types and their dependencies.
// A Container is the root Scope that represents the top-level scoped
// directed acyclic graph of the dependencies.
type Container struct {
// this is the "root" Scope that represents the
// root of the scope tree.
scope *Scope
}
// containerWriter provides write access to the Container's underlying data
// store.
type containerWriter interface {
// setValue sets the value with the given name and type in the container.
// If a value with the same name and type already exists, it will be
// overwritten.
setValue(name string, t reflect.Type, v reflect.Value)
// setDecoratedValue sets a decorated value with the given name and type
// in the container. If a decorated value with the same name and type already
// exists, it will be overwritten.
setDecoratedValue(name string, t reflect.Type, v reflect.Value)
// submitGroupedValue submits a value to the value group with the provided
// name.
submitGroupedValue(name string, t reflect.Type, v reflect.Value)
// submitDecoratedGroupedValue submits a decorated value to the value group
// with the provided name.
submitDecoratedGroupedValue(name string, t reflect.Type, v reflect.Value)
}
// containerStore provides access to the Container's underlying data store.
type containerStore interface {
containerWriter
// Adds a new graph node to the Container
newGraphNode(w interface{}, orders map[*Scope]int)
// Returns a slice containing all known types.
knownTypes() []reflect.Type
// Retrieves the value with the provided name and type, if any.
getValue(name string, t reflect.Type) (v reflect.Value, ok bool)
// Retrieves a decorated value with the provided name and type, if any.
getDecoratedValue(name string, t reflect.Type) (v reflect.Value, ok bool)
// Retrieves all values for the provided group and type.
//
// The order in which the values are returned is undefined.
getValueGroup(name string, t reflect.Type) []reflect.Value
// Retrieves all decorated values for the provided group and type, if any.
getDecoratedValueGroup(name string, t reflect.Type) (reflect.Value, bool)
// Returns the providers that can produce a value with the given name and
// type.
getValueProviders(name string, t reflect.Type) []provider
// Returns the providers that can produce values for the given group and
// type.
getGroupProviders(name string, t reflect.Type) []provider
// Returns the providers that can produce a value with the given name and
// type across all the Scopes that are in effect of this containerStore.
getAllValueProviders(name string, t reflect.Type) []provider
// Returns the decorator that can decorate values for the given name and
// type.
getValueDecorator(name string, t reflect.Type) (decorator, bool)
// Reutrns the decorator that can decorate values for the given group and
// type.
getGroupDecorator(name string, t reflect.Type) (decorator, bool)
// Reports a list of stores (starting at this store) up to the root
// store.
storesToRoot() []containerStore
createGraph() *dot.Graph
// Returns invokerFn function to use when calling arguments.
invoker() invokerFn
// Returns a clock to use
clock() digclock.Clock
}
// New constructs a Container.
func New(opts ...Option) *Container {
s := newScope()
c := &Container{scope: s}
for _, opt := range opts {
opt.applyOption(c)
}
return c
}
// DeferAcyclicVerification is an Option to override the default behavior
// of container.Provide, deferring the dependency graph validation to no longer
// run after each call to container.Provide. The container will instead verify
// the graph on first `Invoke`.
//
// Applications adding providers to a container in a tight loop may experience
// performance improvements by initializing the container with this option.
func DeferAcyclicVerification() Option {
return deferAcyclicVerificationOption{}
}
type deferAcyclicVerificationOption struct{}
func (deferAcyclicVerificationOption) String() string {
return "DeferAcyclicVerification()"
}
func (deferAcyclicVerificationOption) applyOption(c *Container) {
c.scope.deferAcyclicVerification = true
}
// RecoverFromPanics is an [Option] to recover from panics that occur while
// running functions given to the container. When set, recovered panics
// will be placed into a [PanicError], and returned at the invoke callsite.
// See [PanicError] for an example on how to handle panics with this option
// enabled, and distinguish them from errors.
func RecoverFromPanics() Option {
return recoverFromPanicsOption{}
}
type recoverFromPanicsOption struct{}
func (recoverFromPanicsOption) String() string {
return "RecoverFromPanics()"
}
func (recoverFromPanicsOption) applyOption(c *Container) {
c.scope.recoverFromPanics = true
}
// Changes the source of randomness for the container.
//
// This will help provide determinism during tests.
func setRand(r *rand.Rand) Option {
return setRandOption{r: r}
}
type setRandOption struct{ r *rand.Rand }
func (o setRandOption) String() string {
return fmt.Sprintf("setRand(%p)", o.r)
}
func (o setRandOption) applyOption(c *Container) {
c.scope.rand = o.r
}
// Changes the source of time for the container.
func setClock(c digclock.Clock) Option {
return setClockOption{c: c}
}
type setClockOption struct{ c digclock.Clock }
func (o setClockOption) String() string {
return fmt.Sprintf("setClock(%v)", o.c)
}
func (o setClockOption) applyOption(c *Container) {
c.scope.clockSrc = o.c
}
// DryRun is an Option which, when set to true, disables invocation of functions supplied to
// Provide and Invoke. Use this to build no-op containers.
func DryRun(dry bool) Option {
return dryRunOption(dry)
}
type dryRunOption bool
func (o dryRunOption) String() string {
return fmt.Sprintf("DryRun(%v)", bool(o))
}
func (o dryRunOption) applyOption(c *Container) {
if o {
c.scope.invokerFn = dryInvoker
} else {
c.scope.invokerFn = defaultInvoker
}
}
// invokerFn specifies how the container calls user-supplied functions.
type invokerFn func(fn reflect.Value, args []reflect.Value) (results []reflect.Value)
func defaultInvoker(fn reflect.Value, args []reflect.Value) []reflect.Value {
return fn.Call(args)
}
// Generates zero values for results without calling the supplied function.
func dryInvoker(fn reflect.Value, _ []reflect.Value) []reflect.Value {
ft := fn.Type()
results := make([]reflect.Value, ft.NumOut())
for i := 0; i < ft.NumOut(); i++ {
results[i] = reflect.Zero(fn.Type().Out(i))
}
return results
}
// String representation of the entire Container
func (c *Container) String() string {
return c.scope.String()
}
// Scope creates a child scope of the Container with the given name.
func (c *Container) Scope(name string, opts ...ScopeOption) *Scope {
return c.scope.Scope(name, opts...)
}
type byTypeName []reflect.Type
func (bs byTypeName) Len() int {
return len(bs)
}
func (bs byTypeName) Less(i int, j int) bool {
return fmt.Sprint(bs[i]) < fmt.Sprint(bs[j])
}
func (bs byTypeName) Swap(i int, j int) {
bs[i], bs[j] = bs[j], bs[i]
}
func shuffledCopy(rand *rand.Rand, items []reflect.Value) []reflect.Value {
newItems := make([]reflect.Value, len(items))
for i, j := range rand.Perm(len(items)) {
newItems[i] = items[j]
}
return newItems
}