-
Notifications
You must be signed in to change notification settings - Fork 81
/
builder.go
225 lines (196 loc) · 5.66 KB
/
builder.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
package dynamicstruct
import "reflect"
type (
// Builder holds all fields' definitions for desired structs.
// It gives opportunity to add or remove fields, or to edit
// existing ones. In the end, it provides definition for
// dynamic structs, used to create new instances.
Builder interface {
// AddField creates new struct's field.
// It expects field's name, type and string.
// Type is provided as an instance of some golang type.
// Tag is provided as classical golang field tag.
//
// builder.AddField("SomeFloatField", 0.0, `json:"boolean" validate:"gte=10"`)
//
AddField(name string, typ interface{}, tag string) Builder
// RemoveField removes existing struct's field.
//
// builder.RemoveField("SomeFloatField")
//
RemoveField(name string) Builder
// HasField checks if struct has a field with a given name.
//
// if builder.HasField("SomeFloatField") { ...
//
HasField(name string) bool
// GetField returns struct's field definition.
// If there is no such field, it returns nil.
// Usable to edit existing struct's field.
//
// field := builder.GetField("SomeFloatField")
//
GetField(name string) FieldConfig
// Build returns definition for dynamic struct.
// Definition can be used to create new instances.
//
// dStruct := builder.Build()
//
Build() DynamicStruct
}
// FieldConfig holds single field's definition.
// It provides possibility to edit field's type and tag.
FieldConfig interface {
// SetType changes field's type.
// Expected value is an instance of golang type.
//
// field.SetType([]int{})
//
SetType(typ interface{}) FieldConfig
// SetTag changes fields's tag.
// Expected value is an string which represents classical
// golang tag.
//
// field.SetTag(`json:"slice"`)
//
SetTag(tag string) FieldConfig
}
// DynamicStruct contains defined dynamic struct.
// This definition can't be changed anymore, once is built.
// It provides a method for creating new instances of same defintion.
DynamicStruct interface {
// New provides new instance of defined dynamic struct.
//
// value := dStruct.New()
//
New() interface{}
// NewSliceOfStructs provides new slice of defined dynamic struct, with 0 length and capacity.
//
// value := dStruct.NewSliceOfStructs()
//
NewSliceOfStructs() interface{}
// New provides new map of defined dynamic struct with desired key type.
//
// value := dStruct.NewMapOfStructs("")
//
NewMapOfStructs(key interface{}) interface{}
}
builderImpl struct {
fields []*fieldConfigImpl
}
fieldConfigImpl struct {
name string
pkg string
typ interface{}
tag string
anonymous bool
}
dynamicStructImpl struct {
definition reflect.Type
}
)
// NewStruct returns new clean instance of Builder interface
// for defining fresh dynamic struct.
//
// builder := dynamicstruct.NewStruct()
//
func NewStruct() Builder {
return &builderImpl{
fields: []*fieldConfigImpl{},
}
}
// ExtendStruct extends existing instance of struct and
// returns new instance of Builder interface.
//
// builder := dynamicstruct.MergeStructs(MyStruct{})
//
func ExtendStruct(value interface{}) Builder {
return MergeStructs(value)
}
// MergeStructs merges a list of existing instances of structs and
// returns new instance of Builder interface.
//
// builder := dynamicstruct.MergeStructs(MyStructOne{}, MyStructTwo{}, MyStructThree{})
//
func MergeStructs(values ...interface{}) Builder {
builder := NewStruct()
for _, value := range values {
valueOf := reflect.Indirect(reflect.ValueOf(value))
typeOf := valueOf.Type()
for i := 0; i < valueOf.NumField(); i++ {
fval := valueOf.Field(i)
ftyp := typeOf.Field(i)
builder.(*builderImpl).addField(ftyp.Name, ftyp.PkgPath, fval.Interface(), string(ftyp.Tag), ftyp.Anonymous)
}
}
return builder
}
func (b *builderImpl) AddField(name string, typ interface{}, tag string) Builder {
return b.addField(name, "", typ, tag, false)
}
func (b *builderImpl) addField(name string, pkg string, typ interface{}, tag string, anonymous bool) Builder {
b.fields = append(b.fields, &fieldConfigImpl{
name: name,
typ: typ,
tag: tag,
anonymous: anonymous,
})
return b
}
func (b *builderImpl) RemoveField(name string) Builder {
for i := range b.fields {
if b.fields[i].name == name {
b.fields = append(b.fields[:i], b.fields[i+1:]...)
break
}
}
return b
}
func (b *builderImpl) HasField(name string) bool {
for i := range b.fields {
if b.fields[i].name == name {
return true
}
}
return false
}
func (b *builderImpl) GetField(name string) FieldConfig {
for i := range b.fields {
if b.fields[i].name == name {
return b.fields[i]
}
}
return nil
}
func (b *builderImpl) Build() DynamicStruct {
var structFields []reflect.StructField
for _, field := range b.fields {
structFields = append(structFields, reflect.StructField{
Name: field.name,
PkgPath: field.pkg,
Type: reflect.TypeOf(field.typ),
Tag: reflect.StructTag(field.tag),
Anonymous: field.anonymous,
})
}
return &dynamicStructImpl{
definition: reflect.StructOf(structFields),
}
}
func (f *fieldConfigImpl) SetType(typ interface{}) FieldConfig {
f.typ = typ
return f
}
func (f *fieldConfigImpl) SetTag(tag string) FieldConfig {
f.tag = tag
return f
}
func (ds *dynamicStructImpl) New() interface{} {
return reflect.New(ds.definition).Interface()
}
func (ds *dynamicStructImpl) NewSliceOfStructs() interface{} {
return reflect.New(reflect.SliceOf(ds.definition)).Interface()
}
func (ds *dynamicStructImpl) NewMapOfStructs(key interface{}) interface{} {
return reflect.New(reflect.MapOf(reflect.Indirect(reflect.ValueOf(key)).Type(), ds.definition)).Interface()
}