-
Notifications
You must be signed in to change notification settings - Fork 83
/
functions.go
128 lines (115 loc) · 2.85 KB
/
functions.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
package gval
import (
"context"
"fmt"
"reflect"
)
type function func(ctx context.Context, arguments ...interface{}) (interface{}, error)
func toFunc(f interface{}) function {
if f, ok := f.(func(arguments ...interface{}) (interface{}, error)); ok {
return function(func(ctx context.Context, arguments ...interface{}) (interface{}, error) {
var v interface{}
errCh := make(chan error, 1)
go func() {
defer func() {
if recovered := recover(); recovered != nil {
errCh <- fmt.Errorf("%v", recovered)
}
}()
result, err := f(arguments...)
v = result
errCh <- err
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case err := <-errCh:
close(errCh)
return v, err
}
})
}
if f, ok := f.(func(ctx context.Context, arguments ...interface{}) (interface{}, error)); ok {
return function(f)
}
fun := reflect.ValueOf(f)
t := fun.Type()
return func(ctx context.Context, args ...interface{}) (interface{}, error) {
var v interface{}
errCh := make(chan error, 1)
go func() {
defer func() {
if recovered := recover(); recovered != nil {
errCh <- fmt.Errorf("%v", recovered)
}
}()
in, err := createCallArguments(ctx, t, args)
if err != nil {
errCh <- err
return
}
out := fun.Call(in)
r := make([]interface{}, len(out))
for i, e := range out {
r[i] = e.Interface()
}
err = nil
errorInterface := reflect.TypeOf((*error)(nil)).Elem()
if len(r) > 0 && t.Out(len(r)-1).Implements(errorInterface) {
if r[len(r)-1] != nil {
err = r[len(r)-1].(error)
}
r = r[0 : len(r)-1]
}
switch len(r) {
case 0:
v = nil
case 1:
v = r[0]
default:
v = r
}
errCh <- err
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case err := <-errCh:
close(errCh)
return v, err
}
}
}
func createCallArguments(ctx context.Context, t reflect.Type, args []interface{}) ([]reflect.Value, error) {
variadic := t.IsVariadic()
numIn := t.NumIn()
// if first argument is a context, use the given execution context
if numIn > 0 {
thisFun := reflect.ValueOf(createCallArguments)
thisT := thisFun.Type()
if t.In(0) == thisT.In(0) {
args = append([]interface{}{ctx}, args...)
}
}
if (!variadic && len(args) != numIn) || (variadic && len(args) < numIn-1) {
return nil, fmt.Errorf("invalid number of parameters")
}
in := make([]reflect.Value, len(args))
var inType reflect.Type
for i, arg := range args {
if !variadic || i < numIn-1 {
inType = t.In(i)
} else if i == numIn-1 {
inType = t.In(numIn - 1).Elem()
}
argVal := reflect.ValueOf(arg)
if arg == nil {
argVal = reflect.ValueOf(reflect.Interface)
} else if !argVal.Type().AssignableTo(inType) {
return nil, fmt.Errorf("expected type %s for parameter %d but got %T",
inType.String(), i, arg)
}
in[i] = argVal
}
return in, nil
}