/
utils.go
134 lines (121 loc) · 3.08 KB
/
utils.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
package flotilla
import (
"fmt"
"reflect"
"strings"
)
var (
errorType = reflect.TypeOf((*error)(nil)).Elem()
)
func existsIn(s string, l []string) bool {
for _, x := range l {
if s == x {
return true
}
}
return false
}
func doAdd(s string, ss []string) []string {
if isAppendable(s, ss) {
ss = append(ss, s)
}
return ss
}
func isAppendable(s string, ss []string) bool {
for _, x := range ss {
if x == s {
return false
}
}
return true
}
func isFunc(fn interface{}) bool {
return reflect.ValueOf(fn).Kind() == reflect.Func
}
// See https://github.com/zenazn/goji/blob/master/web/func_equal.go
func equalFunc(a, b interface{}) bool {
if !isFunc(a) || !isFunc(b) {
panic("funcEqual: type error!")
}
av := reflect.ValueOf(&a).Elem()
bv := reflect.ValueOf(&b).Elem()
return av.InterfaceData() == bv.InterfaceData()
}
func valueFunc(fn interface{}) reflect.Value {
v := reflect.ValueOf(fn)
if v.Kind() != reflect.Func {
panic(newError("Provided:(%+v, type: %T), but it is not a function", fn, fn))
}
if !goodFunc(v.Type()) {
panic(newError("Cannot use function %q with %d results\nreturn must be 1 value, or 1 value and 1 error value", fn, v.Type().NumOut()))
}
return v
}
func goodFunc(typ reflect.Type) bool {
switch {
case typ.NumOut() == 1:
return true
case typ.NumOut() == 2 && typ.Out(1) == errorType:
return true
}
return false
}
func reflectFuncs(fns map[string]interface{}) map[string]reflect.Value {
ret := make(map[string]reflect.Value)
for k, v := range fns {
ret[k] = valueFunc(v)
}
return ret
}
func canBeNil(typ reflect.Type) bool {
switch typ.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
return true
}
return false
}
// From http://golang.org/src/pkg/text/template/funcs.go
func call(fn reflect.Value, args ...interface{}) (interface{}, error) {
typ := fn.Type()
numIn := typ.NumIn()
var dddType reflect.Type
if typ.IsVariadic() {
if len(args) < numIn-1 {
return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
}
dddType = typ.In(numIn - 1).Elem()
} else {
if len(args) != numIn {
return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
}
}
argv := make([]reflect.Value, len(args))
for i, arg := range args {
value := reflect.ValueOf(arg)
// Compute the expected type. Clumsy because of variadics.
var argType reflect.Type
if !typ.IsVariadic() || i < numIn-1 {
argType = typ.In(i)
} else {
argType = dddType
}
if !value.IsValid() && canBeNil(argType) {
value = reflect.Zero(argType)
}
if !value.Type().AssignableTo(argType) {
return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
}
argv[i] = value
}
result := fn.Call(argv)
if len(result) == 2 && !result[1].IsNil() {
return result[0].Interface(), result[1].Interface().(error)
}
return result[0].Interface(), nil
}
func dropTrailing(path string, trailing string) string {
if fp := strings.Split(path, "/"); fp[len(fp)-1] == trailing {
return strings.Join(fp[0:len(fp)-1], "/")
}
return path
}