-
Notifications
You must be signed in to change notification settings - Fork 0
/
getter.go
249 lines (214 loc) · 6.88 KB
/
getter.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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
// Copyright © 2018 Kent Gibson <warthog618@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package config
import (
"strings"
"github.com/warthog618/config/keys"
)
// Getter specifies the minimal interface for a configuration Getter.
//
// A Getter must be safe for concurrent use by multiple goroutines.
type Getter interface {
// Get the value of the named config leaf key.
// Also returns an ok, similar to a map read, to indicate if the value was
// found.
// The type underlying the returned interface{} must be convertable to the
// expected type by cfgconv.
//
// Get does not need to support getting of objects, as returning of complete
// objects is neither supported nor required.
//
// But it does support getting of arrays.
// For arrays, referenced by the array name say "ax", a []interface{} must
// be returned.
// Array elements are referenced using keys of form "ax[idx]", where idx is
// the zero-based index into the array.
// The length of the array is returned by a key of form "ax[]".
// If the getter only contains part of the array then it should return only
// the elements it contains, not "ax" or "ax[]".
//
// For arrays of objects the array must be returned, to be consistent with
// other arrays, but the elements may be nil.
//
// Must be safe to call from multiple goroutines.
Get(key string) (value interface{}, found bool)
}
// GetterAsOption allows a Getter to be passed to New as an option.
type GetterAsOption struct {
}
func (s GetterAsOption) applyConfigOption(c *Config) {
}
// UpdateCommit is a function that commits an update to a getter.
// After the call the change becomes visible to Get.
type UpdateCommit func()
// WatchableGetter is the interface supported by Getters that may support being
// watched.
type WatchableGetter interface {
// Create a watcher on the getter.
// Watcher will exit if the done chan closes.
// Watcher will send updates via the update channel.
// Watcher will send terminal errors via the err channel.
NewWatcher(done <-chan struct{}) GetterWatcher
}
// GetterWatcher contains channels returning updates and errors from Getter
// watchers.
type GetterWatcher interface {
Update() <-chan GetterUpdate
}
// GetterUpdate contains an update from a getter.
type GetterUpdate interface {
Commit()
}
type getterWatcher struct {
uch chan GetterUpdate
}
func (g *getterWatcher) Update() <-chan GetterUpdate {
return g.uch
}
func newGetterWatcher() *getterWatcher {
return &getterWatcher{uch: make(chan GetterUpdate)}
}
// Decorator is a func that takes one Getter and returns
// a decorated Getter.
type Decorator func(Getter) Getter
// getterDecorator is a common implementation for decorators.
type getterDecorator struct {
g Getter
}
// NewWatcher implements the WatchableGetter interface.
func (g getterDecorator) NewWatcher(done <-chan struct{}) GetterWatcher {
if wg, ok := g.g.(WatchableGetter); ok {
return wg.NewWatcher(done)
}
return nil
}
// Decorate applies an ordered list of decorators to a Getter.
// The decorators are applied in reverse order, to create a decorator chain with
// the first decorator being the first link in the chain.
// When the returned getter is used, the first decorator is called first, then
// the second, etc and finally the decorated Getter itself.
func Decorate(g Getter, dd ...Decorator) Getter {
if g == nil {
return nil
}
dg := g
for i := len(dd) - 1; i >= 0; i-- {
dg = dd[i](dg)
}
return dg
}
// WithFallback provides a Decorator that falls back to a default Getter if the
// key is not found in the decorated Getter.
func WithFallback(d Getter) Decorator {
return func(g Getter) Getter {
if d == nil {
return g
}
return Overlay(g, d)
}
}
// WithGraft returns a decorator that attaches the root of the decorated Getter
// to a node in the config space.
// The prefix defines where the root node of the getter is located in the config
// space. The prefix must include any separator prior to the first field.
//
// e.g. with a prefix "a.module.", reading the key "a.module.field" from the
// WithGraft will return the "field" from the wrapped Getter.
func WithGraft(prefix string) Decorator {
return func(g Getter) Getter {
return graftDecorator{getterDecorator{g}, prefix}
}
}
type graftDecorator struct {
getterDecorator
prefix string
}
func (g graftDecorator) Get(key string) (interface{}, bool) {
if !strings.HasPrefix(key, g.prefix) {
return nil, false
}
key = key[len(g.prefix):]
return g.g.Get(key)
}
// WithKeyReplacer provides a decorator which performs a transformation on the
// key using the ReplacerFunc before calling the Getter.
func WithKeyReplacer(r keys.Replacer) Decorator {
return func(g Getter) Getter {
if r == nil {
return g
}
return keyReplacerDecorator{getterDecorator{g}, r}
}
}
type keyReplacerDecorator struct {
getterDecorator
r keys.Replacer
}
func (g keyReplacerDecorator) Get(key string) (interface{}, bool) {
return g.g.Get(g.r.Replace(key))
}
// WithMustGet provides a Decorator that panics if a key is not found by the
// decorated Getter.
func WithMustGet() Decorator {
return func(g Getter) Getter {
return mustDecorator{getterDecorator{g}}
}
}
type mustDecorator struct {
getterDecorator
}
func (g mustDecorator) Get(key string) (interface{}, bool) {
v, found := g.g.Get(key)
if !found {
panic(NotFoundError{Key: key})
}
return v, true
}
// WithPrefix provides a Decorator that adds a prefix to the key before calling
// the Getter.
// This is a common special case of KeyReplacer where the key is prefixed with a
// fixed string.
func WithPrefix(prefix string) Decorator {
return func(g Getter) Getter {
return prefixDecorator{getterDecorator{g}, prefix}
}
}
type prefixDecorator struct {
getterDecorator
prefix string
}
func (g prefixDecorator) Get(key string) (interface{}, bool) {
return g.g.Get(g.prefix + key)
}
// UpdateHandler receives an update, performs some transformation
// on it, and forwards (or not) the transformed update.
// Must return if either the done or in channels are closed.
// May close the out chan to indicate that no further updates are possible.
type UpdateHandler func(done <-chan struct{}, in <-chan GetterUpdate, out chan<- GetterUpdate)
// WithUpdateHandler adds an update processing decorator to a Getter.
func WithUpdateHandler(handler UpdateHandler) Decorator {
return func(g Getter) Getter {
if _, ok := g.(WatchableGetter); !ok {
return g
}
return updateDecorator{g: g, h: handler}
}
}
type updateDecorator struct {
g Getter
h UpdateHandler
}
// NewWatcher implements the WatchableGetter interface.
func (g updateDecorator) NewWatcher(done <-chan struct{}) GetterWatcher {
wg := g.g.(WatchableGetter)
w := newGetterWatcher()
gw := wg.NewWatcher(done)
go g.h(done, gw.Update(), w.uch)
return w
}
// Get implements the Watcher interface.
func (g updateDecorator) Get(key string) (interface{}, bool) {
return g.g.Get(key)
}