2020-10-09 16:15:36 +03:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"unicode"
|
|
|
|
"unicode/utf8"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2024-04-23 07:28:58 +03:00
|
|
|
subSig = "func(context.Context, interface{}) error"
|
2020-10-09 16:15:36 +03:00
|
|
|
)
|
|
|
|
|
2021-04-27 08:32:47 +03:00
|
|
|
// Precompute the reflect type for error. Can't use error directly
|
|
|
|
// because Typeof takes an empty interface value. This is annoying.
|
|
|
|
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
2020-10-09 16:15:36 +03:00
|
|
|
|
|
|
|
// Is this an exported - upper case - name?
|
|
|
|
func isExported(name string) bool {
|
2024-04-22 08:47:50 +03:00
|
|
|
r, _ := utf8.DecodeRuneInString(name)
|
|
|
|
return unicode.IsUpper(r)
|
2020-10-09 16:15:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Is this type exported or a builtin?
|
|
|
|
func isExportedOrBuiltinType(t reflect.Type) bool {
|
|
|
|
for t.Kind() == reflect.Ptr {
|
|
|
|
t = t.Elem()
|
|
|
|
}
|
|
|
|
// PkgPath will be non-empty even for an exported type,
|
|
|
|
// so we need to check the type name as well.
|
|
|
|
return isExported(t.Name()) || t.PkgPath() == ""
|
|
|
|
}
|
|
|
|
|
2021-07-27 23:58:06 +03:00
|
|
|
// ValidateSubscriber func signature
|
2020-10-09 16:15:36 +03:00
|
|
|
func ValidateSubscriber(sub Subscriber) error {
|
|
|
|
typ := reflect.TypeOf(sub.Subscriber())
|
|
|
|
var argType reflect.Type
|
2021-04-27 08:32:47 +03:00
|
|
|
switch typ.Kind() {
|
|
|
|
case reflect.Func:
|
2020-10-09 16:15:36 +03:00
|
|
|
name := "Func"
|
|
|
|
switch typ.NumIn() {
|
|
|
|
case 2:
|
|
|
|
argType = typ.In(1)
|
|
|
|
default:
|
2024-04-23 07:28:58 +03:00
|
|
|
return fmt.Errorf("subscriber %v takes wrong number of args: %v required signature %s", name, typ.NumIn(), subSig)
|
2020-10-09 16:15:36 +03:00
|
|
|
}
|
|
|
|
if !isExportedOrBuiltinType(argType) {
|
|
|
|
return fmt.Errorf("subscriber %v argument type not exported: %v", name, argType)
|
|
|
|
}
|
|
|
|
if typ.NumOut() != 1 {
|
2024-04-23 07:28:58 +03:00
|
|
|
return fmt.Errorf("subscriber %v has wrong number of return values: %v require signature %s",
|
|
|
|
name, typ.NumOut(), subSig)
|
2020-10-09 16:15:36 +03:00
|
|
|
}
|
|
|
|
if returnType := typ.Out(0); returnType != typeOfError {
|
|
|
|
return fmt.Errorf("subscriber %v returns %v not error", name, returnType.String())
|
|
|
|
}
|
2021-04-27 08:32:47 +03:00
|
|
|
default:
|
2020-10-09 16:15:36 +03:00
|
|
|
hdlr := reflect.ValueOf(sub.Subscriber())
|
|
|
|
name := reflect.Indirect(hdlr).Type().Name()
|
|
|
|
|
|
|
|
for m := 0; m < typ.NumMethod(); m++ {
|
|
|
|
method := typ.Method(m)
|
|
|
|
switch method.Type.NumIn() {
|
|
|
|
case 3:
|
|
|
|
argType = method.Type.In(2)
|
|
|
|
default:
|
2024-04-23 07:28:58 +03:00
|
|
|
return fmt.Errorf("subscriber %v.%v takes wrong number of args: %v required signature %s",
|
|
|
|
name, method.Name, method.Type.NumIn(), subSig)
|
2020-10-09 16:15:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if !isExportedOrBuiltinType(argType) {
|
|
|
|
return fmt.Errorf("%v argument type not exported: %v", name, argType)
|
|
|
|
}
|
|
|
|
if method.Type.NumOut() != 1 {
|
|
|
|
return fmt.Errorf(
|
2024-04-23 07:28:58 +03:00
|
|
|
"subscriber %v.%v has wrong number of return values: %v require signature %s",
|
|
|
|
name, method.Name, method.Type.NumOut(), subSig)
|
2020-10-09 16:15:36 +03:00
|
|
|
}
|
|
|
|
if returnType := method.Type.Out(0); returnType != typeOfError {
|
|
|
|
return fmt.Errorf("subscriber %v.%v returns %v not error", name, method.Name, returnType.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|