-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathfiller_api.go
240 lines (215 loc) · 8.62 KB
/
filler_api.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
package nject
import (
"reflect"
)
// PostActionFuncArg are functional arguments to PostActionByTag,
// PostActionByName, and PostActionByType.
type PostActionFuncArg func(*postActionOption)
type postActionOption struct {
function interface{}
fill bool
fillSet bool
matchToInterface bool
}
// generatedFromInjectionChain is a special kind of provider that inspects the rest of the
// injection chain to replace itself with a regular provider. The ReplaceSelf method will
// be called only once.
type generatedFromInjectionChain interface {
String() string
ReplaceSelf(chainBefore Collection, chainAfter Collection) (selfReplacement Provider, err error)
}
var _ generatedFromInjectionChain = gfic{}
type gfic struct {
name string
f func(chainBefore Collection, chainAfter Collection) (selfReplacement Provider, err error)
}
func (g gfic) String() string { return g.name }
func (g gfic) ReplaceSelf(before Collection, after Collection) (selfReplacement Provider, err error) {
return g.f(before, after)
}
// GenerateFromInjectionChain creates a very special provider from a function
// that examines the injection chain and then returns a replacement provider.
// The first parameter for the function is a Collection representing all the providers
// that are earlier in the chain from from the new special provider; the second
// parameter is a Collection representing all the providers that are later
// in the chain from the new special provider.
func GenerateFromInjectionChain(
name string,
f func(chainBefore Collection, chainAfter Collection) (selfReplacement Provider, err error),
) generatedFromInjectionChain {
return gfic{
name: name,
f: f,
}
}
type ignore struct{}
// FillerFuncArg is a functional argument for MakeStructBuilder
type FillerFuncArg func(*fillerOptions)
// WithTag sets the struct tag to use for per-struct-field
// directives in MakeStructBuilder. The default tag is "nject"
//
// EXPERIMENTAL: this is currently considered experimental
// and could be removed in a future release. If you are using
// this, please open a pull request to remove this comment.
func WithTag(tag string) FillerFuncArg {
return func(o *fillerOptions) {
o.tag = tag
}
}
// TODO: WithOptionalMethod
// WithMethodCall looks up a method on the struct being
// filled or built and adds a method invocation to the
// dependency chain. The method can be any kind of function
// provider (the last function, a wrapper, etc). If there
// is no method of that name, then MakeStructBuilder will
// return an error.
//
// EXPERIMENTAL: this is currently considered experimental
// and could be removed in a future release. If you are using
// this, please open a pull request to remove this comment.
func WithMethodCall(methodName string) FillerFuncArg {
// Implementation note:
// We'll use a Reflective to invoke the method using the
// the version of the method that takes an explicit
// receiver.
return func(o *fillerOptions) {
o.postMethodName = append(o.postMethodName, methodName)
}
}
// PostActionByTag establishes a tag value that indicates that
// after the struct is built or filled, a function should be called
// passing a pointer to the tagged field to the function. The
// function must take as an input parameter a pointer to the type
// of the field or it must take as an input paraemter an interface
// type that the field implements. interface{} is allowed.
// This function will be added to the injection chain after the
// function that builds or fills the struct. If there is also a
// WithMethodCall, this function will run before that.
//
// EXPERIMENTAL: this is currently considered experimental
// and could be removed in a future release. If you are using
// this, please open a pull request to remove this comment.
func PostActionByTag(tagValue string, function interface{}, opts ...PostActionFuncArg) FillerFuncArg {
// Implementation note:
// There could be more than one field using the same type so
// the normal chain parameter passing methods won't work.
// To get around that, we'll create a Reflective that is a
// thin wrapper around a function. We'll select the closest
// match between the function input and the field and replace
// that type in the list of inputs with the struct being filled.
// The actual Call() we will grab the field from the struct
// using it's index and use that to call the function.
options := makePostActionOption(function, opts)
return func(o *fillerOptions) {
o.postActionByTag[tagValue] = options
}
}
// PostActionByType arranges to call a function for every field in
// struct that is being filled where the type of the field in
// the struct exactly matches the first input parameter of the
// provided function. PostActionByType calls are made after
// PostActionByTag calls, but before WithMethodCall invocations.
//
// If there is no match to the type of the function, then the function
// is not invoked.
//
// EXPERIMENTAL: this is currently considered experimental
// and could be removed in a future release. If you are using
// this, please open a pull request to remove this comment.
func PostActionByType(function interface{}, opts ...PostActionFuncArg) FillerFuncArg {
options := makePostActionOption(function, opts)
return func(o *fillerOptions) {
o.postActionByType = append(o.postActionByType, options)
}
}
// PostActionByName arranges to call a function passing in the field that
// has a matching name. PostActionByName happens before PostActionByType
// and after PostActionByTag calls.
//
// EXPERIMENTAL: this is currently considered experimental
// and could be removed in a future release. If you are using
// this, please open a pull request to remove this comment.
func PostActionByName(name string, function interface{}, opts ...PostActionFuncArg) FillerFuncArg {
options := makePostActionOption(function, opts)
return func(o *fillerOptions) {
o.postActionByName[name] = options
}
}
// FillExisting changes the behavior of MakeStructBuilder so that it
// fills fields in a struct that it receives from upstream in the
// provider chain rather than starting fresh with a new structure.
//
// EXPERIMENTAL: this is currently considered experimental
// and could be removed in a future release. If you are using
// this, please open a pull request to remove this comment.
func FillExisting(o *fillerOptions) {
o.create = false
}
func makePostActionOption(function interface{}, userOpts []PostActionFuncArg, typeOpts ...PostActionFuncArg) postActionOption {
options := postActionOption{
function: function,
}
for _, opt := range typeOpts {
opt(&options)
}
for _, opt := range userOpts {
opt(&options)
}
return options
}
// TODO: add ExampleWithFIll
// WithFill overrides the default behaviors of PostActionByType, PostActionByName,
// and PostActionByTag with respect to the field being automatically filled.
// By default, if there is a post-action that that receives a pointer to the
// field, then the field will not be filled from the injection chain.
//
// EXPERIMENTAL: this is currently considered experimental
// and could be removed in a future release. If you are using
// this, please open a pull request to remove this comment.
func WithFill(b bool) PostActionFuncArg {
return func(o *postActionOption) {
o.fill = b
o.fillSet = true
}
}
// MatchToOpenInterface requires that the post action function have exactly one
// open interface type (interface{}) in its arguments list. A pointer to the
// field will be passed to the interface parameter.
//
// EXPERIMENTAL: this is currently considered experimental
// and could be removed in a future release. If you are using
// this, please open a pull request to remove this comment.
func MatchToOpenInterface(b bool) PostActionFuncArg {
return func(o *postActionOption) {
o.matchToInterface = true
}
}
// MustMakeStructBuilder wraps a panic around failed
// MakeStructBuilder calls
//
// EXPERIMENTAL: this is currently considered experimental
// and could be removed in a future release. If you are using
// this, please open a pull request to remove this comment.
func MustMakeStructBuilder(model interface{}, opts ...FillerFuncArg) Provider {
p, err := MakeStructBuilder(model, opts...)
if err != nil {
panic(err.Error())
}
return p
}
// TODO: add ExampleProvideRequireGap
// ProvideRequireGap identifies types that are required but are not provided.
func ProvideRequireGap(provided []reflect.Type, required []reflect.Type) []reflect.Type {
have := make(map[typeCode]struct{})
for _, t := range provided {
have[getTypeCode(t)] = struct{}{}
}
var missing []reflect.Type
for _, t := range required {
if _, ok := have[getTypeCode(t)]; ok {
continue
}
missing = append(missing, t)
}
return missing
}