-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcompose.go
61 lines (54 loc) · 1.6 KB
/
compose.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
package utils
import (
"errors"
"reflect"
"strconv"
)
// Compose will compose the received functions, there is no limit on the number of functions.
// Notice that the return value of the composed function is interface{}.
func Compose(functions ...interface{}) (ret func(...interface{}) interface{}, err error) {
defer getErr(&err)
ret = compose(functions...)
return
}
func compose(functions ...interface{}) (ret func(...interface{}) interface{}) {
verifyComposeFuncType(functions)
composedFunc := func(in ...interface{}) interface{} {
param := make([]reflect.Value, 0, len(in))
for _, inParam := range in {
param = append(param, reflect.ValueOf(inParam))
}
for i := 0; i < len(functions); i++ {
thisFn := reflect.ValueOf(functions[i])
param = thisFn.Call(param[:])
}
return param[0].Interface()
}
return composedFunc
}
func verifyComposeFuncType(functions []interface{}) {
for i, function := range functions {
fn := reflect.ValueOf(function)
if fn.Kind() != reflect.Func {
newErr(errors.New("Param "+strconv.Itoa(i)+" is not a function"), "Compose")
}
}
for i := 0; i < len(functions)-2; i++ {
thisFn := reflect.ValueOf(functions[i])
nextFn := reflect.ValueOf(functions[i+1])
if !canPipe(thisFn, nextFn) {
newErr(errors.New("Function "+strconv.Itoa(i)+" and "+strconv.Itoa(i+1)+" cannot be piped."), "Compose")
}
}
}
func canPipe(thisFn, nextFn reflect.Value) bool {
if thisFn.Type().NumOut() != nextFn.Type().NumIn() {
return false
}
for i := 0; i < thisFn.Type().NumOut(); i++ {
if thisFn.Type().Out(i) != nextFn.Type().In(i) {
return false
}
}
return true
}