@@ -151,60 +151,110 @@ type callable struct {
151151// RegisterMethods iterates the methods provided by the lib API, and makes them visible to dispatch
152152func (inst * Instance ) RegisterMethods () {
153153 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 )
158156 inst .regMethods = & regMethodSet {reg : reg }
159157}
160158
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 ) {
162160 implType := reflect .TypeOf (impl )
161+ msetType := reflect .TypeOf (methods )
162+ methodMap := inst .buildMethodMap (methods )
163163 // Iterate methods on the implementation, register those that have the right signature
164164 num := implType .NumMethod ()
165165 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 )
168168 funcName := fmt .Sprintf ("%s.%s" , ourName , lowerName )
169169
170- // Validate the parameters to the method
170+ // Validate the parameters to the implementation
171171 // should have 3 input parameters: (receiver, scope, input struct)
172172 // should have 2 output parametres: (output value, error)
173173 // TODO(dustmop): allow variadic returns: error only, cursor for pagination
174- f := m .Type
174+ f := i .Type
175175 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 () ))
177177 }
178178 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 () ))
180180 }
181181 // First input must be the receiver
182182 inType := f .In (0 )
183183 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 ) )
185185 }
186186 // Second input must be a scope
187187 inType = f .In (1 )
188188 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 ) )
190190 }
191191 // Third input is a pointer to the input struct
192192 inType = f .In (2 )
193193 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 ) )
195195 }
196196 inType = inType .Elem ()
197197 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 ) )
199199 }
200200 // First output is anything
201201 outType := f .Out (0 )
202202 // Second output must be an error
203203 outErrType := f .Out (1 )
204204 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 ))
206253 }
207254
255+ // Remove this method from the methodSetMap now that it has been processed
256+ delete (methodMap , i .Name )
257+
208258 // Save the method to the registration table
209259 reg [funcName ] = callable {
210260 Impl : impl ,
@@ -214,6 +264,23 @@ func (inst *Instance) registerOne(ourName string, impl interface{}, reg map[stri
214264 }
215265 log .Debugf ("%d: registered %s(*%s) %v" , k , funcName , inType , outType )
216266 }
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
217284}
218285
219286// MethodSet represents a set of methods to be registered
0 commit comments