forked from Cepave/open-falcon-backend
-
Notifications
You must be signed in to change notification settings - Fork 0
/
base.go
368 lines (318 loc) · 9.3 KB
/
base.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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
//
// This package provides lazy-loading and postfix binding for text building.
//
// Abstract
//
// When we are generating complex SQL statement,
// the final code of SQL is decided by query parameters:
// SELECT *
// FROM tab_car
// WHERE car_name = ?
//
// For 'car_name = ?', it is only shown if and only if user has input "viable" value on search field of UI.
//
// Text by condition
//
// You could use post-condition to decide whether or not a text should be shown:
// t := StringGetter("Hello! If you are happy.").Post().Viable(false)
//
// fmt.Sprintf("%s", t) // Nothing shown
//
// With SQL syntax, you could decide whether or not to generate "WHERE" by variable:
// where := Prefix(
// StringGetter("WHERE "),
// StrinGetter("car_name = ?").
// Post().Viable(carName != ""),
// )
//
// Breeds multiple text
//
// You could use some built-in functions to generate multiple text by array/slice.
// arrayData := []int { 1, 3, 5 }
//
// // Generates "?, ?, ?"
// conditions := RepeatAndJoinByLen("?", StringGetter(", "), arrayData)
//
// Short term DSL
//
// There are some pre-defined, short-typed DSLs to make code shorter:
// where := Prefix(Dsl.S("WHERE "), Dsl.S("car_name = ?").
// Post().Viable(carName != ""))
//
package textbuilder
import (
"fmt"
"reflect"
"unicode/utf8"
)
// This instance provides string of empty("")
const EmptyGetter = StringGetter("")
// Defines the common interface for a text object,
// which is evaluated lazily.
type TextGetter interface {
fmt.Stringer
Post
}
// Defines the transformation of a TextGetter to another
type Transformer func(TextGetter) TextGetter
// Defines the function to generate TextList from a TextGetter
type Breeder func(TextGetter) TextList
// Defines the function to reduce a TextList to a TextGetter
type Distiller func(TextList) TextGetter
// Gets posting processor of a TextGetter
type Post interface {
// Retrieves the PostProcessor, which defines interfaces for postfix on TextGetter
Post() PostProcessor
}
// Defines the operations of post processor
type PostProcessor interface {
TextGetter
Transform(t Transformer) PostProcessor
Breed(b Breeder) TextList
Prefix(prefix TextGetter) PostProcessor
Suffix(suffix TextGetter) PostProcessor
Surrounding(prefix TextGetter, suffix TextGetter) PostProcessor
Repeat(times int) TextList
RepeatByLen(lenObject interface{}) TextList
Viable(v bool) PostProcessor
}
// Initialize a new instance of post processor with default operations
func NewPost(content TextGetter) *DefaultPost {
return &DefaultPost{content}
}
// Implements default post processor
type DefaultPost struct {
content TextGetter
}
func (p *DefaultPost) Transform(t Transformer) PostProcessor {
p.content = t(p.content)
return p
}
func (p *DefaultPost) Breed(b Breeder) TextList {
return b(p.content)
}
func (p *DefaultPost) Prefix(prefix TextGetter) PostProcessor {
p.content = Prefix(prefix, p.content)
return p
}
func (p *DefaultPost) Suffix(suffix TextGetter) PostProcessor {
p.content = Suffix(p.content, suffix)
return p
}
func (p *DefaultPost) Surrounding(prefix TextGetter, suffix TextGetter) PostProcessor {
p.content = Surrounding(prefix, p.content, suffix)
return p
}
func (p *DefaultPost) Repeat(times int) TextList {
return Repeat(p.content, times)
}
func (p *DefaultPost) RepeatByLen(lenObject interface{}) TextList {
return RepeatByLen(p.content, lenObject)
}
func (p *DefaultPost) Viable(v bool) PostProcessor {
if !v {
p.content = EmptyGetter
}
return p
}
func (p *DefaultPost) Post() PostProcessor {
return p
}
func (p *DefaultPost) String() string {
return p.content.String()
}
// Implements the text getter with string value
type StringGetter string
func (t StringGetter) String() string {
return string(t)
}
func (t StringGetter) Post() PostProcessor {
return NewPost(t)
}
// Converts fmt.Stringer interface to TextGetter
func NewStringerGetter(v fmt.Stringer) *StringerGetter {
return &StringerGetter{v}
}
type StringerGetter struct {
stringer fmt.Stringer
}
func (s *StringerGetter) String() string {
return s.stringer.String()
}
func (s *StringerGetter) Post() PostProcessor {
return NewPost(s)
}
// Used to get len of an object
//
// This interface is usually used with RepeatByLen().
type ObjectLen interface {
Len() int
}
type TextList interface {
ListPost
ObjectLen
Get(int) TextGetter
}
// Gets posting processor of a TextList
type ListPost interface {
Post() ListPostProcessor
}
// Defines operations for a TextList
type ListPostProcessor interface {
Distill(Distiller) TextGetter
Join(separator TextGetter) TextGetter
}
// Initialzie an instance of DefaultListPost
func NewListPost(list TextList) *DefaultListPost {
return &DefaultListPost{list}
}
// Implements default post prcessor for a list
type DefaultListPost struct {
list TextList
}
func (l *DefaultListPost) Join(separator TextGetter) TextGetter {
return JoinTextList(separator, l.list)
}
func (l *DefaultListPost) Distill(d Distiller) TextGetter {
return d(l.list)
}
type TextGetters []TextGetter
func Getters(getters ...TextGetter) TextGetters {
return TextGetters(getters)
}
func (t TextGetters) Get(index int) TextGetter {
return t[index]
}
func (t TextGetters) Len() int {
return len(t)
}
func (t TextGetters) Post() ListPostProcessor {
return NewListPost(t)
}
// Converts any value to TextGetter
//
// If the value is text getter, this function return it natively.
//
// If the value is string, this function cast it to StringGetter.
//
// Otherwise, use fmt.Sprintf("%v") to retrieve the string representation of input value.
func ToTextGetter(v interface{}) TextGetter {
switch castedValue := v.(type) {
case TextGetter:
return castedValue
case fmt.Stringer:
return NewStringerGetter(v.(fmt.Stringer))
case []byte:
return StringGetter(string(castedValue))
case string:
return StringGetter(castedValue)
}
return TextGetterPrintf("%v", v)
}
// Converts multiple values to TextList, for the conversion of element, see ToTextGetter
func ToTextList(anyObjects ...interface{}) TextList {
getters := make([]TextGetter, len(anyObjects))
for i, v := range anyObjects {
getters[i] = ToTextGetter(v)
}
return TextGetters(getters)
}
func TextGetterPrintf(format string, a ...interface{}) TextGetter {
return &formatterImpl{format, a}
}
// Builds viable getter if the value is viable
//
// The value would be evaluated eagerly.
//
// For string - must be non-empty
// For TextGetter - the result of content must be non empty
// For array, slice, map, chan - the len(array) > 0
//
// Otherwise - value.IsNil() should be false
func IsViable(value interface{}) bool {
switch textValue := value.(type) {
case string:
return textValue != ""
case fmt.Stringer:
return IsViable(NewStringerGetter(textValue).String())
case TextGetter:
return IsViable(textValue.String())
}
reflectValue := reflect.ValueOf(value)
switch reflectValue.Type().Kind() {
case reflect.Array, reflect.Slice, reflect.Map, reflect.Chan:
return reflectValue.Len() > 0
}
return !reflectValue.IsNil()
}
// Prefixing the content(if the content viable)
func Prefix(prefix TextGetter, content TextGetter) TextGetter {
return &prefixImpl{prefix, content}
}
// Suffixing the content(if the content is viable)
func Suffix(content TextGetter, suffix TextGetter) TextGetter {
return &suffixImpl{content, suffix}
}
// Surrounding the content(if the content is viable)
func Surrounding(prefix TextGetter, content TextGetter, suffix TextGetter) TextGetter {
return &surroundingImpl{prefix, content, suffix}
}
// Surrounding the content(if the content is viable)
func SurroundingSame(s TextGetter, content TextGetter) TextGetter {
return &surroundingImpl{s, content, s}
}
// Joining the viable element of getters
func Join(separator TextGetter, getters ...TextGetter) TextGetter {
return JoinTextList(separator, TextGetters(getters))
}
// Joining the viable element of TextList
func JoinTextList(separator TextGetter, textList TextList) TextGetter {
return &joinImpl{separator, textList}
}
// Repeating the viable element of TextList
func Repeat(text TextGetter, times int) TextList {
list := make(TextGetters, times)
for i := 0; i < times; i++ {
list[i] = text
}
return list
}
// Repeats the len of object:
//
// For object len: use Len() function
// For String: use utf8.RuneCountInString(<string>) function
// For Array, Chan, Map, or Slice: use reflect.Value.Len() function
func RepeatByLen(text TextGetter, lenObject interface{}) TextList {
var repeatTimes int
switch v := lenObject.(type) {
case ObjectLen:
repeatTimes = v.Len()
case string:
repeatTimes = utf8.RuneCountInString(v)
default:
value := reflect.ValueOf(lenObject)
switch value.Kind() {
case reflect.Array, reflect.Slice, reflect.Chan, reflect.Map:
repeatTimes = value.Len()
default:
panic(fmt.Sprintf("Cannot figure out the \"len\" of type[%T].", lenObject))
}
}
return Repeat(text, repeatTimes)
}
func RepeatAndJoin(text TextGetter, separator TextGetter, times int) TextGetter {
return JoinTextList(separator, Repeat(text, times))
}
func RepeatAndJoinByLen(text TextGetter, separator TextGetter, lenObject interface{}) TextGetter {
return JoinTextList(separator, RepeatByLen(text, lenObject))
}
type formatterImpl struct {
formatter string
args []interface{}
}
func (f *formatterImpl) String() string {
return fmt.Sprintf(f.formatter, f.args...)
}
func (f *formatterImpl) Post() PostProcessor {
return NewPost(f)
}