-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgen.go
136 lines (115 loc) · 3.55 KB
/
gen.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
package factory
import (
"fmt"
"reflect"
"sync/atomic"
randomdata "github.com/Pallinder/go-randomdata"
)
// adaptValue converts/adapts passed value into value generator
func adaptValue(i interface{}) GeneratorFunc {
return func(Ctx) (interface{}, error) {
return i, nil
}
}
var errorInterface = reflect.TypeOf((*error)(nil)).Elem()
// adaptFunc tries to adapt arbitrary function to be used as generator
func adaptFunc(f interface{}, args ...interface{}) GeneratorFunc {
val := reflect.ValueOf(f)
typ := reflect.TypeOf(f)
// check input argumrnts
if !typ.IsVariadic() && typ.NumIn() != len(args) {
panic(fmt.Errorf("not enough input arguments to make a function call. Expected: %d, was: %d",
typ.NumIn(), len(args)))
}
// check function signature. Perimted number is 1 or 2
if typ.NumOut() == 0 || typ.NumOut() > 2 {
panic(fmt.Errorf("expect function to return 1 or 2 values but was: %d", typ.NumOut()))
}
// check second output parameter implements error interface
if typ.NumOut() == 2 && !typ.Out(1).Implements(errorInterface) {
panic(fmt.Errorf("expect second returned type implement error but found: %+v", typ.Out(1)))
}
// prepare input arguments
in := make([]reflect.Value, len(args))
for i, arg := range args {
in[i] = reflect.ValueOf(arg)
}
return func(Ctx) (interface{}, error) {
r := val.Call(in)
if len(r) == 1 || r[1].IsNil() {
return r[0].Interface(), nil
}
return r[0].Interface(), r[1].Interface().(error)
}
}
// Seq returns function that sequentially generates integers in interval [0, max)
func Seq(max int) func() int {
n := int64(0)
return func() int {
x := int(n)
atomic.AddInt64(&n, 1)
return x % max
}
}
// Rnd returns function that randomly enerates integers in interval [0, max)
func Rnd(max int) func() int {
return func() int {
return randomdata.Number(max)
}
}
// Select picks a value from options
func Select(f func(int) func() int, options ...interface{}) GeneratorFunc {
g := f(len(options))
return func(Ctx) (interface{}, error) {
return options[g()], nil
}
}
// SeqSelect = Select(Seq, options...)
func SeqSelect(options ...interface{}) GeneratorFunc {
return Select(Seq, options...)
}
// RndSelect = Select(Rnd, options...)
func RndSelect(options ...interface{}) GeneratorFunc {
return Select(Rnd, options...)
}
// NewGenerator makes a field generator function
func NewGenerator(i interface{}, args ...interface{}) GeneratorFunc {
// for usecases like:
// func myGenFunc() GeneratorFunc {
// return func(Ctx) (interface{}, error) { ... }
// }
if genFunc, ok := i.(GeneratorFunc); ok {
return genFunc
}
// for usecases like:
// func myGenFunc() func(Ctx) (interface{}, error) {
// return func(Ctx) (interface{}, error) { ... }
// }
//
// or in-place declarations:
// f := NewFactory(
// User{},
// Use(func(ctx Ctx) (interface{}, error) { ... }),
// )
if genFunc, ok := i.(func(Ctx) (interface{}, error)); ok {
return genFunc
}
// if i is a factory use Create method
if fact, ok := i.(*Factory); ok {
return func(Ctx) (interface{}, error) {
return fact.Create()
}
}
// if i is a function, use function to generator converter
if v := reflect.ValueOf(i); v.Kind() == reflect.Func {
// use Func adapter in case i is of Kind Func
return adaptFunc(i, args...)
}
// if it's just some static value, use value to generator converter
if len(args) == 0 {
// use static value generator if no other arguments provided
return adaptValue(i)
}
// otherwise make generator function to randomly select from given options
return RndSelect(append([]interface{}{i}, args...)...)
}