1
0
mirror of https://github.com/ManyakRus/crud_generator.git synced 2024-12-22 00:36:41 +02:00
crud_generator/vendor/github.com/ompluscator/dynamic-struct/builder.go
Nikitin Aleksandr a6ea64ee3e новый
2023-10-24 18:03:04 +03:00

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()
}