-
Notifications
You must be signed in to change notification settings - Fork 1
/
reflect.go
98 lines (76 loc) · 2.47 KB
/
reflect.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
package jopher
import (
"errors"
"reflect"
)
// reflectFunction converts a supplied interface into a reflect.Value
func reflectFunction(function interface{}) reflect.Value {
reflected := reflect.ValueOf(function)
if reflected.Kind() != reflect.Func {
panic(errors.New("please supply a function"))
}
return reflected
}
// callReflected calls a reflected function, returning the a slice of results and an error
func callReflected(fn reflect.Value, args ...interface{}) (interface{}, error) {
// Reflect all the arguments and call the function
reflectedArgs := reflectAll(args...)
results := fn.Call(reflectedArgs)
// Determine if the function returns an error as the last return value
hasError := hasLastError(fn.Type())
// Split the results into a slice of interfaces and an error value
result, err := splitResults(results, hasError)
if err != nil {
return nil, err
}
return result, nil
}
// reflectAll converts the supplied arguments to reflect values
func reflectAll(args ...interface{}) []reflect.Value {
reflected := make([]reflect.Value, len(args))
for i := range args {
reflected[i] = reflect.ValueOf(args[i])
}
return reflected
}
// unReflectAll converts the supplied reflect values to a slice of interfaces
func unReflectAll(results []reflect.Value) []interface{} {
outs := make([]interface{}, len(results))
for i := range results {
outs[i] = results[i].Interface()
}
return outs
}
// splitResults splits a slice of results into an interface and an error. The interface could contain
// nil (if no value was returned), a single value (if a single value was returned), or a slice
// of interface{}s (if multiple values were returned).
func splitResults(results []reflect.Value, lastError bool) (interface{}, error) {
count := len(results)
// Fish out the error at the end
var err error
if lastError && count > 0 {
var errorValue reflect.Value
results, errorValue = results[:count-1], results[count-1]
if errorValue.IsValid() && !errorValue.IsNil() {
err = errorValue.Interface().(error)
}
}
// Clean up the returned result
actualResults := unReflectAll(results)
switch len(actualResults) {
case 0:
return nil, err
case 1:
return actualResults[0], err
default:
return actualResults, err
}
}
// hasLastError determines if the last return argument of a function is an error
func hasLastError(t reflect.Type) bool {
count := t.NumOut()
if count == 0 {
return false
}
return t.Out(count-1) == reflect.ValueOf((*error)(nil)).Type().Elem()
}