mirror of
https://github.com/ManyakRus/crud_generator.git
synced 2024-12-22 00:36:41 +02:00
226 lines
5.7 KiB
Go
226 lines
5.7 KiB
Go
|
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()
|
||
|
}
|