-
Notifications
You must be signed in to change notification settings - Fork 375
/
config.go
206 lines (194 loc) · 6 KB
/
config.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package omnisearch
import (
"fmt"
"reflect"
)
type coordinatorConfig struct {
providers []Provider
coordinator *coordinator
}
func (c *coordinatorConfig) usingConstructorMake(constructor interface{}, target reflect.Type) (interface{}, error) {
value := reflect.ValueOf(constructor)
typ := value.Type()
if typ.Kind() != reflect.Func {
return nil, fmt.Errorf("got a %s", typ.Name())
}
iTgt := -1
iError := -1
// Finding the index of the returns we want.
{
i := typ.NumOut()
for i > 0 {
i--
typOut := typ.Out(i)
if typOut == target || typOut.ConvertibleTo(target) {
if iTgt != -1 {
return nil, fmt.Errorf("type %s as constructor have more than one %s return", typ.Name(), target.Name())
}
iTgt = i
} else if typOut == errorType || typOut.ConvertibleTo(errorType) {
if iError != -1 {
return nil, fmt.Errorf("type %s as constructor have more than one error return", typ.Name())
}
iError = i
}
// Ignoring useless one.
}
}
if iTgt == -1 {
return nil, fmt.Errorf("type %s as constructor doesn't return any %s", typ.Name(), target.Name())
}
// Creating the argument slice
var args []reflect.Value
{
i := typ.NumIn()
args = make([]reflect.Value, i)
for i > 0 {
i--
typIn := typ.In(i)
ProviderLoop:
for _, p := range c.providers {
for _, pt := range p.Available() {
if pt == typIn || pt.ConvertibleTo(typIn) {
rv, err := p.Make(pt)
if err != nil {
return nil, fmt.Errorf("error building %s for %s: provider %q errored: %w", typIn.Name(), typ.Name(), p.Name(), err)
}
rt := rv.Type()
if rt != typIn {
if rt.ConvertibleTo(typIn) {
rv = rv.Convert(typIn)
} else {
return nil, fmt.Errorf("error building %s for %s: provider %q returned wrong type %s while %s was expected", typIn.Name(), typ.Name(), p.Name(), rt.Name(), typIn.Name())
}
}
args[i] = rv
break ProviderLoop
}
}
}
}
}
// Calling the function
out := value.Call(args)
if iError != -1 {
err := out[iError].Interface()
if err != nil {
return nil, fmt.Errorf("type %s as constructor errored: %w", typ.Name(), err.(error))
}
}
// Checking for nil
tgt := out[iTgt].Interface()
if tgt == nil {
return nil, fmt.Errorf("type %s as constructor returned nil %s", typ.Name(), target.Name())
}
return tgt, nil
}
// Configurator is used to configure the search engine.
type Configurator func(*coordinatorConfig) error
// CMerge merges multiple configurator, from first to last.
func CMerge(cfgs ...Configurator) Configurator {
return func(c *coordinatorConfig) error {
for _, v := range cfgs {
err := v(c)
if err != nil {
return err
}
}
return nil
}
}
var (
providerType = reflect.TypeOf((*Provider)(nil)).Elem()
engineType = reflect.TypeOf((*Engine)(nil)).Elem()
parserType = reflect.TypeOf((*Parser)(nil)).Elem()
errorType = reflect.TypeOf((*error)(nil)).Elem()
)
// CAddProvider will add providers to the config, from first to last.
// It accept either instancied Provider or function returning a Provider.
// The function can have some params, in this case the provider logic will be used.
// The function must return a Provider and may return an error, if it returns an error it will be checked against nil and any non nil value will error.
// The provider will be registered for each type only if the type is not yet registered.
func CAddProvider(provs ...interface{}) Configurator {
return func(c *coordinatorConfig) error {
for _, v := range provs {
if v == nil {
return fmt.Errorf("expected a Provider or a Provider constructor, but got a nil value %T", v)
}
// First try if that is instancied ?
p, ok := v.(Provider)
if ok {
goto AddProvider
}
{
// No we have to build it ourself
r, err := c.usingConstructorMake(v, providerType)
if err != nil {
return fmt.Errorf("expected a Provider or a Provider constructor, but %s", err)
}
p = r.(Provider)
}
AddProvider:
c.providers = append(c.providers, p)
}
return nil
}
}
// CAddEngine will add engines to the Coordinator.
// It accept either instancied Engine or function returning an Engine.
// The function can have some params, in this case the provider logic will be used.
// The function must return an Engine and may return an error, if it returns an error it will be checked against nil and any non nil value will error.
func CAddEngine(engines ...interface{}) Configurator {
return func(c *coordinatorConfig) error {
for _, v := range engines {
if v == nil {
return fmt.Errorf("expected an Engine or an Engine constructor, but got a nil value %T", v)
}
// First try if that is instancied ?
e, ok := v.(Engine)
if ok {
goto AddEngine
}
// No we have to build it ourself
{
r, err := c.usingConstructorMake(v, engineType)
if err != nil {
return fmt.Errorf("expected an Engine or an Engine constructor, but %w", err)
}
e = r.(Engine)
}
AddEngine:
c.coordinator.engines = append(c.coordinator.engines, e)
}
return nil
}
}
// CAddParser adds some parser to the coordinator.
// It accept either instancied Parser or function returning a Parser.
// The function can have some params, in this case the provider logic will be used.
// The function must return a Parser and may return an error, if it returns an error it will be checked against nil and any non nil value will error.
func CAddParser(parsers ...interface{}) Configurator {
return func(c *coordinatorConfig) error {
for _, v := range parsers {
if v == nil {
return fmt.Errorf("expected a Parser or a Parser constructor, but got a nil value %T", v)
}
// First try if that is instancied ?
p, ok := v.(Parser)
if ok {
goto AddParser
}
// No we have to build it ourself
{
r, err := c.usingConstructorMake(v, parserType)
if err != nil {
return fmt.Errorf("expected a Parser or a Parser constructor, but %w", err)
}
p = r.(Parser)
}
AddParser:
c.coordinator.parsers = append(c.coordinator.parsers, p)
}
return nil
}
}