-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
Copy pathcontext_func.go
203 lines (167 loc) · 4.94 KB
/
context_func.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package context
import (
"errors"
"reflect"
"sync"
)
// ErrInvalidArgs fires when the `Context.CallFunc`
// is called with invalid number of arguments.
var ErrInvalidArgs = errors.New("invalid arguments")
// Func represents a function registered by the Context.
// See its `buildMeta` and `call` internal methods.
type Func struct {
RegisterName string // the name of which this function is registered, for information only.
Raw interface{} // the Raw function, can be used for custom casting.
PersistenceArgs []interface{} // the persistence input arguments given on registration.
once sync.Once // guards build once, on first call.
// Available after the first call.
Meta *FuncMeta
}
func newFunc(name string, fn interface{}, persistenceArgs ...interface{}) *Func {
return &Func{
RegisterName: name,
Raw: fn,
PersistenceArgs: persistenceArgs,
}
}
// FuncMeta holds the necessary information about a registered
// context function. Built once by the Func.
type FuncMeta struct {
Handler Handler // when it's just a handler.
HandlerWithErr func(*Context) error // when it's just a handler which returns an error.
RawFunc func() // when it's just a func.
RawFuncWithErr func() error // when it's just a func which returns an error.
RawFuncArgs func(...interface{})
RawFuncArgsWithErr func(...interface{}) error
Value reflect.Value
Type reflect.Type
ExpectedArgumentsLength int
PersistenceInputs []reflect.Value
AcceptsContext bool // the Context, if exists should be always first argument.
ReturnsError bool // when the function's last output argument is error.
}
func (f *Func) buildMeta() {
switch fn := f.Raw.(type) {
case Handler:
f.Meta = &FuncMeta{Handler: fn}
return
// case func(*Context):
// f.Meta = &FuncMeta{Handler: fn}
// return
case func(*Context) error:
f.Meta = &FuncMeta{HandlerWithErr: fn}
return
case func():
f.Meta = &FuncMeta{RawFunc: fn}
return
case func() error:
f.Meta = &FuncMeta{RawFuncWithErr: fn}
return
case func(...interface{}):
f.Meta = &FuncMeta{RawFuncArgs: fn}
return
case func(...interface{}) error:
f.Meta = &FuncMeta{RawFuncArgsWithErr: fn}
return
}
fn := f.Raw
meta := FuncMeta{}
if val, ok := fn.(reflect.Value); ok {
meta.Value = val
} else {
meta.Value = reflect.ValueOf(fn)
}
meta.Type = meta.Value.Type()
if meta.Type.Kind() != reflect.Func {
return
}
meta.ExpectedArgumentsLength = meta.Type.NumIn()
skipInputs := len(meta.PersistenceInputs)
if meta.ExpectedArgumentsLength > skipInputs {
meta.AcceptsContext = isContext(meta.Type.In(skipInputs))
}
if numOut := meta.Type.NumOut(); numOut > 0 {
// error should be the last output.
if isError(meta.Type.Out(numOut - 1)) {
meta.ReturnsError = true
}
}
persistenceArgs := f.PersistenceArgs
if len(persistenceArgs) > 0 {
inputs := make([]reflect.Value, 0, len(persistenceArgs))
for _, arg := range persistenceArgs {
if in, ok := arg.(reflect.Value); ok {
inputs = append(inputs, in)
} else {
inputs = append(inputs, reflect.ValueOf(in))
}
}
meta.PersistenceInputs = inputs
}
f.Meta = &meta
}
func (f *Func) call(ctx *Context, args ...interface{}) ([]reflect.Value, error) {
f.once.Do(f.buildMeta)
meta := f.Meta
if meta.Handler != nil {
meta.Handler(ctx)
return nil, nil
}
if meta.HandlerWithErr != nil {
return nil, meta.HandlerWithErr(ctx)
}
if meta.RawFunc != nil {
meta.RawFunc()
return nil, nil
}
if meta.RawFuncWithErr != nil {
return nil, meta.RawFuncWithErr()
}
if meta.RawFuncArgs != nil {
meta.RawFuncArgs(args...)
return nil, nil
}
if meta.RawFuncArgsWithErr != nil {
return nil, meta.RawFuncArgsWithErr(args...)
}
inputs := make([]reflect.Value, 0, f.Meta.ExpectedArgumentsLength)
inputs = append(inputs, f.Meta.PersistenceInputs...)
if f.Meta.AcceptsContext {
inputs = append(inputs, reflect.ValueOf(ctx))
}
for _, arg := range args {
if in, ok := arg.(reflect.Value); ok {
inputs = append(inputs, in)
} else {
inputs = append(inputs, reflect.ValueOf(arg))
}
}
// keep it here, the inptus may contain the context.
if f.Meta.ExpectedArgumentsLength != len(inputs) {
return nil, ErrInvalidArgs
}
outputs := f.Meta.Value.Call(inputs)
return outputs, getError(outputs)
}
var contextType = reflect.TypeOf((*Context)(nil))
// isContext returns true if the "typ" is a type of Context.
func isContext(typ reflect.Type) bool {
return typ == contextType
}
var errTyp = reflect.TypeOf((*error)(nil)).Elem()
// isError returns true if "typ" is type of `error`.
func isError(typ reflect.Type) bool {
return typ.Implements(errTyp)
}
func getError(outputs []reflect.Value) error {
if n := len(outputs); n > 0 {
lastOut := outputs[n-1]
if isError(lastOut.Type()) {
if lastOut.IsNil() {
return nil
}
return lastOut.Interface().(error)
}
}
return nil
}