@@ -151,60 +151,110 @@ type callable struct {
151
151
// RegisterMethods iterates the methods provided by the lib API, and makes them visible to dispatch
152
152
func (inst * Instance ) RegisterMethods () {
153
153
reg := make (map [string ]callable )
154
- // TODO(dustmop): Change registerOne to take both the MethodSet and the Impl, validate
155
- // that their signatures agree.
156
- inst .registerOne ("fsi" , & FSIImpl {}, reg )
157
- inst .registerOne ("access" , accessImpl {}, reg )
154
+ inst .registerOne ("fsi" , inst .Filesys (), fsiImpl {}, reg )
155
+ inst .registerOne ("access" , inst .Access (), accessImpl {}, reg )
158
156
inst .regMethods = & regMethodSet {reg : reg }
159
157
}
160
158
161
- func (inst * Instance ) registerOne (ourName string , impl interface {}, reg map [string ]callable ) {
159
+ func (inst * Instance ) registerOne (ourName string , methods MethodSet , impl interface {}, reg map [string ]callable ) {
162
160
implType := reflect .TypeOf (impl )
161
+ msetType := reflect .TypeOf (methods )
162
+ methodMap := inst .buildMethodMap (methods )
163
163
// Iterate methods on the implementation, register those that have the right signature
164
164
num := implType .NumMethod ()
165
165
for k := 0 ; k < num ; k ++ {
166
- m := implType .Method (k )
167
- lowerName := strings .ToLower (m .Name )
166
+ i := implType .Method (k )
167
+ lowerName := strings .ToLower (i .Name )
168
168
funcName := fmt .Sprintf ("%s.%s" , ourName , lowerName )
169
169
170
- // Validate the parameters to the method
170
+ // Validate the parameters to the implementation
171
171
// should have 3 input parameters: (receiver, scope, input struct)
172
172
// should have 2 output parametres: (output value, error)
173
173
// TODO(dustmop): allow variadic returns: error only, cursor for pagination
174
- f := m .Type
174
+ f := i .Type
175
175
if f .NumIn () != 3 {
176
- log . Fatalf ("%s: bad number of inputs: %d" , funcName , f .NumIn ())
176
+ panic ( fmt . Sprintf ("%s: bad number of inputs: %d" , funcName , f .NumIn () ))
177
177
}
178
178
if f .NumOut () != 2 {
179
- log . Fatalf ("%s: bad number of outputs: %d" , funcName , f .NumOut ())
179
+ panic ( fmt . Sprintf ("%s: bad number of outputs: %d" , funcName , f .NumOut () ))
180
180
}
181
181
// First input must be the receiver
182
182
inType := f .In (0 )
183
183
if inType != implType {
184
- log . Fatalf ("%s: first input param should be impl, got %v" , funcName , inType )
184
+ panic ( fmt . Sprintf ("%s: first input param should be impl, got %v" , funcName , inType ) )
185
185
}
186
186
// Second input must be a scope
187
187
inType = f .In (1 )
188
188
if inType .Name () != "scope" {
189
- log . Fatalf ("%s: second input param should be scope, got %v" , funcName , inType )
189
+ panic ( fmt . Sprintf ("%s: second input param should be scope, got %v" , funcName , inType ) )
190
190
}
191
191
// Third input is a pointer to the input struct
192
192
inType = f .In (2 )
193
193
if inType .Kind () != reflect .Ptr {
194
- log . Fatalf ("%s: third input param must be a struct pointer, got %v" , funcName , inType )
194
+ panic ( fmt . Sprintf ("%s: third input param must be a struct pointer, got %v" , funcName , inType ) )
195
195
}
196
196
inType = inType .Elem ()
197
197
if inType .Kind () != reflect .Struct {
198
- log . Fatalf ("%s: third input param must be a struct pointer, got %v" , funcName , inType )
198
+ panic ( fmt . Sprintf ("%s: third input param must be a struct pointer, got %v" , funcName , inType ) )
199
199
}
200
200
// First output is anything
201
201
outType := f .Out (0 )
202
202
// Second output must be an error
203
203
outErrType := f .Out (1 )
204
204
if outErrType .Name () != "error" {
205
- log .Fatalf ("%s: second output param should be error, got %v" , funcName , outErrType )
205
+ panic (fmt .Sprintf ("%s: second output param should be error, got %v" , funcName , outErrType ))
206
+ }
207
+
208
+ // Validate the parameters to the method that matches the implementation
209
+ // should have 3 input parameters: (receiver, context.Context, input struct [same as impl])
210
+ // should have 2 output parametres: (output value [same as impl], error)
211
+ m , ok := methodMap [i .Name ]
212
+ if ! ok {
213
+ panic (fmt .Sprintf ("method %s not found on MethodSet" , i .Name ))
214
+ }
215
+ f = m .Type
216
+ if f .NumIn () != 3 {
217
+ panic (fmt .Sprintf ("%s: bad number of inputs: %d" , funcName , f .NumIn ()))
218
+ }
219
+ msetNumMethods := f .NumOut ()
220
+ if msetNumMethods < 1 && msetNumMethods > 2 {
221
+ panic (fmt .Sprintf ("%s: bad number of outputs: %d" , funcName , f .NumOut ()))
222
+ }
223
+ // First input must be the receiver
224
+ mType := f .In (0 )
225
+ if mType .Name () != msetType .Name () {
226
+ panic (fmt .Sprintf ("%s: first input param should be impl, got %v" , funcName , mType ))
227
+ }
228
+ // Second input must be a context
229
+ mType = f .In (1 )
230
+ if mType .Name () != "Context" {
231
+ panic (fmt .Sprintf ("%s: second input param should be context.Context, got %v" , funcName , mType ))
232
+ }
233
+ // Third input is a pointer to the input struct
234
+ mType = f .In (2 )
235
+ if mType .Kind () != reflect .Ptr {
236
+ panic (fmt .Sprintf ("%s: third input param must be a pointer, got %v" , funcName , mType ))
237
+ }
238
+ mType = mType .Elem ()
239
+ if mType != inType {
240
+ panic (fmt .Sprintf ("%s: third input param must match impl, expect %v, got %v" , funcName , inType , mType ))
241
+ }
242
+ // First output, if there's more than 1, matches the impl output
243
+ if msetNumMethods == 2 {
244
+ mType = f .Out (0 )
245
+ if mType != outType {
246
+ panic (fmt .Sprintf ("%s: first output param must match impl, expect %v, got %v" , funcName , outType , mType ))
247
+ }
248
+ }
249
+ // Last output must be an error
250
+ mType = f .Out (msetNumMethods - 1 )
251
+ if mType .Name () != "error" {
252
+ panic (fmt .Sprintf ("%s: last output param should be error, got %v" , funcName , mType ))
206
253
}
207
254
255
+ // Remove this method from the methodSetMap now that it has been processed
256
+ delete (methodMap , i .Name )
257
+
208
258
// Save the method to the registration table
209
259
reg [funcName ] = callable {
210
260
Impl : impl ,
@@ -214,6 +264,23 @@ func (inst *Instance) registerOne(ourName string, impl interface{}, reg map[stri
214
264
}
215
265
log .Debugf ("%d: registered %s(*%s) %v" , k , funcName , inType , outType )
216
266
}
267
+
268
+ for k := range methodMap {
269
+ if k != "Name" {
270
+ panic (fmt .Sprintf ("%s: did not find implementation for method %s" , msetType , k ))
271
+ }
272
+ }
273
+ }
274
+
275
+ func (inst * Instance ) buildMethodMap (impl interface {}) map [string ]reflect.Method {
276
+ result := make (map [string ]reflect.Method )
277
+ implType := reflect .TypeOf (impl )
278
+ num := implType .NumMethod ()
279
+ for k := 0 ; k < num ; k ++ {
280
+ m := implType .Method (k )
281
+ result [m .Name ] = m
282
+ }
283
+ return result
217
284
}
218
285
219
286
// MethodSet represents a set of methods to be registered
0 commit comments