in wstl1/mapping_engine/projector/projector.go [61:158]
func FromFunction(fn interface{}, name string) (types.Projector, error) {
tokenType := reflect.TypeOf((*jsonutil.JSONToken)(nil)).Elem()
f := reflect.ValueOf(fn)
if f.Kind() != reflect.Func {
return nil, fmt.Errorf("projector must be a function")
}
// Check args are our JSON types.
ft := reflect.TypeOf(fn)
for i := 0; i < ft.NumIn(); i++ {
isObj := ft.In(i).AssignableTo(tokenType)
isSliceOfObj := ft.In(i).Kind() == reflect.Slice && ft.In(i).Elem().AssignableTo(tokenType)
if !isObj && !isSliceOfObj {
return nil, fmt.Errorf("parameter %d is of type %v which is not supported", i, ft.In(i))
}
}
// Check return type is what we expect.
if ft.NumOut() != 2 {
return nil, fmt.Errorf("incorrect return type, expected (jsonutil.JSONToken, error) got %d return values", ft.NumOut())
}
if !ft.Out(0).AssignableTo(tokenType) || !ft.Out(1).AssignableTo(reflect.TypeOf((*error)(nil)).Elem()) {
return nil, fmt.Errorf("incorrect return type, expected (jsonutil.JSONToken, error) got (%v, %v)", ft.Out(0), ft.Out(1))
}
// Build wrapper closure.
return func(metaArgs []jsonutil.JSONMetaNode, pctx *types.Context) (jsonutil.JSONToken, error) {
errLocation := errors.FnLocationf("Native Function Preamble %q", name)
if err := pctx.PushProjectorToStack(name); err != nil {
return nil, errors.Wrap(errLocation, err)
}
// Lose the meta.
args := make([]jsonutil.JSONToken, len(metaArgs))
for i, metaArg := range metaArgs {
node, err := jsonutil.NodeToToken(metaArg)
if err != nil {
return nil, errors.Wrap(errLocation, fmt.Errorf("error converting args: %v", err))
}
args[i] = node
}
if ft.IsVariadic() && len(args) < ft.NumIn()-1 {
return nil, errors.Wrap(errLocation, fmt.Errorf("expected at least %d parameters (could be more, function is variadic), got %d", ft.NumIn()-1, len(args)))
}
if !ft.IsVariadic() && len(args) != ft.NumIn() {
return nil, errors.Wrap(errLocation, fmt.Errorf("expected %d parameters, got %d", ft.NumIn(), len(args)))
}
argvs := make([]reflect.Value, 0, len(args))
for i, arg := range args {
if ft.IsVariadic() && i == ft.NumIn()-1 {
a, err := extractVariadic(ft.In(i).Elem(), args[i:])
if err != nil {
return nil, errors.Wrap(errLocation, fmt.Errorf("error extracting variadic argument %d: %v", i, err))
}
argvs = append(argvs, a...)
break
}
if ft.In(i).Kind() == reflect.Slice {
a, err := extractSlice(ft.In(i).Elem(), arg)
if err != nil {
return nil, errors.Wrap(errLocation, fmt.Errorf("error extracting slice argument %d: %v", i, err))
}
argvs = append(argvs, a)
continue
}
a, err := extractSimple(ft.In(i), arg)
if err != nil {
return nil, errors.Wrap(errLocation, fmt.Errorf("error extracting argument %d: %v", i, err))
}
argvs = append(argvs, a)
}
result := f.Call(argvs)
var r jsonutil.JSONToken
var err error
if ri := result[0].Interface(); ri != nil {
r = ri.(jsonutil.JSONToken)
}
if ri := result[1].Interface(); ri != nil {
err = ri.(error)
}
pctx.PopProjectorFromStack(name)
if err != nil {
return nil, errors.Wrap(errors.FnLocationf("Native Function %q", name), err)
}
return r, nil
}, nil
}