-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathparams_transform.go
More file actions
97 lines (85 loc) · 2.07 KB
/
params_transform.go
File metadata and controls
97 lines (85 loc) · 2.07 KB
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
package clingy
import (
"reflect"
"time"
"github.com/zeebo/errs/v2"
)
func transformParam(arg *param, val interface{}) (_ interface{}, err error) {
call := callOne
if arg.rep {
call = callMany
}
rval := reflect.ValueOf(val)
for _, fn := range arg.fns {
rval, err = call(rval, reflect.ValueOf(fn))
if err != nil {
return arg.zero(), err
}
}
if arg.opt && !arg.rep {
rval = ptrTo(rval)
}
return rval.Interface(), nil
}
func callMany(rval, rfn reflect.Value) (reflect.Value, error) {
if rval.IsNil() {
return reflect.Zero(reflect.SliceOf(rfn.Type().Out(0))), nil
}
out := reflect.MakeSlice(reflect.SliceOf(rfn.Type().Out(0)), rval.Len(), rval.Len())
for i := 0; i < rval.Len(); i++ {
result, err := callOne(rval.Index(i), rfn)
if err != nil {
return reflect.Value{}, err
}
out.Index(i).Set(result)
}
return out, nil
}
func callOne(rval, rfn reflect.Value) (reflect.Value, error) {
results := rfn.Call([]reflect.Value{rval})
if err, ok := results[1].Interface().(error); ok && err != nil {
return reflect.Value{}, err
}
return results[0], nil
}
var (
stringType = reflect.TypeOf("")
boolType = reflect.TypeOf(false)
durationType = reflect.TypeOf(time.Duration(0))
errorType = reflect.TypeOf((*error)(nil)).Elem()
)
func guessType(fns []interface{}) reflect.Type {
typ := stringType
if len(fns) > 0 {
ftyp := reflect.TypeOf(fns[len(fns)-1])
if ftyp.Kind() == reflect.Func && ftyp.NumOut() > 0 {
typ = ftyp.Out(0)
}
}
return typ
}
func zero(typ reflect.Type) interface{} {
return reflect.Zero(typ).Interface()
}
func ptrTo(x reflect.Value) reflect.Value {
y := reflect.New(x.Type())
y.Elem().Set(x)
return y
}
func checkFns(fns []interface{}) (reflect.Type, error) {
typ := stringType
for _, fn := range fns {
ftyp := reflect.TypeOf(fn)
switch {
case false,
ftyp.Kind() != reflect.Func,
ftyp.NumIn() != 1,
ftyp.NumOut() != 2,
ftyp.In(0) != typ,
ftyp.Out(1) != errorType:
return guessType(fns), errs.Errorf("transform: %v cannot be applied to %v", ftyp, typ)
}
typ = ftyp.Out(0)
}
return typ, nil
}