From 4234f96f2118fdb49506c6a37149b8fdd93c9e7d Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sat, 21 Oct 2023 01:06:42 +0200 Subject: [PATCH 01/31] Allow for easy creating of custom tasks to be created allowing fields to have custom functions as generators --- README.md | 2 + dreflect/dreflect.go | 2 +- field.go | 27 ++++++++- generator/basic.functions.go | 3 +- generator/functions.go | 1 + generator/generated.field.go | 14 +++-- generator/generator.go | 2 + generator/task.go | 109 +++++++++++++++++++---------------- go.mod | 9 ++- modifier.go | 4 +- 10 files changed, 114 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 52bb67d..eedd706 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,8 @@ func main() { AddField("Person", Person{Name: "Martin", Age: 25}, `json:"person"`). AddField("Job", "Software Developer", "") + + strct := structBuilder.Build().Instance() generatedStruct := dstruct.NewGeneratedStruct(strct) diff --git a/dreflect/dreflect.go b/dreflect/dreflect.go index 8d473be..42b4335 100644 --- a/dreflect/dreflect.go +++ b/dreflect/dreflect.go @@ -112,7 +112,7 @@ func convert(src reflect.Value, dst reflect.Value) reflect.Value { } retPointer := reflect.New(dst.Type()) - //enure that new pointer uses same memory address as src pointer + //ensure that new pointer uses same memory address as src pointer reflect.NewAt(src.Type(), unsafe.Pointer(retPointer.Elem().UnsafeAddr())).Elem(). Set(src) diff --git a/field.go b/field.go index 3dd6544..b9ba4b1 100644 --- a/field.go +++ b/field.go @@ -1,6 +1,10 @@ package dstruct -import "reflect" +import ( + "fmt" + "reflect" + "strconv" +) type structField struct { name string @@ -35,3 +39,24 @@ func (f structField) GetFieldFQName() string { func (f structField) GetTag(t string) string { return f.tag.Get(t) } + +func (f structField) GetJsonName() string { + return f.jsonName +} + +func (f structField) GetEnumValues() (enumValues map[string]int) { + enum, ok := f.tag.Lookup("enum") + if ok { + numEnums, err := strconv.Atoi(enum) + if err != nil { + return + } + enumValues = make(map[string]int) + for i := 1; i <= numEnums; i++ { + if key := f.tag.Get(fmt.Sprintf("enum_%d", i)); key != "" { + enumValues[key] = i + } + } + } + return +} diff --git a/generator/basic.functions.go b/generator/basic.functions.go index 346d925..521b561 100644 --- a/generator/basic.functions.go +++ b/generator/basic.functions.go @@ -11,7 +11,8 @@ import ( type basicGenerationFunction struct { _func func(...any) any - args []any + // TODO consider making args exportable for more customizable generation functions + args []any } const ISO8601 string = "2018-03-20T09:12:28Z" diff --git a/generator/functions.go b/generator/functions.go index d995991..ecc0bea 100644 --- a/generator/functions.go +++ b/generator/functions.go @@ -55,6 +55,7 @@ func GenerateNumberFunc[n number](min, max n) GenerationFunction { } f.args = []any{&min, &max} + return f } diff --git a/generator/generated.field.go b/generator/generated.field.go index 8da71f7..b99fbb9 100644 --- a/generator/generated.field.go +++ b/generator/generated.field.go @@ -6,8 +6,6 @@ import ( "strconv" ) -type ParentType map[string]int - type GeneratedField struct { Name string Value reflect.Value @@ -155,9 +153,17 @@ func (field *GeneratedField) getGenerationFunction() GenerationFunction { return GenerateFixedValueFunc(tags.Get(fmt.Sprintf("enum_%d", generateNum(0, numEnums-1)+1))) } - gen_task, ok := tags.Lookup("gen_task") + _, ok = tags.Lookup("gen_task") if ok { - return getTask(gen_task, field.Name).getFunction() + taskProperties, err := CreateTaskProperties(field.Name, tags) + if err != nil { + panic(fmt.Sprintf("Error decoding gen_task: %s", err.Error())) + } + task := GetTask(taskProperties.TaskName) + if task == nil { + panic(fmt.Sprintf("Unregistered task %s", taskProperties.TaskName)) + } + return task.GenerationFunction(*taskProperties) } return field.Generator.DefaultGenerationFunctions[kind] } diff --git a/generator/generator.go b/generator/generator.go index b3df972..3e4e9ca 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -6,6 +6,8 @@ import ( type GenerationFunction interface { Generate() any + // Copy copies the generation config and returns a copy of it with the same config + // as the origin generation config Copy(*GenerationConfig) GenerationFunction GetGenerationConfig() *GenerationConfig SetGenerationConfig(*GenerationConfig) GenerationFunction diff --git a/generator/task.go b/generator/task.go index dc1d86e..e4ce5c0 100644 --- a/generator/task.go +++ b/generator/task.go @@ -2,83 +2,92 @@ package generator import ( "fmt" + "reflect" "strconv" "strings" ) type TaskName string -const ( - GenInt32 TaskName = "GenInt32" -) +type Task interface { + GenerationFunction(taskProperties TaskProperties) GenerationFunction + ExpectedParameterCount() int + Name() string +} + +var tasks map[string]Task -type Task struct { - Name TaskName - Parameters string +func init() { + tasks = make(map[string]Task) +} + +type TaskProperties struct { + TaskName string + Parameters []string FieldName string } -type GenInt32Params struct { - min int32 - max int32 +func GetTask(task string) Task { + return tasks[task] } -func (t *Task) GenInt32Params() GenInt32Params { - params := strings.Split(t.Parameters, ",") +func AddTask(task Task) error { + name := task.Name() + if tasks[name] != nil { + return fmt.Errorf("Task with name %s already exists", name) + } + tasks[name] = task + return nil +} - if len(params) != 2 { - panic(fmt.Sprintf("error with field %s: task %s: task requires 2 parameters but has %d", t.FieldName, t.Name, len(params))) +func getParameters(parameterCount int, tags reflect.StructTag) (parameters []string) { + for i := 1; i <= parameterCount; i++ { + parameters = append(parameters, tags.Get(fmt.Sprintf("gen_task_%d", i))) } - param_1, err := strconv.Atoi(params[0]) - if err != nil { - panic(fmt.Sprintf("error with field %s: task %s error: %s", t.FieldName, t.Name, err)) + return parameters +} + +func CreateTaskProperties(fieldName string, tags reflect.StructTag) (*TaskProperties, error) { + gen_task_tag := strings.TrimSpace(tags.Get("gen_task")) + leftBraceIndex := strings.Index(gen_task_tag, "(") + if leftBraceIndex == -1 { + return nil, fmt.Errorf("error with field %s: task %s error: no ( found", fieldName, gen_task_tag) } - param_2, err := strconv.Atoi(params[1]) + if gen_task_tag[len(gen_task_tag)-1:] != ")" { + return nil, fmt.Errorf("error with field %s: task %s error: last character of task must be )", fieldName, gen_task_tag) + } + taskName := gen_task_tag[:leftBraceIndex] + parameterCount, err := strconv.Atoi(gen_task_tag[leftBraceIndex+1 : len(gen_task_tag)-1]) if err != nil { - panic(fmt.Sprintf("error with field %s: task %s error: %s", t.FieldName, t.Name, err)) + return nil, fmt.Errorf("error getting task parameter count: %w", err) } - if param_1 > param_2 { - err = fmt.Errorf("min must be less or equal to the max value min = %d max = %d", param_1, param_2) - panic(fmt.Sprintf("error with field %s: task %s error: %s", t.FieldName, t.Name, err)) + return &TaskProperties{ + TaskName: taskName, + Parameters: getParameters(parameterCount, tags), + FieldName: fieldName, + }, nil +} - } +func GetTagForTask(name TaskName, params ...any) reflect.StructTag { - return GenInt32Params{ - min: int32(param_1), - max: int32(param_2), + if tasks[string(name)] == nil { + panic(fmt.Sprintf("Task '%s' is not registered", name)) } -} -func getTask(task string, fieldName string) Task { - task = strings.TrimSpace(task) - leftBraceIndex := strings.Index(task, "(") - if leftBraceIndex == -1 { - panic(fmt.Sprintf("error with field %s: task %s error: no ( found", fieldName, task)) + tags := fmt.Sprintf(`gen_task:"%s(%d)"`, name, len(params)) + for i, p := range params { + tags += fmt.Sprintf(` gen_task_%d:"%v"`, (i + 1), p) } - if task[len(task)-1:] != ")" { - panic(fmt.Sprintf("error with field %s: task %s error: last character of task must be )", fieldName, task)) - } - taskName := task[:leftBraceIndex] - parameters := task[leftBraceIndex+1 : len(task)-1] - return Task{ - Name: TaskName(taskName), - Parameters: parameters, - FieldName: fieldName, - } + return reflect.StructTag(tags) + } -func (t Task) getFunction() GenerationFunction { - switch t.Name { - case GenInt32: - params := t.GenInt32Params() - return GenerateNumberFunc(params.min, params.max) +func validateParamCount(task Task, taskProperties TaskProperties) { + if len(taskProperties.Parameters) != task.ExpectedParameterCount() { + panic(fmt.Sprintf("error with field %s. task '%s': task requires %d parameters but has %d", taskProperties.FieldName, task.Name(), task.ExpectedParameterCount(), len(taskProperties.Parameters))) } - panic(fmt.Sprintf("Invalid task name '%s' for field %s ", t.Name, t.FieldName)) -} -func GetTagsForGenInt32Task(min int32, max int32) string { - return fmt.Sprintf(`gen_task:"%s(%d,%d)"`, GenInt32, min, max) } diff --git a/go.mod b/go.mod index a85ca65..71a4660 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,13 @@ module github.com/MartinSimango/dstruct -go 1.20 +go 1.21.3 + + +retract v1.1.1 +retract v1.1.0 +retract v1.0.0 +retract v0.1.2 +retract v0.1.1 require ( github.com/stretchr/testify v1.8.1 diff --git a/modifier.go b/modifier.go index a82c0b8..3f97fe8 100644 --- a/modifier.go +++ b/modifier.go @@ -31,7 +31,9 @@ type DynamicStructModifier interface { // GetFields returns a map containing all fields within a struct GetFields() FieldData - // Update updates the struct's underlying tree to represent that of the strct's value + // Update updates the struct's underlying tree to represent that of the strct's value. + // The structs underlying tree can change if new fields are added due to fields within the struct changing from + // nil to become not nil. This can lead to new additional fields being introduced within the struct Update() // Apply is a combination of Set and Update. Update is not called if Apply fails. From f7bfa50702f6586d452a9f5408ac1978a775271b Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sat, 21 Oct 2023 01:12:30 +0200 Subject: [PATCH 02/31] Export validateParamCount function --- generator/task.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/task.go b/generator/task.go index e4ce5c0..3bfd392 100644 --- a/generator/task.go +++ b/generator/task.go @@ -85,7 +85,7 @@ func GetTagForTask(name TaskName, params ...any) reflect.StructTag { } -func validateParamCount(task Task, taskProperties TaskProperties) { +func ValidateParamCount(task Task, taskProperties TaskProperties) { if len(taskProperties.Parameters) != task.ExpectedParameterCount() { panic(fmt.Sprintf("error with field %s. task '%s': task requires %d parameters but has %d", taskProperties.FieldName, task.Name(), task.ExpectedParameterCount(), len(taskProperties.Parameters))) } From 899c701300f2a5799c648b8df2a75e57d8f2ec08 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sun, 22 Oct 2023 17:30:40 +0200 Subject: [PATCH 03/31] Update go.mod file --- go.mod | 14 +++++++------- go.sum | 2 -- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 71a4660..3689aa6 100644 --- a/go.mod +++ b/go.mod @@ -2,12 +2,13 @@ module github.com/MartinSimango/dstruct go 1.21.3 - -retract v1.1.1 -retract v1.1.0 -retract v1.0.0 -retract v0.1.2 -retract v0.1.1 +retract ( + v1.1.1 + v1.1.0 + v1.0.0 + v0.1.2 + v0.1.1 +) require ( github.com/stretchr/testify v1.8.1 @@ -16,7 +17,6 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index da2056e..2a72546 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From 80215eaef4ed14922073b04e8f0022fe6f83f701 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sun, 22 Oct 2023 17:58:55 +0200 Subject: [PATCH 04/31] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index eedd706..4073e40 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ extend or merge structs which have struct fields of type `any` their value must ## Sections +* [Install](https://github.com/MartinSimango/dstruct#install) * [How it works?](https://github.com/MartinSimango/dstruct#how-it-works) * [Using the builder](https://github.com/MartinSimango/dstruct#using-the-builder) * [Using the modifier](https://github.com/MartinSimango/dstruct#using-the-modifier) @@ -30,7 +31,11 @@ extend or merge structs which have struct fields of type `any` their value must +## Install +```sh +go get github.com/MartinSimango/dstruct +``` ## How it works? From 93d4560dca06f6a79298e65885d4cb94fec57927 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Fri, 27 Oct 2023 22:15:38 +0200 Subject: [PATCH 05/31] Update README.md --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4073e40..9718ab7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +> :warning: **Notice** +> * Please do not install any of these versions: v1.1.1 v1.1.0 v1.0.0 v0.1.2 v0.1.1 as these were removed from the repo - (but are still available at pkg.go.dev). +> * When installing please explicitly install the actual latest version of dstruct which is currently v0.3.0-beta. + + # dstruct A golang package that allows one to create, modify and generate structs dynamically. @@ -14,7 +19,7 @@ Features: Limitations: * You cannot extend structs with unexported embedded fields. -* If a struct pointer cannot be fully dereferenced then the struct's subfields won't be added to the dynamic struct. This is done mainly to avoid self referencing structs as these will create infinite node trees. +* If a struct pointer cannot be fully dereferenced then the struct's subfields won't be added to the dynamic struct. This is done mainly to avoid self-referencing structs as these will create infinite node trees. * Dynamic structs with struct fields of type `any (interface {})` cannot be created. If you try extend or merge structs which have struct fields of type `any` their value must be set to a concrete type. @@ -34,7 +39,7 @@ extend or merge structs which have struct fields of type `any` their value must ## Install ```sh -go get github.com/MartinSimango/dstruct +go get github.com/MartinSimango/dstruct@v0.3.0-beta ``` ## How it works? From 48c59045f8d6b01518b21b6c4e7843c39498fba5 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Fri, 1 Dec 2023 10:49:12 +0200 Subject: [PATCH 06/31] featredesign generation logic --- generated.struct.go | 128 ++++-- generator/basic.functions.go | 159 -------- generator/config.go | 330 ++++------------ generator/config/config.builder.go | 35 ++ generator/config/config.go | 84 ++++ generator/config/number.config.go | 157 ++++++++ generator/config/slice.config.go | 44 +++ generator/core/array.go | 1 + generator/core/bool.go | 15 + generator/core/core.generation.function.go | 16 + generator/core/date.go | 27 ++ .../core/default.generation.functions.go | 42 ++ generator/core/fixed.function.holder.go | 41 ++ generator/core/fixed.go | 13 + generator/core/function.holder.go | 51 +++ generator/core/generated.field.go | 198 ++++++++++ generator/core/generation.unit.go | 49 +++ generator/core/nil.go | 14 + generator/core/no.arg.function.holder.go | 26 ++ generator/core/number.function.holder.go | 39 ++ generator/core/number.go | 44 +++ generator/core/pointer.go | 34 ++ generator/core/slice.function.holder.go | 40 ++ generator/core/slice.go | 46 +++ generator/core/string.go | 20 + generator/core/struct.go | 18 + generator/functions.go | 168 ++------ generator/generated.field.go | 370 ++++++++++-------- generator/generation.function.go | 10 + generator/generation.unit.go | 43 -- generator/generator.go | 106 +++-- modifier.go | 2 +- util/number.go | 6 + 33 files changed, 1541 insertions(+), 835 deletions(-) delete mode 100644 generator/basic.functions.go create mode 100644 generator/config/config.builder.go create mode 100644 generator/config/config.go create mode 100644 generator/config/number.config.go create mode 100644 generator/config/slice.config.go create mode 100644 generator/core/array.go create mode 100644 generator/core/bool.go create mode 100644 generator/core/core.generation.function.go create mode 100644 generator/core/date.go create mode 100644 generator/core/default.generation.functions.go create mode 100644 generator/core/fixed.function.holder.go create mode 100644 generator/core/fixed.go create mode 100644 generator/core/function.holder.go create mode 100644 generator/core/generated.field.go create mode 100644 generator/core/generation.unit.go create mode 100644 generator/core/nil.go create mode 100644 generator/core/no.arg.function.holder.go create mode 100644 generator/core/number.function.holder.go create mode 100644 generator/core/number.go create mode 100644 generator/core/pointer.go create mode 100644 generator/core/slice.function.holder.go create mode 100644 generator/core/slice.go create mode 100644 generator/core/string.go create mode 100644 generator/core/struct.go create mode 100644 generator/generation.function.go delete mode 100644 generator/generation.unit.go create mode 100644 util/number.go diff --git a/generated.struct.go b/generated.struct.go index 749ce93..dd78d18 100644 --- a/generated.struct.go +++ b/generated.struct.go @@ -2,11 +2,13 @@ package dstruct import ( "fmt" + "reflect" - "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" + "github.com/MartinSimango/dstruct/generator/core" ) -type GenerationFields map[string]*generator.GenerationUnit +type GenerationFields map[string]*core.GenerationUnit type GeneratedStruct interface { DynamicStructModifier @@ -17,8 +19,12 @@ type GeneratedStruct interface { // new generated fields to be accessed and modified by Set and Get methods. GenerateAndUpdate() - // GetFieldGenerationConfig gets the generation config for field within the struct. - GetFieldGenerationConfig(field string) *generator.GenerationConfig + // GetFieldValueGenerationConfig gets the generation config for field within the struct. + GetFieldValueGenerationConfig(field string) config.GenerationValueConfig + + GetFieldGenerationConfig(field string) config.Config + + GetValueGenerationConfig() config.GenerationValueConfig // SetFieldGenerationConfig sets the generation config for field within the struct. It returns // an error if the field does not exist or if the field cannot be generated. @@ -28,28 +34,37 @@ type GeneratedStruct interface { // Fields types that cannot be generated: structs, func, chan, any (will default to a nil value being generated). // // Note: Pointers to structs can be generated. - SetFieldGenerationConfig(field string, generationConfig *generator.GenerationConfig) error + SetFieldGenerationConfig(field string, generationConfig config.Config) error + + SetFieldGenerationValueConfig(field string, config config.GenerationValueConfig) + + SetGenerationConfig(config config.Config) + + SetGenerationValueConfig(config config.GenerationValueConfig) } type GeneratedStructImpl struct { *DynamicStructModifierImpl - generatedFields GenerationFields - generator *generator.Generator + generatedFields GenerationFields + generatedFieldConfig core.GeneratedFieldConfig + config config.Config } var _ GeneratedStruct = &GeneratedStructImpl{} func NewGeneratedStruct(val any) *GeneratedStructImpl { - return NewGeneratedStructWithConfig(val, generator.NewGenerator(generator.NewGenerationConfig())) + return NewGeneratedStructWithConfig(val, config.NewConfig()) } func NewGeneratedStructWithConfig(val any, - gen *generator.Generator) *GeneratedStructImpl { + cfg config.Config) *GeneratedStructImpl { generatedStruct := &GeneratedStructImpl{ DynamicStructModifierImpl: ExtendStruct(val).Build().(*DynamicStructModifierImpl), generatedFields: make(GenerationFields), - generator: gen, + config: cfg, + generatedFieldConfig: core.NewGenerateFieldConfig(cfg, config.DefaultGenerationValueConfig()), } + generatedStruct.populateGeneratedFields() return generatedStruct } @@ -60,13 +75,19 @@ func (gs *GeneratedStructImpl) populateGeneratedFields() { if field.HasChildren() { continue } + fieldKind := field.data.value.Kind() - gs.generatedFields[name] = generator.NewGenerationUnit( - generator.NewGeneratedField(field.data.fqn, + gs.generatedFields[name] = core.NewGenerationUnit( + core.NewGeneratedField(field.data.fqn, field.data.value, field.data.tag, - gs.generator.Copy(), + gs.generatedFieldConfig.Copy(fieldKind), )) + if fieldKind == reflect.Slice { + f := gs.generatedFields[name] + f.GenerationFunctions[fieldKind] = + core.NewSliceFunctionHolder(core.GenerateSliceFunc, f.GeneratedField, gs.config) + } } } @@ -79,38 +100,85 @@ func (gs *GeneratedStructImpl) GenerateAndUpdate() { gs.Update() } -func (gs *GeneratedStructImpl) SetFieldGenerationConfig(field string, generationConfig *generator.GenerationConfig) error { +func (gs *GeneratedStructImpl) changeChildrenConfig(node *Node[structField], cfg config.Config) { + for k, v := range node.children { + if v.HasChildren() { + gs.changeChildrenConfig(v, cfg) + continue + } + gs.generatedFields[k].GenerationFunctions[v.data.typ.Kind()].SetConfig(cfg) + } + +} + +func (gs *GeneratedStructImpl) SetFieldGenerationConfig(field string, cfg config.Config) error { if gs.fieldNodeMap[field] == nil { return fmt.Errorf("field %s does not exist within the struct", field) } + // TODO TEST for structs + if gs.fieldNodeMap[field].HasChildren() { + gs.changeChildrenConfig(gs.fieldNodeMap[field], cfg) + } + if gs.generatedFields[field] == nil { return fmt.Errorf("cannot set config for field %s", field) } - gs.generatedFields[field].Field.Generator.GenerationConfig = generationConfig - return nil -} + kind := gs.generatedFields[field].Value.Kind() -func (gs *GeneratedStructImpl) GetFieldGenerationConfig(field string) *generator.GenerationConfig { - return gs.generatedFields[field].Field.Generator.GenerationConfig + gs.generatedFields[field].GeneratedField.GenerationFunctions[kind].SetConfig(cfg) + return nil } -func (gs *GeneratedStructImpl) GetFieldGenerator(field string) *generator.Generator { - return gs.generatedFields[field].Field.Generator -} -func (gs *GeneratedStructImpl) SetFieldDefaultGenerationFunction(field string, - generationFunction generator.GenerationFunction) { +func (gs *GeneratedStructImpl) SetFieldGenerationFunction(field string, + functionHolder core.FunctionHolder) { kind := gs.fieldNodeMap[field].data.GetType().Kind() - generator := gs.generatedFields[field].Field.Generator - generator.DefaultGenerationFunctions[kind] = generationFunction - generationFunction.SetGenerationConfig(generator.GenerationConfig) + generator := gs.generatedFields[field].GeneratedFieldConfig + generator.GenerationFunctions[kind] = functionHolder gs.generatedFields[field].UpdateCurrentFunction = true } -func (gs *GeneratedStructImpl) SetFieldGenerator(field string, - generator *generator.Generator) { - gs.generatedFields[field].Field.Generator = generator +func (gs *GeneratedStructImpl) SetFieldGenerationFunctions(field string, + defaultGenerationFunctions core.DefaultGenerationFunctions) { + gs.generatedFields[field].GenerationFunctions = defaultGenerationFunctions gs.generatedFields[field].UpdateCurrentFunction = true +} + +func (gs *GeneratedStructImpl) SetGenerationConfig(config config.Config) { + for name, field := range gs.fieldNodeMap { + if field.HasChildren() { + continue + } + gs.generatedFields[name].SetConfig(config.Copy()) + } +} + +func (gs *GeneratedStructImpl) SetFieldGenerationValueConfig(field string, config config.GenerationValueConfig) { + gs.generatedFields[field].GenerationValueConfig = config +} + +func (gs *GeneratedStructImpl) SetGenerationValueConfig(config config.GenerationValueConfig) { + for name, field := range gs.fieldNodeMap { + if field.HasChildren() { + continue + } + gs.generatedFields[name].GenerationValueConfig = config + } +} + +// GetFieldValueGenerationConfig implements GeneratedStruct. +func (gs *GeneratedStructImpl) GetFieldValueGenerationConfig(field string) config.GenerationValueConfig { + return gs.generatedFields[field].GenerationValueConfig +} + +// GetFieldValueGenerationConfig implements GeneratedStruct. +func (gs *GeneratedStructImpl) GetFieldGenerationConfig(field string) config.Config { + k := gs.generatedFields[field].GeneratedField.Value.Kind() + return gs.generatedFields[field].GeneratedField.GenerationFunctions[k].GetConfig() +} +// GetValueGenerationConfig implements GeneratedStruct. +func (gs *GeneratedStructImpl) GetValueGenerationConfig() config.GenerationValueConfig { + return gs.generatedFieldConfig.GenerationValueConfig } func (gs *GeneratedStructImpl) generateFields() { diff --git a/generator/basic.functions.go b/generator/basic.functions.go deleted file mode 100644 index 521b561..0000000 --- a/generator/basic.functions.go +++ /dev/null @@ -1,159 +0,0 @@ -package generator - -import ( - "fmt" - "math/rand" - "reflect" - "time" - - "github.com/takahiromiyamoto/go-xeger" -) - -type basicGenerationFunction struct { - _func func(...any) any - // TODO consider making args exportable for more customizable generation functions - args []any -} - -const ISO8601 string = "2018-03-20T09:12:28Z" - -type number interface { - int8 | int | int32 | int64 | float32 | float64 -} - -func generateNum[n number](min, max n) n { - return min + (n(rand.Float64() * float64(max+1-min))) -} - -func init() { - generateSlice = basicGenerationFunction{ - _func: func(parameters ...any) any { - - field := parameters[0].(*GeneratedField) - generationConfig := field.Generator.GenerationConfig - sliceType := reflect.TypeOf(field.Value.Interface()).Elem() - min := generationConfig.sliceMinLength - max := generationConfig.sliceMaxLength - - len := generateNum(min, max) - sliceOfElementType := reflect.SliceOf(sliceType) - slice := reflect.MakeSlice(sliceOfElementType, 0, 1024) - sliceElement := reflect.New(sliceType) - - for i := 0; i < len; i++ { - newField := &GeneratedField{ - Name: fmt.Sprintf("%s#%d", field.Name, i), - Value: reflect.ValueOf(sliceElement.Interface()).Elem(), - Tag: field.Tag, - Generator: field.Generator.Copy(), - Parent: field, - } - - newField.SetValue() - - slice = reflect.Append(slice, sliceElement.Elem()) - } - return slice.Interface() - - }, - } - - generatePointerValue = basicGenerationFunction{ - - _func: func(parameters ...any) any { - field := parameters[0].(*GeneratedField) - if !field.Generator.GenerationConfig.setNonRequiredFields { - return nil - } - - field.Value.Set(reflect.New(field.Value.Type().Elem())) - fieldPointerValue := *field - fieldPointerValue.Value = field.Value.Elem() - fieldPointerValue.PointerValue = &field.Value - fieldPointerValue.SetValue() - - if field.Value.Elem().CanSet() { - field.Value.Elem().Set(fieldPointerValue.Value) - } - - return field.Value.Interface() - - }, - } - - generateStruct = basicGenerationFunction{ - - _func: func(parameters ...any) any { - field := parameters[0].(*GeneratedField) - field.setStructValues() - return field.Value.Interface() - }, - } -} - -var ( - generateStringFromRegex basicGenerationFunction = basicGenerationFunction{ - _func: func(parameters ...any) any { - regex := parameters[0].(string) - x, err := xeger.NewXeger(regex) - if err != nil { - panic(err) - } - return x.Generate() - }, - } - - generateNumber basicGenerationFunction = basicGenerationFunction{ - _func: func(parameters ...any) any { - min := parameters[0] - max := parameters[1] - paramKind := reflect.ValueOf(min).Elem().Kind() - - switch paramKind { - case reflect.Int: - return generateNum(*min.(*int), *max.(*int)) - case reflect.Int32: - return generateNum(*min.(*int32), *max.(*int32)) - case reflect.Int64: - return generateNum(*min.(*int64), *max.(*int64)) - case reflect.Float32: - return generateNum(*min.(*float32), *max.(*float32)) - case reflect.Float64: - return generateNum(*min.(*float64), *max.(*float64)) - default: - panic(fmt.Sprintf("Invalid number type: %s", paramKind)) - - } - }, - } - - generateBool basicGenerationFunction = basicGenerationFunction{ - _func: func(parameters ...any) any { - return generateNum(0, 1) == 0 - }, - } - - generateObject basicGenerationFunction = basicGenerationFunction{ - _func: func(parameters ...any) any { - return nil - }, - } - - generateNilValue basicGenerationFunction = basicGenerationFunction{ - _func: func(parameters ...any) any { - return nil - }, - } - - generateStruct basicGenerationFunction - - generateSlice basicGenerationFunction - - generatePointerValue basicGenerationFunction - - generateDateTime basicGenerationFunction = basicGenerationFunction{ - _func: func(parameters ...any) any { - return time.Now().UTC().Format(time.RFC3339) - }, - } -) diff --git a/generator/config.go b/generator/config.go index 3429e57..155de22 100644 --- a/generator/config.go +++ b/generator/config.go @@ -1,253 +1,81 @@ package generator -import ( - "time" -) - -type ConfigType interface { - IntConfig | FloatConfig | SliceConfig -} - -type IntConfig struct { - int64Min int64 - int64Max int64 - int32Min int32 - int32Max int32 - intMin int - intMax int -} - -type FloatConfig struct { - float64Min float64 - float64Max float64 - float32Min float32 - float32Max float32 -} - -type SliceConfig struct { - sliceMinLength int - sliceMaxLength int -} - -type DateConfig struct { - dateFormat string - dateStart time.Time - dateEnd time.Time -} - -func defaultIntConfig() IntConfig { - return IntConfig{ - int64Min: 0, - int64Max: 100, - int32Min: 0, - int32Max: 100, - intMin: 0, - intMax: 100, - } -} - -func defaultFloatConfig() FloatConfig { - return FloatConfig{ - float64Min: 0, - float64Max: 100, - float32Min: 0, - float32Max: 100, - } -} - -func defaultSliceConfig() SliceConfig { - return SliceConfig{ - sliceMinLength: 0, - sliceMaxLength: 10, - } -} - -func defaultDateConfig() DateConfig { - return DateConfig{ - dateFormat: time.RFC3339, - } -} - -type ValueGenerationType uint8 - -const ( - Generate ValueGenerationType = iota // will generate all field - GenerateOnce // will generate all the fields - UseDefaults -) - -type recursiveDefinition struct { - Allow bool - Count uint -} - -type GenerationValueConfig struct { - valueGenerationType ValueGenerationType - setNonRequiredFields bool - recursiveDefinition recursiveDefinition -} - -type GenerationConfig struct { - GenerationValueConfig - SliceConfig - IntConfig - FloatConfig - DateConfig -} - -func NewGenerationConfig() (generationConfig *GenerationConfig) { - generationConfig = &GenerationConfig{ - GenerationValueConfig: GenerationValueConfig{ - valueGenerationType: UseDefaults, - setNonRequiredFields: false, - recursiveDefinition: recursiveDefinition{ - Allow: false, - Count: 1, - }, - }, - SliceConfig: defaultSliceConfig(), - IntConfig: defaultIntConfig(), - FloatConfig: defaultFloatConfig(), - DateConfig: defaultDateConfig(), - } - return -} - -func (gc *GenerationConfig) Clone() (config *GenerationConfig) { - config = &GenerationConfig{} - *config = *gc - return -} - -func (gc *GenerationConfig) AllowRecursion(a bool) *GenerationConfig { - gc.recursiveDefinition.Allow = a - return gc -} -func (gc *GenerationConfig) SetRecursionCount(r uint) *GenerationConfig { - gc.recursiveDefinition.Count = r - return gc -} - -func (gc *GenerationConfig) SetNonRequiredFields(val bool) *GenerationConfig { - gc.setNonRequiredFields = val - return gc -} - -func (gc *GenerationConfig) SetValueGenerationType(valueGenerationType ValueGenerationType) *GenerationConfig { - gc.valueGenerationType = valueGenerationType - return gc -} - -func (gc *GenerationConfig) SetIntRange(min, max int) *GenerationConfig { - if min > max { - return gc - } - gc.intMin, gc.intMax = min, max - return gc -} - -// deprecated: use SetIntRange -func (gc *GenerationConfig) SetIntMax(max int) *GenerationConfig { - if max >= gc.intMin { - gc.intMax = max - } - return gc -} - -func (gc *GenerationConfig) SetIntMin(min int) *GenerationConfig { - if min <= gc.intMax { - gc.intMin = min - } - return gc -} - -func (gc *GenerationConfig) SetInt64Max(max int64) *GenerationConfig { - if max >= gc.int64Min { - gc.int64Max = max - } - return gc -} - -func (gc *GenerationConfig) SetInt64Min(min int64) *GenerationConfig { - if min <= gc.int64Max { - gc.int64Min = min - } - return gc -} - -func (gc *GenerationConfig) SetInt32Max(max int32) *GenerationConfig { - if max >= gc.int32Min { - gc.int32Max = max - } - return gc -} - -func (gc *GenerationConfig) SetInt32Min(min int32) *GenerationConfig { - if min <= gc.int32Max { - gc.int32Min = min - } - return gc -} - -func (gc *GenerationConfig) SetFloat64Max(max float64) *GenerationConfig { - if max >= gc.float64Min { - gc.float64Max = max - } - return gc -} - -func (gc *GenerationConfig) SetFloat64Min(min float64) *GenerationConfig { - if min <= gc.float64Max { - gc.float64Min = min - } - return gc -} - -func (gc *GenerationConfig) SetFloat32Max(max float32) *GenerationConfig { - if max >= gc.float32Min { - gc.float32Max = max - } - return gc -} - -func (gc *GenerationConfig) SetFloat32Min(min float32) *GenerationConfig { - if min <= gc.float32Max { - gc.float32Min = min - } - return gc -} - -func (gc *GenerationConfig) SetSliceLengthMax(max int) *GenerationConfig { - if max >= gc.sliceMinLength { - gc.sliceMaxLength = max - } - return gc -} - -func (gc *GenerationConfig) SetSliceLengthMin(min int) *GenerationConfig { - if min <= gc.sliceMaxLength { - gc.sliceMinLength = min - } - return gc -} - -func (gc *GenerationConfig) SetDateFormat(format string) *GenerationConfig { - - // TODO validate format - gc.dateFormat = format - return gc -} - -func (gc *GenerationConfig) SetDateStart(start time.Time) *GenerationConfig { - if !start.After(gc.dateEnd) { - gc.dateStart = start - } - return gc -} - -func (gc *GenerationConfig) SetDateEnd(end time.Time) *GenerationConfig { - if end.After(gc.dateStart) { - gc.dateStart = end - } - return gc -} +// import ( +// "time" +// ) + +// type DateConfig struct { +// dateFormat string +// dateStart time.Time +// dateEnd time.Time +// } + +// func defaultDateConfig() DateConfig { +// return DateConfig{ +// dateFormat: time.RFC3339, +// } +// } + +// func NewGenerationConfig() (generationConfig *FunctionGenerationConfig) { +// generationConfig = &FunctionGenerationConfig{ +// GenerationValueConfig: GenerationValueConfig{ +// valueGenerationType: UseDefaults, +// setNonRequiredFields: false, +// recursiveDefinition: recursiveDefinition{ +// Allow: false, +// Count: 1, +// }, +// }, +// SliceConfig: defaultSliceConfig(), +// IntConfig: defaultIntConfig(), +// FloatConfig: defaultFloatConfig(), +// DateConfig: defaultDateConfig(), +// } +// return +// } + +// func (gc *FunctionGenerationConfig) Clone() (config *FunctionGenerationConfig) { +// config = &FunctionGenerationConfig{} +// *config = *gc +// return +// } + +// func (gc *FunctionGenerationConfig) AllowRecursion(a bool) *FunctionGenerationConfig { +// gc.recursiveDefinition.Allow = a +// return gc +// } +// func (gc *FunctionGenerationConfig) SetRecursionCount(r uint) *FunctionGenerationConfig { +// gc.recursiveDefinition.Count = r +// return gc +// } + +// func (gc *FunctionGenerationConfig) SetNonRequiredFields(val bool) *FunctionGenerationConfig { +// gc.setNonRequiredFields = val +// return gc +// } + +// func (gc *FunctionGenerationConfig) SetValueGenerationType(valueGenerationType ValueGenerationType) *FunctionGenerationConfig { +// gc.valueGenerationType = valueGenerationType +// return gc +// } + +// func (gc *FunctionGenerationConfig) SetDateFormat(format string) *FunctionGenerationConfig { + +// // TODO validate format +// gc.dateFormat = format +// return gc +// } + +// func (gc *FunctionGenerationConfig) SetDateStart(start time.Time) *FunctionGenerationConfig { +// if !start.After(gc.dateEnd) { +// gc.dateStart = start +// } +// return gc +// } + +// func (gc *FunctionGenerationConfig) SetDateEnd(end time.Time) *FunctionGenerationConfig { +// if end.After(gc.dateStart) { +// gc.dateStart = end +// } +// return gc +// } diff --git a/generator/config/config.builder.go b/generator/config/config.builder.go new file mode 100644 index 0000000..bd948bd --- /dev/null +++ b/generator/config/config.builder.go @@ -0,0 +1,35 @@ +package config + +type ConfigBuilder interface { + WithNumberConfig(NumberConfig) ConfigBuilder + WithSliceConfig(SliceConfig) ConfigBuilder + Build() *ConfigImpl +} + +type ConfigBuilderImpl struct { + config *ConfigImpl +} + +var _ ConfigBuilder = &ConfigBuilderImpl{} + +func NewConfigBuilder() *ConfigBuilderImpl { + return &ConfigBuilderImpl{ + config: &ConfigImpl{}, + } +} + +// WithSliceConfig implements ConfigBuilder. +func (cb *ConfigBuilderImpl) WithNumberConfig(nc NumberConfig) ConfigBuilder { + cb.config.NumberConfig = nc + return cb +} + +// WithSliceConfig implements ConfigBuilder. +func (cb *ConfigBuilderImpl) WithSliceConfig(sc SliceConfig) ConfigBuilder { + cb.config.SliceConfig = sc + return cb +} + +func (cb *ConfigBuilderImpl) Build() *ConfigImpl { + return cb.config +} diff --git a/generator/config/config.go b/generator/config/config.go new file mode 100644 index 0000000..997a139 --- /dev/null +++ b/generator/config/config.go @@ -0,0 +1,84 @@ +package config + +type ConfigType uint + +type ValueGenerationType uint8 + +const ( + Generate ValueGenerationType = iota // will generate all field + GenerateOnce // will generate the fields once + UseDefaults +) + +type RecursiveDefinition struct { + Allow bool + Depth uint +} + +type GenerationValueConfig struct { + ValueGenerationType ValueGenerationType + SetNonRequiredFields bool + RecursiveDefinition RecursiveDefinition +} + +func DefaultGenerationValueConfig() GenerationValueConfig { + return GenerationValueConfig{ + ValueGenerationType: UseDefaults, + SetNonRequiredFields: false, + RecursiveDefinition: RecursiveDefinition{ + Allow: false, + Depth: 1, + }, + } +} + +func NewConfig() *ConfigImpl { + return NewConfigBuilder(). + WithNumberConfig(NewNumberConfig()). + WithSliceConfig(NewSliceConfig()). + Build() +} + +type Config interface { + ConfigBuilder + Number() NumberConfig + Slice() SliceConfig + SetSliceLength(min, max int) Config + Copy() Config +} + +type ConfigImpl struct { + ConfigBuilderImpl + SliceConfig SliceConfig + NumberConfig NumberConfig +} + +var _ Config = &ConfigImpl{} + +func (c *ConfigImpl) Copy() Config { + newConfig := &ConfigImpl{} + if c.SliceConfig != nil { + newConfig.SliceConfig = c.SliceConfig.Copy() + } + + if c.NumberConfig != nil { + newConfig.NumberConfig = c.NumberConfig.Copy() + } + + return newConfig + +} + +func (c *ConfigImpl) Slice() SliceConfig { + return c.SliceConfig + +} + +func (c *ConfigImpl) Number() NumberConfig { + return c.NumberConfig +} + +func (c *ConfigImpl) SetSliceLength(min, max int) Config { + c.SliceConfig.SetLengthRange(min, max) + return c +} diff --git a/generator/config/number.config.go b/generator/config/number.config.go new file mode 100644 index 0000000..9546062 --- /dev/null +++ b/generator/config/number.config.go @@ -0,0 +1,157 @@ +package config + +import "github.com/MartinSimango/dstruct/util" + +type NumberConfig interface { + IntRange() (*int, *int) + SetIntRange(min int, max int) NumberConfig + Int8Range() (*int8, *int8) + SetInt8Range(min int8, max int8) NumberConfig + Int16Range() (*int16, *int16) + SetInt16Range(min int16, max int16) NumberConfig + Int32Range() (*int32, *int32) + SetInt32Range(min int32, max int32) NumberConfig + Int64Range() (*int64, *int64) + SetInt64Range(min int64, max int64) NumberConfig + Copy() NumberConfig +} +type Range[n util.Number] struct { + min n + max n +} + +type NumberConfigImpl struct { + intRange Range[int] + int8Range Range[int8] + int16Range Range[int16] + int32Range Range[int32] + int64Range Range[int64] +} + +var _ NumberConfig = &NumberConfigImpl{} + +// Int16Range implements NumberConfig. +func (nc *NumberConfigImpl) Int16Range() (*int16, *int16) { + return &nc.int16Range.min, &nc.int16Range.max +} + +// Int32Range implements NumberConfig. +func (nc *NumberConfigImpl) Int32Range() (*int32, *int32) { + return &nc.int32Range.min, &nc.int32Range.max +} + +// Int64Range implements NumberConfig. +func (nc *NumberConfigImpl) Int64Range() (*int64, *int64) { + return &nc.int64Range.min, &nc.int64Range.max + +} + +// Int8Range implements NumberConfig. +func (nc *NumberConfigImpl) Int8Range() (*int8, *int8) { + return &nc.int8Range.min, &nc.int8Range.max +} + +// SetInt16Range implements NumberConfig. +func (nc *NumberConfigImpl) SetInt16Range(min int16, max int16) NumberConfig { + setRange(min, max, &nc.int16Range.min, &nc.int16Range.max) + return nc +} + +// SetInt32Range implements NumberConfig. +func (nc *NumberConfigImpl) SetInt32Range(min int32, max int32) NumberConfig { + setRange(min, max, &nc.int32Range.min, &nc.int32Range.max) + return nc +} + +// SetInt64Range implements NumberConfig. +func (nc *NumberConfigImpl) SetInt64Range(min int64, max int64) NumberConfig { + setRange(min, max, &nc.int64Range.min, &nc.int64Range.max) + return nc +} + +// SetInt8Range implements NumberConfig. +func (nc *NumberConfigImpl) SetInt8Range(min int8, max int8) NumberConfig { + setRange(min, max, &nc.int8Range.min, &nc.int8Range.max) + return nc +} + +func (nc *NumberConfigImpl) IntRange() (*int, *int) { + return &nc.intRange.min, &nc.intRange.max + +} + +func (nc *NumberConfigImpl) SetIntRange(min, max int) NumberConfig { + setRange(min, max, &nc.intRange.min, &nc.intRange.max) + return nc +} + +// Copy implements NumberConfig. +func (nc *NumberConfigImpl) Copy() NumberConfig { + newNumberConfig := &NumberConfigImpl{} + *newNumberConfig = *nc + return newNumberConfig +} + +func setRange[n util.Number](min, max n, ncMin, ncMax *n) { + if min > max { + return + } + *ncMin, *ncMax = min, max +} + +func NewNumberConfig() *NumberConfigImpl { + return &NumberConfigImpl{ + intRange: Range[int]{0, 10}, + int8Range: Range[int8]{0, 10}, + int16Range: Range[int16]{0, 10}, + int32Range: Range[int32]{0, 10}, + int64Range: Range[int64]{0, 10}, + } +} + +// IntRange() (*n, *n) +// SetIntRange(min n, max n) + +// type NumberConfig[n util.Number] interface { +// // TODO pointer values should only be accesible internally move files to same package +// MinRange() *n +// MaxRange() *n +// SetRange(min n, max n) +// Copy() NumberConfig[n] +// } + +// type NumberConfigImpl[n util.Number] struct { +// minRange n +// maxRange n +// } + +// var _ NumberConfig[int] = &NumberConfigImpl[int]{} + +// func NewNumberConfig[n util.Number]() (cfg *NumberConfigImpl[n]) { +// return &NumberConfigImpl[n]{ +// minRange: 0, +// maxRange: 10, +// } +// } + +// func NewNumberConfigWithRange[n util.Number](min, max n) (cfg *NumberConfigImpl[n]) { +// return &NumberConfigImpl[n]{ +// minRange: min, +// maxRange: max, +// } +// } + +// func (nc *NumberConfigImpl[n]) MinRange() *n { +// return &nc.minRange + +// } + +// func (nc *NumberConfigImpl[n]) MaxRange() *n { +// return &nc.maxRange +// } + +// func (nc *NumberConfigImpl[n]) Copy() NumberConfig[n] { +// newNumberConfig := &NumberConfigImpl[n]{} +// *newNumberConfig = *nc +// return newNumberConfig +// } diff --git a/generator/config/slice.config.go b/generator/config/slice.config.go new file mode 100644 index 0000000..6ddeee9 --- /dev/null +++ b/generator/config/slice.config.go @@ -0,0 +1,44 @@ +package config + +type SliceConfig interface { + SetLengthRange(min, max int) SliceConfig + MinLength() int + MaxLength() int + Copy() SliceConfig +} + +type SliceConfigImpl struct { + minLength int + maxLength int +} + +var _ SliceConfig = &SliceConfigImpl{} + +func (sc *SliceConfigImpl) SetLengthRange(min, max int) SliceConfig { + if min > max { + return sc + } + sc.minLength, sc.maxLength = min, max + return sc + +} + +// MinLength implements SliceConfig. +func (sc *SliceConfigImpl) MinLength() int { + return sc.minLength + +} + +// MaxLength implements SliceConfig. +func (sc *SliceConfigImpl) MaxLength() int { + return sc.maxLength +} + +func (sc *SliceConfigImpl) Copy() SliceConfig { + s := *sc + return &s +} + +func NewSliceConfig() *SliceConfigImpl { + return &SliceConfigImpl{0, 10} +} diff --git a/generator/core/array.go b/generator/core/array.go new file mode 100644 index 0000000..9a8bc95 --- /dev/null +++ b/generator/core/array.go @@ -0,0 +1 @@ +package core diff --git a/generator/core/bool.go b/generator/core/bool.go new file mode 100644 index 0000000..87713fa --- /dev/null +++ b/generator/core/bool.go @@ -0,0 +1,15 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" +) + +func GenerateBoolFunc() generator.GenerationFunction { + + return &coreGenerationFunction{ + _func: func(parameters ...any) any { + return generateNum(0, 1) == 0 + }, + // Config: , + } +} diff --git a/generator/core/core.generation.function.go b/generator/core/core.generation.function.go new file mode 100644 index 0000000..7d1bcd3 --- /dev/null +++ b/generator/core/core.generation.function.go @@ -0,0 +1,16 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" +) + +type coreGenerationFunction struct { + _func func(...any) any + args []any +} + +var _ generator.GenerationFunction = &coreGenerationFunction{} + +func (cgf *coreGenerationFunction) Generate() any { + return cgf._func(cgf.args...) +} diff --git a/generator/core/date.go b/generator/core/date.go new file mode 100644 index 0000000..3553b61 --- /dev/null +++ b/generator/core/date.go @@ -0,0 +1,27 @@ +package core + +import ( + "time" + + "github.com/MartinSimango/dstruct/generator" +) + +const ISO8601 string = "2018-03-20T09:12:28Z" + +func GenerateDateTimeFunc() generator.GenerationFunction { + + // TODO have a proper implementation + return &coreGenerationFunction{ + _func: func(parameters ...any) any { + return time.Now().UTC().Format(time.RFC3339) + }, + } + +} + +func GenerateDateTimeBetweenDatesFunc(startDate, endDate time.Time) generator.GenerationFunction { + // TODO implement + // f := generateDateTime + // return f + return nil +} diff --git a/generator/core/default.generation.functions.go b/generator/core/default.generation.functions.go new file mode 100644 index 0000000..657979b --- /dev/null +++ b/generator/core/default.generation.functions.go @@ -0,0 +1,42 @@ +package core + +import ( + "reflect" + + "github.com/MartinSimango/dstruct/generator/config" +) + +type DefaultGenerationFunctions map[reflect.Kind]FunctionHolder + +func (d DefaultGenerationFunctions) Copy(kind reflect.Kind) (dgf DefaultGenerationFunctions) { + // look at the kind and only return what needs to be copied + dgf = make(DefaultGenerationFunctions) + switch kind { + case reflect.Struct, reflect.Slice, reflect.Ptr: + for k, v := range d { + dgf[k] = v.Copy() + } + default: + dgf[kind] = d[kind].Copy() + } + return dgf + +} + +func NewDefaultGenerationFunctions(cfg config.Config) DefaultGenerationFunctions { + + defaultGenerationFunctions := make(DefaultGenerationFunctions) + defaultGenerationFunctions[reflect.String] = NewFixedFunctionHolder(GenerateStringFromRegexFunc, "^[a-zA-Z]{3}$") + defaultGenerationFunctions[reflect.Int] = NewGenerateNumberFunctionHolder[int](cfg.Number()) + defaultGenerationFunctions[reflect.Int8] = NewGenerateNumberFunctionHolder[int8](cfg.Number()) + defaultGenerationFunctions[reflect.Int16] = NewGenerateNumberFunctionHolder[int16](cfg.Number()) + defaultGenerationFunctions[reflect.Int32] = NewGenerateNumberFunctionHolder[int32](cfg.Number()) + defaultGenerationFunctions[reflect.Int64] = NewGenerateNumberFunctionHolder[int64](cfg.Number()) + defaultGenerationFunctions[reflect.Float32] = NewGenerateNumberFunctionHolder[float32](cfg.Number()) + defaultGenerationFunctions[reflect.Float64] = NewGenerateNumberFunctionHolder[float64](cfg.Number()) + defaultGenerationFunctions[reflect.Bool] = NewFunctionHolderNoArgs(GenerateBoolFunc()) + defaultGenerationFunctions[reflect.Ptr] = NewFunctionHolderNoArgs(GenerateNilValueFunc()) + + return defaultGenerationFunctions + +} diff --git a/generator/core/fixed.function.holder.go b/generator/core/fixed.function.holder.go new file mode 100644 index 0000000..12595fd --- /dev/null +++ b/generator/core/fixed.function.holder.go @@ -0,0 +1,41 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" +) + +type FixedFunctionHolderFunc[T any] func(value T) generator.GenerationFunction + +type FixedFunctionHolder[T any] struct { + value T + BaseFunctionHolder +} + +var _ FunctionHolder = &FixedFunctionHolder[int]{} + +func NewFixedFunctionHolder[T any](f FixedFunctionHolderFunc[T], value T) *FixedFunctionHolder[T] { + return &FixedFunctionHolder[T]{ + BaseFunctionHolder: BaseFunctionHolder{ + fun: f, + }, + value: value, + } + +} + +// Override +func (c *FixedFunctionHolder[T]) GetFunction() generator.GenerationFunction { + if c.generationFunction != nil { + return c.generationFunction + } + c.generationFunction = c.fun.(FixedFunctionHolderFunc[T])(c.value) + + return c.generationFunction +} + +func (c *FixedFunctionHolder[T]) Copy() FunctionHolder { + return &FixedFunctionHolder[T]{ + BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + value: c.value, + } +} diff --git a/generator/core/fixed.go b/generator/core/fixed.go new file mode 100644 index 0000000..6268252 --- /dev/null +++ b/generator/core/fixed.go @@ -0,0 +1,13 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" +) + +func GenerateFixedValueFunc[T any](n T) generator.GenerationFunction { + return &coreGenerationFunction{ + _func: func(p ...any) any { + return n + }, + } +} diff --git a/generator/core/function.holder.go b/generator/core/function.holder.go new file mode 100644 index 0000000..37171ca --- /dev/null +++ b/generator/core/function.holder.go @@ -0,0 +1,51 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" +) + +type FunctionHolder interface { + GetFunction() generator.GenerationFunction + SetFunction(generationFunction generator.GenerationFunction) + GetConfig() config.Config + SetConfig(cfg config.Config) + Copy() FunctionHolder +} + +type BaseFunctionHolder struct { + config config.Config + generationFunction generator.GenerationFunction + fun any +} + +func (c *BaseFunctionHolder) SetFunction(generationFunction generator.GenerationFunction) { + c.generationFunction = generationFunction +} + +func (c *BaseFunctionHolder) GetFunction() generator.GenerationFunction { + return c.generationFunction +} + +func (c *BaseFunctionHolder) GetConfig() config.Config { + return c.config +} + +func (c *BaseFunctionHolder) SetConfig(cfg config.Config) { + c.generationFunction = nil // new config recreate generation function + c.config = cfg +} + +func (c *BaseFunctionHolder) Copy() BaseFunctionHolder { + // TODO should this panic? + var configCopy config.Config + if c.config != nil { + configCopy = c.config.Copy() + } + + return BaseFunctionHolder{ + fun: c.fun, + config: configCopy, + generationFunction: c.generationFunction, + } +} diff --git a/generator/core/generated.field.go b/generator/core/generated.field.go new file mode 100644 index 0000000..fb61b4c --- /dev/null +++ b/generator/core/generated.field.go @@ -0,0 +1,198 @@ +package core + +import ( + "fmt" + "reflect" + "strconv" + + "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" +) + +type GeneratedFieldConfig struct { + GenerationFunctions DefaultGenerationFunctions + GenerationValueConfig config.GenerationValueConfig +} + +func (gf *GeneratedFieldConfig) Copy(kind reflect.Kind) (gfc GeneratedFieldConfig) { + return GeneratedFieldConfig{ + GenerationFunctions: gf.GenerationFunctions.Copy(kind), + GenerationValueConfig: gf.GenerationValueConfig, + } +} + +func (gf *GeneratedFieldConfig) SetConfig(cfg config.Config) { + for _, v := range gf.GenerationFunctions { + v.SetConfig(cfg) + } +} + +func NewGenerateFieldConfig(cfg config.Config, gvc config.GenerationValueConfig) GeneratedFieldConfig { + return GeneratedFieldConfig{ + GenerationFunctions: NewDefaultGenerationFunctions(cfg), + GenerationValueConfig: gvc, + } +} + +type GeneratedField struct { + Name string + Value reflect.Value + Tag reflect.StructTag + GeneratedFieldConfig + Parent *GeneratedField + PointerValue *reflect.Value +} + +func NewGeneratedField(fqn string, + value reflect.Value, + tag reflect.StructTag, + generatedFieldConfig GeneratedFieldConfig) *GeneratedField { + g := &GeneratedField{ + Name: fqn, + Value: value, + Tag: tag, + GeneratedFieldConfig: generatedFieldConfig, + } + return g +} + +func (field *GeneratedField) checkForRecursiveDefinition(fail bool) bool { + var depth uint = 0 + var matchedField *GeneratedField + for parent := field.Parent; parent != nil; parent = parent.Parent { + if parent.Value.Type() == field.Value.Type() { + if !field.GenerationValueConfig.RecursiveDefinition.Allow || fail { + panic(fmt.Sprintf("github.com/MartinSimango/dstruct/generator: recursive definition found for field `%s` of type %s", parent.Name, parent.Value.Type())) + } + depth++ + if depth == 1 { + matchedField = parent + } + } + if depth == (field.GenerationValueConfig.RecursiveDefinition.Depth + 1) { + // fmt.Println(":DF ", field.Name, matchedField.Name, field.Value.Type(), matchedField.Value.Type(), matchedField.Value, depth) + if matchedField.PointerValue != nil { + matchedField.PointerValue.SetZero() + } else { + matchedField.Value.SetZero() + } + return true + } + } + return false + +} + +func (field *GeneratedField) SetValue() bool { + kind := field.Value.Kind() + switch kind { + case reflect.Struct: + if field.checkForRecursiveDefinition(false) { + return true + } + GenerateStructFunc(field).Generate() + case reflect.Pointer: + GeneratePointerValueFunc(field).Generate() + case reflect.Slice: + // Don't allow recursion within slices + if field.checkForRecursiveDefinition(true) { + return true + } + field.Value.Set(reflect.ValueOf(field.GenerationFunctions[kind].GetFunction().Generate())) + case reflect.Interface: + field.Value.Set(reflect.Zero(field.Value.Type())) + default: + field.Value.Set(reflect.ValueOf(field.getGenerationFunction().Generate())) + } + return false +} + +func (field *GeneratedField) setStructValues() { + for j := 0; j < field.Value.NumField(); j++ { + structField := &GeneratedField{ + Name: field.Name + "." + field.Value.Type().Field(j).Name, + Value: field.Value.Field(j), + Tag: field.Value.Type().Field(j).Tag, + GeneratedFieldConfig: field.GeneratedFieldConfig.Copy(field.Value.Field(j).Kind()), + Parent: field, + } + structField.SetValue() + } +} + +func (field *GeneratedField) getGenerationFunction() generator.GenerationFunction { + kind := field.Value.Kind() + tags := field.Tag + + // TODO see if needed + switch kind { + case reflect.Slice: + return GenerateSliceFunc(field, nil) + case reflect.Struct: + return GenerateStructFunc(field) + case reflect.Ptr: + return GeneratePointerValueFunc(field) + } + if field.GenerationValueConfig.ValueGenerationType == config.UseDefaults { + example, ok := tags.Lookup("example") + if !ok { + example, ok = tags.Lookup("default") + } + if ok { + switch kind { + case reflect.Int: + v, _ := strconv.Atoi(example) + return GenerateFixedValueFunc(v) + case reflect.Int32: + v, _ := strconv.Atoi(example) + return GenerateFixedValueFunc(int32(v)) + case reflect.Int64: + v, _ := strconv.Atoi(example) + return GenerateFixedValueFunc(int64(v)) + case reflect.Float64: + v, _ := strconv.ParseFloat(example, 64) + return GenerateFixedValueFunc(float64(v)) + case reflect.String: + return GenerateFixedValueFunc(example) + case reflect.Bool: + v, _ := strconv.ParseBool(example) + return GenerateFixedValueFunc(v) + default: + fmt.Println("Unsupported types for defaults: ", kind, example) + } + + } + } + + pattern := tags.Get("pattern") + if pattern != "" { + return GenerateStringFromRegexFunc(pattern) + } + + format := tags.Get("format") + + switch format { + case "date-time": + return GenerateDateTimeFunc() + } + + enum, ok := tags.Lookup("enum") + if ok { + numEnums, _ := strconv.Atoi(enum) + return GenerateFixedValueFunc(tags.Get(fmt.Sprintf("enum_%d", generateNum(0, numEnums-1)+1))) + } + + _, ok = tags.Lookup("gen_task") + if ok { + taskProperties, err := generator.CreateTaskProperties(field.Name, tags) + if err != nil { + panic(fmt.Sprintf("Error decoding gen_task: %s", err.Error())) + } + task := generator.GetTask(taskProperties.TaskName) + if task == nil { + panic(fmt.Sprintf("Unregistered task %s", taskProperties.TaskName)) + } + return task.GenerationFunction(*taskProperties) + } + return field.GenerationFunctions[kind].GetFunction() +} diff --git a/generator/core/generation.unit.go b/generator/core/generation.unit.go new file mode 100644 index 0000000..f22f8a1 --- /dev/null +++ b/generator/core/generation.unit.go @@ -0,0 +1,49 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" +) + +type GenerationUnit struct { + PreviousValueConfig config.GenerationValueConfig + CurrentFunction generator.GenerationFunction + UpdateCurrentFunction bool + *GeneratedField + count int + latestValue any + generationConfig config.GenerationValueConfig +} + +func NewGenerationUnit(field *GeneratedField) *GenerationUnit { + gu := &GenerationUnit{ + GeneratedField: field, + generationConfig: field.GeneratedFieldConfig.GenerationValueConfig, + } + gu.PreviousValueConfig = gu.generationConfig + gu.CurrentFunction = gu.getGenerationFunction() + return gu +} + +func (gu *GenerationUnit) Generate() any { + // check if important fields have changed and then regenerate the currentfunction + gu.CurrentFunction = gu.getGenerationFunction() + if gu.configChanged(gu.PreviousValueConfig) || gu.UpdateCurrentFunction { + gu.CurrentFunction = gu.getGenerationFunction() + gu.UpdateCurrentFunction = false + } + + if gu.generationConfig.ValueGenerationType == config.GenerateOnce && gu.count > 0 { + return gu.latestValue + } + gu.latestValue = gu.CurrentFunction.Generate() + gu.PreviousValueConfig = gu.generationConfig + gu.count++ + return gu.latestValue +} + +func (gu *GenerationUnit) configChanged(previousConfig config.GenerationValueConfig) bool { + + return gu.generationConfig.ValueGenerationType != previousConfig.ValueGenerationType || + gu.generationConfig.SetNonRequiredFields != previousConfig.SetNonRequiredFields +} diff --git a/generator/core/nil.go b/generator/core/nil.go new file mode 100644 index 0000000..5f7f5e2 --- /dev/null +++ b/generator/core/nil.go @@ -0,0 +1,14 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" +) + +func GenerateNilValueFunc() generator.GenerationFunction { + return &coreGenerationFunction{ + _func: func(parameters ...any) any { + return nil + }, + } + +} diff --git a/generator/core/no.arg.function.holder.go b/generator/core/no.arg.function.holder.go new file mode 100644 index 0000000..81a16f3 --- /dev/null +++ b/generator/core/no.arg.function.holder.go @@ -0,0 +1,26 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" +) + +type FunctionHolderFuncNoArgs func() generator.GenerationFunction + +type FunctionHolderWithNoArgs struct { + BaseFunctionHolder + generationFunction generator.GenerationFunction +} + +var _ FunctionHolder = &FunctionHolderWithNoArgs{} + +func NewFunctionHolderNoArgs(generationFunction generator.GenerationFunction) *FunctionHolderWithNoArgs { + return &FunctionHolderWithNoArgs{ + generationFunction: generationFunction, + } +} + +func (c *FunctionHolderWithNoArgs) Copy() FunctionHolder { + return &FunctionHolderWithNoArgs{ + BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + } +} diff --git a/generator/core/number.function.holder.go b/generator/core/number.function.holder.go new file mode 100644 index 0000000..f2e1fba --- /dev/null +++ b/generator/core/number.function.holder.go @@ -0,0 +1,39 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" +) + +type NumberFunctionHolderFunc func(config.NumberConfig) generator.GenerationFunction + +type NumberFunctionHolder struct { + BaseFunctionHolder +} + +var _ FunctionHolder = &NumberFunctionHolder{} + +func NewNumberFunctionHolder(f NumberFunctionHolderFunc, cfg config.NumberConfig) *NumberFunctionHolder { + return &NumberFunctionHolder{ + BaseFunctionHolder: BaseFunctionHolder{ + config: config.NewConfigBuilder().WithNumberConfig(cfg).Build(), + fun: f, + }, + } +} + +// Override +func (c *NumberFunctionHolder) GetFunction() generator.GenerationFunction { + if c.generationFunction != nil { + return c.generationFunction + } + c.generationFunction = c.fun.(NumberFunctionHolderFunc)(c.config.Number()) + + return c.generationFunction +} + +func (c *NumberFunctionHolder) Copy() FunctionHolder { + return &NumberFunctionHolder{ + BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + } +} diff --git a/generator/core/number.go b/generator/core/number.go new file mode 100644 index 0000000..f31a355 --- /dev/null +++ b/generator/core/number.go @@ -0,0 +1,44 @@ +package core + +import ( + "math/rand" + + "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" + "github.com/MartinSimango/dstruct/util" +) + +func generateNum[n util.Number](min, max n) n { + return min + (n(rand.Float64() * float64(max+1-min))) +} + +func GenerateNumberFunc[n util.Number](cfg config.NumberConfig) generator.GenerationFunction { + min, max := getNumberRange[n](cfg) + return &coreGenerationFunction{ + _func: func(parameters ...any) any { + return generateNum(*min, *max) + }, + args: []any{min, max}, + } + +} + +func getNumberRange[n util.Number](cfg config.NumberConfig) (*n, *n) { + var min, max any + switch any(*new(n)).(type) { + case int: + min, max = cfg.IntRange() + + case int8: + min, max = cfg.Int8Range() + case int32: + min, max = cfg.Int32Range() + case int64: + min, max = cfg.Int64Range() + } + return any(min).(*n), any(max).(*n) +} + +func NewGenerateNumberFunctionHolder[N util.Number](numberConfig config.NumberConfig) *NumberFunctionHolder { + return NewNumberFunctionHolder(GenerateNumberFunc[N], numberConfig) +} diff --git a/generator/core/pointer.go b/generator/core/pointer.go new file mode 100644 index 0000000..bc2619e --- /dev/null +++ b/generator/core/pointer.go @@ -0,0 +1,34 @@ +package core + +import ( + "reflect" + + "github.com/MartinSimango/dstruct/generator" +) + +func GeneratePointerValueFunc(field *GeneratedField) generator.GenerationFunction { + + return &coreGenerationFunction{ + _func: func(parameters ...any) any { + field := parameters[0].(*GeneratedField) + if !field.GenerationValueConfig.SetNonRequiredFields { + return nil + } + + field.Value.Set(reflect.New(field.Value.Type().Elem())) + fieldPointerValue := *field + fieldPointerValue.Value = field.Value.Elem() + fieldPointerValue.PointerValue = &field.Value + fieldPointerValue.SetValue() + + if field.Value.Elem().CanSet() { + field.Value.Elem().Set(fieldPointerValue.Value) + } + + return field.Value.Interface() + + }, + args: []any{field}, + } + +} diff --git a/generator/core/slice.function.holder.go b/generator/core/slice.function.holder.go new file mode 100644 index 0000000..ae96d20 --- /dev/null +++ b/generator/core/slice.function.holder.go @@ -0,0 +1,40 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" +) + +type SliceFunctionHolderFunc func(*GeneratedField, config.Config) generator.GenerationFunction + +type SliceFunctionHolder struct { + BaseFunctionHolder + field *GeneratedField +} + +var _ FunctionHolder = &SliceFunctionHolder{} + +func NewSliceFunctionHolder(f SliceFunctionHolderFunc, field *GeneratedField, cfg config.Config) *SliceFunctionHolder { + return &SliceFunctionHolder{ + BaseFunctionHolder: BaseFunctionHolder{ + config: cfg, + fun: f, + }, + field: field, + } +} + +func (c *SliceFunctionHolder) GetFunction() generator.GenerationFunction { + if c.generationFunction != nil { + return c.generationFunction + } + c.generationFunction = c.fun.(SliceFunctionHolderFunc)(c.field, c.config) + + return c.generationFunction +} + +func (c *SliceFunctionHolder) Copy() FunctionHolder { + return &NumberFunctionHolder{ + BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + } +} diff --git a/generator/core/slice.go b/generator/core/slice.go new file mode 100644 index 0000000..9a45454 --- /dev/null +++ b/generator/core/slice.go @@ -0,0 +1,46 @@ +package core + +import ( + "fmt" + "reflect" + + "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" +) + +func GenerateSliceFunc(field *GeneratedField, config config.Config) generator.GenerationFunction { + return &coreGenerationFunction{ + _func: func(parameters ...any) any { + + field := parameters[0].(*GeneratedField) + sliceConfig := field.GenerationFunctions[reflect.Slice].GetConfig().Slice() + sliceType := reflect.TypeOf(field.Value.Interface()).Elem() + min := sliceConfig.MinLength() + max := sliceConfig.MaxLength() + + len := generateNum(min, max) + sliceOfElementType := reflect.SliceOf(sliceType) + slice := reflect.MakeSlice(sliceOfElementType, 0, 1024) + sliceElement := reflect.New(sliceType) + + for i := 0; i < len; i++ { + elemValue := reflect.ValueOf(sliceElement.Interface()).Elem() + newField := &GeneratedField{ + Name: fmt.Sprintf("%s#%d", field.Name, i), + Value: elemValue, + Tag: field.Tag, + GeneratedFieldConfig: NewGenerateFieldConfig(field.GenerationFunctions[reflect.Slice].GetConfig(), field.GenerationValueConfig), + Parent: field, + } + + newField.SetValue() + + slice = reflect.Append(slice, sliceElement.Elem()) + } + return slice.Interface() + + }, + args: []any{field}, + } + +} diff --git a/generator/core/string.go b/generator/core/string.go new file mode 100644 index 0000000..ea4a5a6 --- /dev/null +++ b/generator/core/string.go @@ -0,0 +1,20 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" + "github.com/takahiromiyamoto/go-xeger" +) + +func GenerateStringFromRegexFunc(regex string) generator.GenerationFunction { + return &coreGenerationFunction{ + _func: func(parameters ...any) any { + regex := parameters[0].(string) + x, err := xeger.NewXeger(regex) + if err != nil { + panic(err) + } + return x.Generate() + }, + args: []any{regex}, + } +} diff --git a/generator/core/struct.go b/generator/core/struct.go new file mode 100644 index 0000000..9fba3df --- /dev/null +++ b/generator/core/struct.go @@ -0,0 +1,18 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" +) + +func GenerateStructFunc(field *GeneratedField) generator.GenerationFunction { + + return &coreGenerationFunction{ + _func: func(parameters ...any) any { + field := parameters[0].(*GeneratedField) + field.setStructValues() + return field.Value.Interface() + }, + args: []any{field}, + } + +} diff --git a/generator/functions.go b/generator/functions.go index ecc0bea..274b215 100644 --- a/generator/functions.go +++ b/generator/functions.go @@ -1,130 +1,42 @@ package generator -import ( - "fmt" - "reflect" - "time" -) - -type ( - generateNumberFunc[N number] GenerationFunctionImpl - generateStringFromRegexFunc GenerationFunctionImpl - generateFixedValueFunc[T any] GenerationFunctionImpl -) - -var _ GenerationFunction = &generateNumberFunc[int]{} - -func (gn generateNumberFunc[N]) Copy(newConfig *GenerationConfig) GenerationFunction { - return GenerateNumberFunc(*gn.args[0].(*N), *gn.args[1].(*N)).SetGenerationConfig(newConfig) -} -func assign[T any](configMin, configMax *T, min, max T) (*T, *T) { - *configMin = min - *configMax = max - return configMin, configMax -} - -func (gf *generateNumberFunc[n]) GetGenerationConfig() *GenerationConfig { - return gf.GenerationConfig -} - -func (gf *generateNumberFunc[n]) SetGenerationConfig(generationConfig *GenerationConfig) GenerationFunction { - min, max := *gf.args[0].(*n), *gf.args[1].(*n) - paramKind := reflect.ValueOf(min).Kind() - var param_1, param_2 any - switch paramKind { - case reflect.Int: - param_1, param_2 = assign(&generationConfig.intMin, &generationConfig.intMax, int(min), int(max)) - case reflect.Int32: - param_1, param_2 = assign(&generationConfig.int32Min, &generationConfig.int32Max, int32(min), int32(max)) - case reflect.Int64: - param_1, param_2 = assign(&generationConfig.int64Min, &generationConfig.int64Max, int64(min), int64(max)) - case reflect.Float32: - param_1, param_2 = assign(&generationConfig.float32Min, &generationConfig.float32Max, float32(min), float32(max)) - case reflect.Float64: - param_1, param_2 = assign(&generationConfig.float64Min, &generationConfig.float64Max, float64(min), float64(max)) - default: - panic(fmt.Sprintf("Invalid number type: %s", paramKind)) - } - gf.args = []any{param_1, param_2} - return gf -} - -func GenerateNumberFunc[n number](min, max n) GenerationFunction { - f := &generateNumberFunc[n]{ - basicGenerationFunction: generateNumber, - } - - f.args = []any{&min, &max} - - return f -} - -func GenerateStringFromRegexFunc(regex string) GenerationFunction { - f := generateStringFromRegexFunc{ - basicGenerationFunction: generateStringFromRegex, - } - f.args = []any{regex} - return f -} - -func GenerateFixedValueFunc[T any](n T) GenerationFunction { - f := generateFixedValueFunc[T]{} - f._func = func(p ...any) any { - return n - } - return f -} - -func GenerateBoolFunc() basicGenerationFunction { - f := generateBool - return f -} - -func GenerateNilValueFunc() basicGenerationFunction { - f := generateNilValue - return f -} - -func GenerateSliceFunc(field *GeneratedField) GenerationFunction { - f := generateSlice - f.args = []any{field} - return f -} - -type generateStructFunc struct { - GenerationFunctionImpl - field *GeneratedField -} - -func GenerateStructFunc(field *GeneratedField) GenerationFunction { - - f := &generateStructFunc{ - GenerationFunctionImpl: GenerationFunctionImpl{ - GenerationConfig: field.Generator.GenerationConfig, - basicGenerationFunction: generateStruct, - }, - field: field, - } - f.args = []any{field} - return f -} - -func (g generateStructFunc) Copy(newConfig *GenerationConfig) GenerationFunction { - return GenerateStructFunc(g.field) -} - -func GeneratePointerValueFunc(field *GeneratedField) GenerationFunction { - f := generatePointerValue - f.args = []any{field} - return f -} - -func GenerateDateTimeFunc() GenerationFunction { - f := generateDateTime - return f -} -func GenerateDateTimeBetweenDatesFunc(startDate, endDate time.Time) GenerationFunction { - // TODO implement - f := generateDateTime - return f -} +// var _ GenerationFunction = &generateNumberFunc[int]{} + +// func (gn generateNumberFunc[N]) Copy(newConfig *FunctionGenerationConfig) GenerationFunction { +// return GenerateNumberFunc(*gn.args[0].(*N), *gn.args[1].(*N)).SetGenerationConfig(newConfig) +// } +// func assign[T any](configMin, configMax *T, min, max T) (*T, *T) { +// *configMin = min +// *configMax = max +// return configMin, configMax +// } + +// func (gf *generateNumberFunc[n]) GetGenerationConfig() *FunctionGenerationConfig { +// return gf.FunctionGenerationConfig +// } + +// func (gf *generateNumberFunc[n]) SetGenerationConfig(generationConfig *FunctionGenerationConfig) GenerationFunction { +// min, max := *gf.args[0].(*n), *gf.args[1].(*n) +// paramKind := reflect.ValueOf(min).Kind() +// var param_1, param_2 any +// switch paramKind { +// case reflect.Int: +// param_1, param_2 = assign(&generationConfig.intMin, &generationConfig.intMax, int(min), int(max)) +// case reflect.Int32: +// param_1, param_2 = assign(&generationConfig.int32Min, &generationConfig.int32Max, int32(min), int32(max)) +// case reflect.Int64: +// param_1, param_2 = assign(&generationConfig.int64Min, &generationConfig.int64Max, int64(min), int64(max)) +// case reflect.Float32: +// param_1, param_2 = assign(&generationConfig.float32Min, &generationConfig.float32Max, float32(min), float32(max)) +// case reflect.Float64: +// param_1, param_2 = assign(&generationConfig.float64Min, &generationConfig.float64Max, float64(min), float64(max)) +// default: +// panic(fmt.Sprintf("Invalid number type: %s", paramKind)) +// } +// gf.args = []any{param_1, param_2} +// return gf +// } + +// func (g generateStructFunc) Copy(newConfig *FunctionGenerationConfig) GenerationFunction { +// return GenerateStructFunc(g.field) +// } diff --git a/generator/generated.field.go b/generator/generated.field.go index b99fbb9..634271f 100644 --- a/generator/generated.field.go +++ b/generator/generated.field.go @@ -1,169 +1,205 @@ package generator -import ( - "fmt" - "reflect" - "strconv" -) - -type GeneratedField struct { - Name string - Value reflect.Value - Tag reflect.StructTag - Generator *Generator - Parent *GeneratedField - PointerValue *reflect.Value -} - -func NewGeneratedField(fqn string, - value reflect.Value, - tag reflect.StructTag, - generator *Generator) *GeneratedField { - g := &GeneratedField{ - Name: fqn, - Value: value, - Tag: tag, - Generator: generator, - } - return g -} - -func (field *GeneratedField) checkForRecursiveDefinition(fail bool) bool { - var depth uint = 0 - var matchedField *GeneratedField - for parent := field.Parent; parent != nil; parent = parent.Parent { - if parent.Value.Type() == field.Value.Type() { - if !field.Generator.GenerationConfig.recursiveDefinition.Allow || fail { - panic(fmt.Sprintf("github.com/MartinSimango/dstruct/generator: recursive definition found for field `%s` of type %s", parent.Name, parent.Value.Type())) - } - depth++ - if depth == 1 { - matchedField = parent - } - } - if depth == (field.Generator.GenerationConfig.recursiveDefinition.Count + 1) { - // fmt.Println(":DF ", field.Name, matchedField.Name, field.Value.Type(), matchedField.Value.Type(), matchedField.Value, depth) - if matchedField.PointerValue != nil { - matchedField.PointerValue.SetZero() - } else { - matchedField.Value.SetZero() - } - return true - } - } - return false - -} - -func (field *GeneratedField) SetValue() bool { - - switch field.Value.Kind() { - case reflect.Struct: - if field.checkForRecursiveDefinition(false) { - return true - } - GenerateStructFunc(field).Generate() - case reflect.Pointer: - GeneratePointerValueFunc(field).Generate() - case reflect.Slice: - if field.checkForRecursiveDefinition(true) { - return true - } - field.Value.Set(reflect.ValueOf(GenerateSliceFunc(field).Generate())) - case reflect.Interface: - field.Value.Set(reflect.Zero(field.Value.Type())) - default: - field.Value.Set(reflect.ValueOf(field.getGenerationFunction().Generate())) - } - return false -} - -func (field *GeneratedField) setStructValues() { - for j := 0; j < field.Value.NumField(); j++ { - structField := &GeneratedField{ - Name: field.Name + "." + field.Value.Type().Field(j).Name, - Value: field.Value.Field(j), - Tag: field.Value.Type().Field(j).Tag, - Generator: field.Generator.Copy(), - Parent: field, - } - structField.SetValue() - } -} - -func (field *GeneratedField) getGenerationFunction() GenerationFunction { - kind := field.Value.Kind() - tags := field.Tag - - switch kind { - case reflect.Slice: - return GenerateSliceFunc(field) - case reflect.Struct: - return GenerateStructFunc(field) - case reflect.Ptr: - return GeneratePointerValueFunc(field) - } - - if field.Generator.GenerationConfig.valueGenerationType == UseDefaults { - example, ok := tags.Lookup("example") - if !ok { - example, ok = tags.Lookup("default") - } - if ok { - switch kind { - case reflect.Int: - v, _ := strconv.Atoi(example) - return GenerateFixedValueFunc(v) - case reflect.Int32: - v, _ := strconv.Atoi(example) - return GenerateFixedValueFunc(int32(v)) - case reflect.Int64: - v, _ := strconv.Atoi(example) - return GenerateFixedValueFunc(int64(v)) - case reflect.Float64: - v, _ := strconv.ParseFloat(example, 64) - return GenerateFixedValueFunc(float64(v)) - case reflect.String: - return GenerateFixedValueFunc(example) - case reflect.Bool: - v, _ := strconv.ParseBool(example) - return GenerateFixedValueFunc(v) - default: - fmt.Println("Unsupported types for defaults: ", kind, example) - } - - } - } - - pattern := tags.Get("pattern") - if pattern != "" { - return GenerateStringFromRegexFunc(pattern) - } - - format := tags.Get("format") - - switch format { - case "date-time": - return GenerateDateTimeFunc() - } - - enum, ok := tags.Lookup("enum") - if ok { - numEnums, _ := strconv.Atoi(enum) - return GenerateFixedValueFunc(tags.Get(fmt.Sprintf("enum_%d", generateNum(0, numEnums-1)+1))) - } - - _, ok = tags.Lookup("gen_task") - if ok { - taskProperties, err := CreateTaskProperties(field.Name, tags) - if err != nil { - panic(fmt.Sprintf("Error decoding gen_task: %s", err.Error())) - } - task := GetTask(taskProperties.TaskName) - if task == nil { - panic(fmt.Sprintf("Unregistered task %s", taskProperties.TaskName)) - } - return task.GenerationFunction(*taskProperties) - } - return field.Generator.DefaultGenerationFunctions[kind] -} +// import ( +// "fmt" +// "reflect" +// "strconv" +// ) + +// // type recursiveField struct { +// // Parent GeneratedField +// // PointerValue reflect.Value +// // } + +// // // FunctionSpy + +// // type GenerationFunctionV1 struct { +// // Config Generation +// // Function GenerationFunction +// // } + +// // type GenerationFieldV1 struct { +// // Name string +// // Value reflect.Value +// // Tag reflect.StructTag + +// // GenerationFunction Function +// // RecursiveField *recursiveField +// // } + +// // type GenerateFieldV1 struct { +// // Name string +// // Value reflect.Value +// // Tag reflect.StructTag +// // Config config. + +// // } + +// type GenerationFieldV1[ConfigType any] struct { +// Name string +// Value reflect.Value +// Tag reflect.StructTag +// FunctionConfig ConfigType +// } + +// type GeneratedField struct { +// Name string +// Value reflect.Value +// Tag reflect.StructTag +// Generator *Generator +// Parent *GeneratedField +// PointerValue *reflect.Value +// } + +// func NewGeneratedField(fqn string, +// value reflect.Value, +// tag reflect.StructTag, +// generator *Generator) *GeneratedField { +// g := &GeneratedField{ +// Name: fqn, +// Value: value, +// Tag: tag, +// Generator: generator, +// } +// return g +// } + +// func (field *GeneratedField) checkForRecursiveDefinition(fail bool) bool { +// var depth uint = 0 +// var matchedField *GeneratedField +// for parent := field.Parent; parent != nil; parent = parent.Parent { +// if parent.Value.Type() == field.Value.Type() { +// if !field.Generator.GenerationConfig.recursiveDefinition.Allow || fail { +// panic(fmt.Sprintf("github.com/MartinSimango/dstruct/generator: recursive definition found for field `%s` of type %s", parent.Name, parent.Value.Type())) +// } +// depth++ +// if depth == 1 { +// matchedField = parent +// } +// } +// if depth == (field.Generator.GenerationConfig.recursiveDefinition.Count + 1) { +// // fmt.Println(":DF ", field.Name, matchedField.Name, field.Value.Type(), matchedField.Value.Type(), matchedField.Value, depth) +// if matchedField.PointerValue != nil { +// matchedField.PointerValue.SetZero() +// } else { +// matchedField.Value.SetZero() +// } +// return true +// } +// } +// return false + +// } + +// func (field *GeneratedField) SetValue() bool { + +// switch field.Value.Kind() { +// case reflect.Struct: +// if field.checkForRecursiveDefinition(false) { +// return true +// } +// GenerateStructFunc(field).Generate() +// case reflect.Pointer: +// GeneratePointerValueFunc(field).Generate() +// case reflect.Slice: +// if field.checkForRecursiveDefinition(true) { +// return true +// } +// field.Value.Set(reflect.ValueOf(GenerateSliceFunc(field).Generate())) +// case reflect.Interface: +// field.Value.Set(reflect.Zero(field.Value.Type())) +// default: +// field.Value.Set(reflect.ValueOf(field.getGenerationFunction().Generate())) +// } +// return false +// } + +// func (field *GeneratedField) setStructValues() { +// for j := 0; j < field.Value.NumField(); j++ { +// structField := &GeneratedField{ +// Name: field.Name + "." + field.Value.Type().Field(j).Name, +// Value: field.Value.Field(j), +// Tag: field.Value.Type().Field(j).Tag, +// Generator: field.Generator.Copy(), +// Parent: field, +// } +// structField.SetValue() +// } +// } + +// func (field *GeneratedField) getGenerationFunction() GenerationFunction { +// kind := field.Value.Kind() +// tags := field.Tag + +// switch kind { +// case reflect.Slice: +// return GenerateSliceFunc(field) +// case reflect.Struct: +// return GenerateStructFunc(field) +// case reflect.Ptr: +// return GeneratePointerValueFunc(field) +// } + +// if field.Generator.GenerationConfig.valueGenerationType == UseDefaults { +// example, ok := tags.Lookup("example") +// if !ok { +// example, ok = tags.Lookup("default") +// } +// if ok { +// switch kind { +// case reflect.Int: +// v, _ := strconv.Atoi(example) +// return GenerateFixedValueFunc(v) +// case reflect.Int32: +// v, _ := strconv.Atoi(example) +// return GenerateFixedValueFunc(int32(v)) +// case reflect.Int64: +// v, _ := strconv.Atoi(example) +// return GenerateFixedValueFunc(int64(v)) +// case reflect.Float64: +// v, _ := strconv.ParseFloat(example, 64) +// return GenerateFixedValueFunc(float64(v)) +// case reflect.String: +// return GenerateFixedValueFunc(example) +// case reflect.Bool: +// v, _ := strconv.ParseBool(example) +// return GenerateFixedValueFunc(v) +// default: +// fmt.Println("Unsupported types for defaults: ", kind, example) +// } + +// } +// } + +// pattern := tags.Get("pattern") +// if pattern != "" { +// return GenerateStringFromRegexFunc(pattern) +// } + +// format := tags.Get("format") + +// switch format { +// case "date-time": +// return GenerateDateTimeFunc() +// } + +// enum, ok := tags.Lookup("enum") +// if ok { +// numEnums, _ := strconv.Atoi(enum) +// return GenerateFixedValueFunc(tags.Get(fmt.Sprintf("enum_%d", generateNum(0, numEnums-1)+1))) +// } + +// _, ok = tags.Lookup("gen_task") +// if ok { +// taskProperties, err := CreateTaskProperties(field.Name, tags) +// if err != nil { +// panic(fmt.Sprintf("Error decoding gen_task: %s", err.Error())) +// } +// task := GetTask(taskProperties.TaskName) +// if task == nil { +// panic(fmt.Sprintf("Unregistered task %s", taskProperties.TaskName)) +// } +// return task.GenerationFunction(*taskProperties) +// } +// return field.Generator.DefaultGenerationFunctions[kind] +// } diff --git a/generator/generation.function.go b/generator/generation.function.go new file mode 100644 index 0000000..8cb2b34 --- /dev/null +++ b/generator/generation.function.go @@ -0,0 +1,10 @@ +package generator + +type GenerationFunction interface { + Generate() any + // Copy copies the generation config and returns a copy of it with the same config + // as the origin generation config + // Copy(*GenerationConfig) GenerationFunction + // GetGenerationConfig() *GenerationConfig + // SetGenerationConfig(*GenerationConfig) GenerationFunction +} diff --git a/generator/generation.unit.go b/generator/generation.unit.go deleted file mode 100644 index a58bdb8..0000000 --- a/generator/generation.unit.go +++ /dev/null @@ -1,43 +0,0 @@ -package generator - -type GenerationUnit struct { - PreviousValueConfig GenerationValueConfig - CurrentFunction GenerationFunction - UpdateCurrentFunction bool - Field *GeneratedField - count int - latestValue any - generationConfig *GenerationConfig -} - -func NewGenerationUnit(field *GeneratedField) *GenerationUnit { - gu := &GenerationUnit{ - Field: field, - generationConfig: field.Generator.GenerationConfig, - } - gu.PreviousValueConfig = gu.generationConfig.GenerationValueConfig - gu.CurrentFunction = gu.Field.getGenerationFunction() - return gu -} - -func (gu *GenerationUnit) Generate() any { - // check if important fields have changed and then regenerate the currentfunction - if gu.configChanged(gu.PreviousValueConfig) || gu.UpdateCurrentFunction { - gu.CurrentFunction = gu.Field.getGenerationFunction() - gu.UpdateCurrentFunction = false - } - - if gu.generationConfig.valueGenerationType == GenerateOnce && gu.count > 0 { - return gu.latestValue - } - gu.latestValue = gu.CurrentFunction.Generate() - gu.PreviousValueConfig = gu.generationConfig.GenerationValueConfig - gu.count++ - return gu.latestValue -} - -func (gu *GenerationUnit) configChanged(previousConfig GenerationValueConfig) bool { - - return gu.generationConfig.valueGenerationType != previousConfig.valueGenerationType || - gu.generationConfig.setNonRequiredFields != previousConfig.setNonRequiredFields -} diff --git a/generator/generator.go b/generator/generator.go index 3e4e9ca..734e55a 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -1,70 +1,64 @@ package generator -import ( - "reflect" -) +// import ( +// "reflect" +// ) -type GenerationFunction interface { - Generate() any - // Copy copies the generation config and returns a copy of it with the same config - // as the origin generation config - Copy(*GenerationConfig) GenerationFunction - GetGenerationConfig() *GenerationConfig - SetGenerationConfig(*GenerationConfig) GenerationFunction -} +// type GenerationFunctionImpl struct { +// *FunctionGenerationConfig +// basicGenerationFunction +// } -type GenerationFunctionImpl struct { - *GenerationConfig - basicGenerationFunction -} +// func (bsf basicGenerationFunction) Generate() any { +// return bsf._func(bsf.args...) +// } -func (bsf basicGenerationFunction) Generate() any { - return bsf._func(bsf.args...) -} +// func (bsf basicGenerationFunction) Copy(*FunctionGenerationConfig) GenerationFunction { +// return bsf +// } -func (bsf basicGenerationFunction) Copy(*GenerationConfig) GenerationFunction { - return bsf -} +// func (bsf basicGenerationFunction) GetGenerationConfig() *FunctionGenerationConfig { +// return nil +// } -func (bsf basicGenerationFunction) GetGenerationConfig() *GenerationConfig { - return nil -} +// func (bsf basicGenerationFunction) SetGenerationConfig(*FunctionGenerationConfig) GenerationFunction { +// return bsf +// } -func (bsf basicGenerationFunction) SetGenerationConfig(*GenerationConfig) GenerationFunction { - return bsf -} +// type DefaultGenerationFunctionType map[reflect.Kind]GenerationFunction -type DefaultGenerationFunctionType map[reflect.Kind]GenerationFunction +// type Generator struct { +// GenerationConfig *FunctionGenerationConfig +// DefaultGenerationFunctions DefaultGenerationFunctionType +// } -type Generator struct { - GenerationConfig *GenerationConfig - DefaultGenerationFunctions DefaultGenerationFunctionType -} +// type GeneratorV1[K reflect.Kind] struct { +// } -func NewGenerator(gc *GenerationConfig) *Generator { +// func NewGenerator(gc *FunctionGenerationConfig) *Generator { - defaultGenerationFunctions := make(DefaultGenerationFunctionType) - defaultGenerationFunctions[reflect.String] = GenerateFixedValueFunc("string") - defaultGenerationFunctions[reflect.Ptr] = GenerateNilValueFunc() - defaultGenerationFunctions[reflect.Int] = GenerateNumberFunc(gc.intMin, gc.intMax).SetGenerationConfig(gc) - defaultGenerationFunctions[reflect.Int64] = GenerateNumberFunc(gc.int64Min, gc.int64Max).SetGenerationConfig(gc) - defaultGenerationFunctions[reflect.Int32] = GenerateNumberFunc(gc.int32Min, gc.int32Max).SetGenerationConfig(gc) - defaultGenerationFunctions[reflect.Float64] = GenerateNumberFunc(gc.float64Min, gc.float64Max).SetGenerationConfig(gc) - defaultGenerationFunctions[reflect.Bool] = GenerateBoolFunc() +// defaultGenerationFunctions := make(DefaultGenerationFunctionType) +// defaultGenerationFunctions[reflect.String] = GenerateFixedValueFunc("string") +// defaultGenerationFunctions[reflect.Ptr] = GenerateNilValueFunc() +// defaultGenerationFunctions[reflect.Int] = GenerateNumberFunc(gc.intMin, gc.intMax).SetGenerationConfig(gc) +// defaultGenerationFunctions[reflect.Int64] = GenerateNumberFunc(gc.int64Min, gc.int64Max).SetGenerationConfig(gc) +// defaultGenerationFunctions[reflect.Int32] = GenerateNumberFunc(gc.int32Min, gc.int32Max).SetGenerationConfig(gc) +// defaultGenerationFunctions[reflect.Float64] = GenerateNumberFunc(gc.float64Min, gc.float64Max).SetGenerationConfig(gc) +// defaultGenerationFunctions[reflect.Bool] = GenerateBoolFunc() - return &Generator{ - GenerationConfig: gc, - DefaultGenerationFunctions: defaultGenerationFunctions, - } -} +// return &Generator{ +// GenerationConfig: gc, +// DefaultGenerationFunctions: defaultGenerationFunctions, +// } +// } -func (gd *Generator) Copy() (generationDefaults *Generator) { - generationDefaults = &Generator{ - DefaultGenerationFunctions: make(DefaultGenerationFunctionType), - GenerationConfig: gd.GenerationConfig.Clone(), - } - for k, v := range gd.DefaultGenerationFunctions { - generationDefaults.DefaultGenerationFunctions[k] = v.Copy(generationDefaults.GenerationConfig) - } - return -} +// func (gd *Generator) Copy() (generationDefaults *Generator) { +// generationDefaults = &Generator{ +// DefaultGenerationFunctions: make(DefaultGenerationFunctionType), +// GenerationConfig: gd.GenerationConfig.Clone(), +// } +// for k, v := range gd.DefaultGenerationFunctions { +// generationDefaults.DefaultGenerationFunctions[k] = v.Copy(generationDefaults.GenerationConfig) +// } +// return +// } diff --git a/modifier.go b/modifier.go index 3f97fe8..06deddc 100644 --- a/modifier.go +++ b/modifier.go @@ -141,7 +141,7 @@ func (dm *DynamicStructModifierImpl) String() string { } func (dm *DynamicStructModifierImpl) Update() { - *dm = *ExtendStruct(dm.strct).Build().(*DynamicStructModifierImpl) + *dm = *ExtendStruct(dm.Instance()).Build().(*DynamicStructModifierImpl) } func (dm *DynamicStructModifierImpl) Apply(field string, value any) error { diff --git a/util/number.go b/util/number.go new file mode 100644 index 0000000..342af7b --- /dev/null +++ b/util/number.go @@ -0,0 +1,6 @@ +package util + +type Number interface { + int | int8 | int16 | int32 | int64 | float32 | float64 | + uint | uint8 | uint16 | uint32 | uint64 | uintptr +} From 12413aa5545f961f4844ddcefc5f820150335e2d Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sun, 10 Dec 2023 23:42:49 +0200 Subject: [PATCH 07/31] refactor config and how generation works --- dreflect/dreflect.go | 6 +- field.go | 1 + generated.struct.go | 148 ++++++--- generator/config.go | 81 ----- generator/config/config.builder.go | 6 + generator/config/config.go | 85 ++++++ generator/config/date.go | 69 +++++ generator/config/number.config.go | 281 ++++++++++-------- generator/core/bool.go | 4 +- generator/core/core.generation.function.go | 7 + generator/core/date.go | 44 ++- .../core/default.generation.functions.go | 9 + generator/core/fixed.go | 3 + generator/core/function.holder.go | 7 + generator/core/generated.field.go | 17 +- generator/core/generation.unit.go | 18 +- generator/core/no.arg.function.holder.go | 12 +- generator/core/number.function.holder.go | 5 +- generator/core/number.go | 56 +++- generator/core/pointer.go | 1 + generator/core/slice.function.holder.go | 19 +- generator/core/slice.go | 7 +- generator/core/string.go | 3 + generator/core/struct.go | 3 + generator/functions.go | 42 --- generator/generated.field.go | 205 ------------- generator/generation.function.go | 8 +- generator/generator.go | 64 ---- generator/generator.type.go | 53 ---- tree.builder.go | 8 +- util/number.go | 6 - 31 files changed, 609 insertions(+), 669 deletions(-) delete mode 100644 generator/config.go create mode 100644 generator/config/date.go delete mode 100644 generator/functions.go delete mode 100644 generator/generated.field.go delete mode 100644 generator/generator.go delete mode 100644 generator/generator.type.go delete mode 100644 util/number.go diff --git a/dreflect/dreflect.go b/dreflect/dreflect.go index 42b4335..e4bf979 100644 --- a/dreflect/dreflect.go +++ b/dreflect/dreflect.go @@ -20,11 +20,15 @@ func GetSliceType(slice any) reflect.Type { return reflect.TypeOf(slice).Elem() } +func ConvertToType[T any](val any) T { + return Convert(reflect.ValueOf(val), reflect.TypeOf(*new(T))).Interface().(T) +} + // Convert extends the reflect.Convert function an proceeds to convert subtypes func Convert(value reflect.Value, t reflect.Type) reflect.Value { defer func() { if r := recover(); r != nil { - panic(fmt.Sprintf("dreflect.Convert: value of type %s cannot be converted to type %s", value.Type(), t)) + panic(fmt.Sprintf("dreflect.Convert: value of type %v cannot be converted to type %v", value.Type(), t)) } }() dst := reflect.New(t).Elem() diff --git a/field.go b/field.go index b9ba4b1..e370b81 100644 --- a/field.go +++ b/field.go @@ -11,6 +11,7 @@ type structField struct { tag reflect.StructTag value reflect.Value typ reflect.Type + goType reflect.Type pkgPath string anonymous bool jsonName string diff --git a/generated.struct.go b/generated.struct.go index dd78d18..f045340 100644 --- a/generated.struct.go +++ b/generated.struct.go @@ -3,7 +3,10 @@ package dstruct import ( "fmt" "reflect" + "time" + "github.com/MartinSimango/dstruct/dreflect" + "github.com/MartinSimango/dstruct/generator" "github.com/MartinSimango/dstruct/generator/config" "github.com/MartinSimango/dstruct/generator/core" ) @@ -19,11 +22,11 @@ type GeneratedStruct interface { // new generated fields to be accessed and modified by Set and Get methods. GenerateAndUpdate() + SetFieldGenerationValueConfig(field string, config config.GenerationValueConfig) + // GetFieldValueGenerationConfig gets the generation config for field within the struct. GetFieldValueGenerationConfig(field string) config.GenerationValueConfig - GetFieldGenerationConfig(field string) config.Config - GetValueGenerationConfig() config.GenerationValueConfig // SetFieldGenerationConfig sets the generation config for field within the struct. It returns @@ -34,84 +37,107 @@ type GeneratedStruct interface { // Fields types that cannot be generated: structs, func, chan, any (will default to a nil value being generated). // // Note: Pointers to structs can be generated. - SetFieldGenerationConfig(field string, generationConfig config.Config) error + SetFieldConfig(field string, generationConfig config.Config) error - SetFieldGenerationValueConfig(field string, config config.GenerationValueConfig) + GetFieldConfig(field string) config.Config - SetGenerationConfig(config config.Config) + SetConfig(config config.Config) + + GetConfig() config.Config SetGenerationValueConfig(config config.GenerationValueConfig) + + SetFieldFromTask(field string, task generator.Task, params ...any) error } -type GeneratedStructImpl struct { +type GeneratedStructImpl[T any] struct { *DynamicStructModifierImpl generatedFields GenerationFields generatedFieldConfig core.GeneratedFieldConfig config config.Config + instance T + excludedTypes map[reflect.Type]bool } -var _ GeneratedStruct = &GeneratedStructImpl{} +// var _ GeneratedStruct = &GeneratedStructImpl[int]{} -func NewGeneratedStruct(val any) *GeneratedStructImpl { +func NewGeneratedStruct[T any](val T) *GeneratedStructImpl[T] { return NewGeneratedStructWithConfig(val, config.NewConfig()) } -func NewGeneratedStructWithConfig(val any, - cfg config.Config) *GeneratedStructImpl { - generatedStruct := &GeneratedStructImpl{ +func NewGeneratedStructWithConfig[T any](val T, + cfg config.Config) *GeneratedStructImpl[T] { + generatedStruct := &GeneratedStructImpl[T]{ DynamicStructModifierImpl: ExtendStruct(val).Build().(*DynamicStructModifierImpl), generatedFields: make(GenerationFields), config: cfg, generatedFieldConfig: core.NewGenerateFieldConfig(cfg, config.DefaultGenerationValueConfig()), + excludedTypes: make(map[reflect.Type]bool), } - - generatedStruct.populateGeneratedFields() + generatedStruct.ExcludeType(time.Time{}, core.DefaultDateFunctionHolder(cfg.Date())) + generatedStruct.populateGeneratedFields(generatedStruct.root) return generatedStruct } -func (gs *GeneratedStructImpl) populateGeneratedFields() { +func (gs *GeneratedStructImpl[T]) ExcludeType(val any, function core.FunctionHolder) { + gs.excludedTypes[reflect.TypeOf(val)] = true +} + +func (gs *GeneratedStructImpl[T]) populateGeneratedFields(node *Node[structField]) { - for name, field := range gs.fieldNodeMap { + for _, field := range node.children { if field.HasChildren() { + if gs.excludedTypes[field.data.goType] { + field.data.value.Set(reflect.ValueOf(time.Now())) + continue + } + gs.populateGeneratedFields(field) continue } fieldKind := field.data.value.Kind() - gs.generatedFields[name] = core.NewGenerationUnit( - core.NewGeneratedField(field.data.fqn, - field.data.value, - field.data.tag, - gs.generatedFieldConfig.Copy(fieldKind), - )) - if fieldKind == reflect.Slice { - f := gs.generatedFields[name] - f.GenerationFunctions[fieldKind] = - core.NewSliceFunctionHolder(core.GenerateSliceFunc, f.GeneratedField, gs.config) - } + generatedField := core.NewGeneratedField(field.data.fqn, + field.data.value, + field.data.tag, + gs.generatedFieldConfig.Copy(fieldKind), + gs.config.Copy(), // TODO account for nil + ) + + gs.generatedFields[field.data.fqn] = core.NewGenerationUnit(generatedField) + } + } -func (gs *GeneratedStructImpl) Generate() { +func (gs *GeneratedStructImpl[T]) Generate() { gs.generateFields() + + v := any(*new(T)) + switch v.(type) { + case nil: + gs.instance = gs.DynamicStructModifierImpl.Instance().(T) + return + } + gs.instance = ToType[T](gs.DynamicStructModifierImpl) } -func (gs *GeneratedStructImpl) GenerateAndUpdate() { +func (gs *GeneratedStructImpl[T]) GenerateAndUpdate() { gs.Generate() gs.Update() } -func (gs *GeneratedStructImpl) changeChildrenConfig(node *Node[structField], cfg config.Config) { - for k, v := range node.children { +func (gs *GeneratedStructImpl[T]) changeChildrenConfig(node *Node[structField], cfg config.Config) { + for _, v := range node.children { if v.HasChildren() { gs.changeChildrenConfig(v, cfg) continue } - gs.generatedFields[k].GenerationFunctions[v.data.typ.Kind()].SetConfig(cfg) + gs.generatedFields[v.data.fqn].GenerationFunctions[v.data.typ.Kind()].SetConfig(cfg) } } -func (gs *GeneratedStructImpl) SetFieldGenerationConfig(field string, cfg config.Config) error { +func (gs *GeneratedStructImpl[T]) SetFieldGenerationConfig(field string, cfg config.Config) error { if gs.fieldNodeMap[field] == nil { return fmt.Errorf("field %s does not exist within the struct", field) } @@ -129,21 +155,20 @@ func (gs *GeneratedStructImpl) SetFieldGenerationConfig(field string, cfg config return nil } -func (gs *GeneratedStructImpl) SetFieldGenerationFunction(field string, +func (gs *GeneratedStructImpl[T]) SetFieldGenerationFunction(field string, functionHolder core.FunctionHolder) { - kind := gs.fieldNodeMap[field].data.GetType().Kind() - generator := gs.generatedFields[field].GeneratedFieldConfig - generator.GenerationFunctions[kind] = functionHolder - gs.generatedFields[field].UpdateCurrentFunction = true + + // kind := gs.fieldNodeMap[field].data.GetType().Kind() + // _ = functionHolder.(*core.FunctionHolderWithNoArgs) + gs.generatedFields[field].GeneratedField.GenerationFunctions[functionHolder.Kind()] = functionHolder } -func (gs *GeneratedStructImpl) SetFieldGenerationFunctions(field string, +func (gs *GeneratedStructImpl[T]) SetFieldGenerationFunctions(field string, defaultGenerationFunctions core.DefaultGenerationFunctions) { gs.generatedFields[field].GenerationFunctions = defaultGenerationFunctions - gs.generatedFields[field].UpdateCurrentFunction = true } -func (gs *GeneratedStructImpl) SetGenerationConfig(config config.Config) { +func (gs *GeneratedStructImpl[T]) SetGenerationConfig(config config.Config) { for name, field := range gs.fieldNodeMap { if field.HasChildren() { continue @@ -152,11 +177,11 @@ func (gs *GeneratedStructImpl) SetGenerationConfig(config config.Config) { } } -func (gs *GeneratedStructImpl) SetFieldGenerationValueConfig(field string, config config.GenerationValueConfig) { +func (gs *GeneratedStructImpl[T]) SetFieldGenerationValueConfig(field string, config config.GenerationValueConfig) { gs.generatedFields[field].GenerationValueConfig = config } -func (gs *GeneratedStructImpl) SetGenerationValueConfig(config config.GenerationValueConfig) { +func (gs *GeneratedStructImpl[T]) SetGenerationValueConfig(config config.GenerationValueConfig) { for name, field := range gs.fieldNodeMap { if field.HasChildren() { continue @@ -166,25 +191,54 @@ func (gs *GeneratedStructImpl) SetGenerationValueConfig(config config.Generation } // GetFieldValueGenerationConfig implements GeneratedStruct. -func (gs *GeneratedStructImpl) GetFieldValueGenerationConfig(field string) config.GenerationValueConfig { +func (gs *GeneratedStructImpl[T]) GetFieldValueGenerationConfig(field string) config.GenerationValueConfig { return gs.generatedFields[field].GenerationValueConfig } // GetFieldValueGenerationConfig implements GeneratedStruct. -func (gs *GeneratedStructImpl) GetFieldGenerationConfig(field string) config.Config { +func (gs *GeneratedStructImpl[T]) GetFieldGenerationConfig(field string) config.Config { k := gs.generatedFields[field].GeneratedField.Value.Kind() return gs.generatedFields[field].GeneratedField.GenerationFunctions[k].GetConfig() } // GetValueGenerationConfig implements GeneratedStruct. -func (gs *GeneratedStructImpl) GetValueGenerationConfig() config.GenerationValueConfig { +func (gs *GeneratedStructImpl[T]) GetValueGenerationConfig() config.GenerationValueConfig { return gs.generatedFieldConfig.GenerationValueConfig } -func (gs *GeneratedStructImpl) generateFields() { +func (gs *GeneratedStructImpl[T]) SetFieldFromTask(field string, task generator.Task, params ...any) error { + taskProperties, err := generator.CreateTaskProperties(field, generator.GetTagForTask(generator.TaskName(task.Name()), params...)) + if err != nil { + return err + } + + gs.SetFieldGenerationFunction(field, core.NewFunctionHolderNoArgs(task.GenerationFunction(*taskProperties))) + return nil + +} + +func ToType[T any](gs DynamicStructModifier) T { + return dreflect.ConvertToType[T](gs.Instance()) +} + +func ToPointerType[T any](gs DynamicStructModifier) *T { + return dreflect.ConvertToType[*T](gs.New()) +} + +func (gs *GeneratedStructImpl[T]) generateFields() { for k, genFunc := range gs.generatedFields { if err := gs.Set(k, genFunc.Generate()); err != nil { - fmt.Println(err) + fmt.Println("E", err) } } + +} + +func (gs *GeneratedStructImpl[T]) Instance() T { + return gs.instance +} + +func (gs *GeneratedStructImpl[T]) New() *T { + gs.DynamicStructModifierImpl.New() + return &gs.instance } diff --git a/generator/config.go b/generator/config.go deleted file mode 100644 index 155de22..0000000 --- a/generator/config.go +++ /dev/null @@ -1,81 +0,0 @@ -package generator - -// import ( -// "time" -// ) - -// type DateConfig struct { -// dateFormat string -// dateStart time.Time -// dateEnd time.Time -// } - -// func defaultDateConfig() DateConfig { -// return DateConfig{ -// dateFormat: time.RFC3339, -// } -// } - -// func NewGenerationConfig() (generationConfig *FunctionGenerationConfig) { -// generationConfig = &FunctionGenerationConfig{ -// GenerationValueConfig: GenerationValueConfig{ -// valueGenerationType: UseDefaults, -// setNonRequiredFields: false, -// recursiveDefinition: recursiveDefinition{ -// Allow: false, -// Count: 1, -// }, -// }, -// SliceConfig: defaultSliceConfig(), -// IntConfig: defaultIntConfig(), -// FloatConfig: defaultFloatConfig(), -// DateConfig: defaultDateConfig(), -// } -// return -// } - -// func (gc *FunctionGenerationConfig) Clone() (config *FunctionGenerationConfig) { -// config = &FunctionGenerationConfig{} -// *config = *gc -// return -// } - -// func (gc *FunctionGenerationConfig) AllowRecursion(a bool) *FunctionGenerationConfig { -// gc.recursiveDefinition.Allow = a -// return gc -// } -// func (gc *FunctionGenerationConfig) SetRecursionCount(r uint) *FunctionGenerationConfig { -// gc.recursiveDefinition.Count = r -// return gc -// } - -// func (gc *FunctionGenerationConfig) SetNonRequiredFields(val bool) *FunctionGenerationConfig { -// gc.setNonRequiredFields = val -// return gc -// } - -// func (gc *FunctionGenerationConfig) SetValueGenerationType(valueGenerationType ValueGenerationType) *FunctionGenerationConfig { -// gc.valueGenerationType = valueGenerationType -// return gc -// } - -// func (gc *FunctionGenerationConfig) SetDateFormat(format string) *FunctionGenerationConfig { - -// // TODO validate format -// gc.dateFormat = format -// return gc -// } - -// func (gc *FunctionGenerationConfig) SetDateStart(start time.Time) *FunctionGenerationConfig { -// if !start.After(gc.dateEnd) { -// gc.dateStart = start -// } -// return gc -// } - -// func (gc *FunctionGenerationConfig) SetDateEnd(end time.Time) *FunctionGenerationConfig { -// if end.After(gc.dateStart) { -// gc.dateStart = end -// } -// return gc -// } diff --git a/generator/config/config.builder.go b/generator/config/config.builder.go index bd948bd..b7edd64 100644 --- a/generator/config/config.builder.go +++ b/generator/config/config.builder.go @@ -3,6 +3,7 @@ package config type ConfigBuilder interface { WithNumberConfig(NumberConfig) ConfigBuilder WithSliceConfig(SliceConfig) ConfigBuilder + WithDateConfig(DateConfig) ConfigBuilder Build() *ConfigImpl } @@ -30,6 +31,11 @@ func (cb *ConfigBuilderImpl) WithSliceConfig(sc SliceConfig) ConfigBuilder { return cb } +func (cb *ConfigBuilderImpl) WithDateConfig(dc DateConfig) ConfigBuilder { + cb.config.DateConfig = dc + return cb +} + func (cb *ConfigBuilderImpl) Build() *ConfigImpl { return cb.config } diff --git a/generator/config/config.go b/generator/config/config.go index 997a139..f1fbbeb 100644 --- a/generator/config/config.go +++ b/generator/config/config.go @@ -36,6 +36,7 @@ func NewConfig() *ConfigImpl { return NewConfigBuilder(). WithNumberConfig(NewNumberConfig()). WithSliceConfig(NewSliceConfig()). + WithDateConfig(NewDateConfig()). Build() } @@ -43,7 +44,22 @@ type Config interface { ConfigBuilder Number() NumberConfig Slice() SliceConfig + Date() DateConfig SetSliceLength(min, max int) Config + SetIntRange(min, max int) Config + SetInt8Range(min, max int8) Config + SetInt16Range(min, max int16) Config + SetInt32Range(min, max int32) Config + SetInt64Range(min, max int64) Config + SetFloat32Range(min, max float32) Config + SetFloat64Range(min, max float64) Config + SetUIntRange(min, max uint) Config + SetUInt8Range(min, max uint8) Config + SetUInt16Range(min, max uint16) Config + SetUInt32Range(min, max uint32) Config + SetUInt64Range(min, max uint64) Config + SetUIntPtr(min, max uintptr) Config + Copy() Config } @@ -51,6 +67,7 @@ type ConfigImpl struct { ConfigBuilderImpl SliceConfig SliceConfig NumberConfig NumberConfig + DateConfig DateConfig } var _ Config = &ConfigImpl{} @@ -71,6 +88,10 @@ func (c *ConfigImpl) Copy() Config { func (c *ConfigImpl) Slice() SliceConfig { return c.SliceConfig +} + +func (c *ConfigImpl) Date() DateConfig { + return c.DateConfig } @@ -82,3 +103,67 @@ func (c *ConfigImpl) SetSliceLength(min, max int) Config { c.SliceConfig.SetLengthRange(min, max) return c } + +func (c *ConfigImpl) SetIntRange(min, max int) Config { + c.NumberConfig.Int().SetRange(min, max) + return c +} + +func (c *ConfigImpl) SetInt8Range(min, max int8) Config { + c.NumberConfig.Int8().SetRange(min, max) + return c +} + +func (c *ConfigImpl) SetInt16Range(min, max int16) Config { + c.NumberConfig.Int16().SetRange(min, max) + return c +} + +func (c *ConfigImpl) SetInt32Range(min, max int32) Config { + c.NumberConfig.Int32().SetRange(min, max) + return c +} + +func (c *ConfigImpl) SetInt64Range(min, max int64) Config { + c.NumberConfig.Int64().SetRange(min, max) + return c +} + +func (c *ConfigImpl) SetFloat32Range(min, max float32) Config { + c.NumberConfig.Float32().SetRange(min, max) + return c +} +func (c *ConfigImpl) SetFloat64Range(min, max float64) Config { + c.NumberConfig.Float64().SetRange(min, max) + return c +} + +func (c *ConfigImpl) SetUIntRange(min, max uint) Config { + c.NumberConfig.UInt().SetRange(min, max) + return c +} + +func (c *ConfigImpl) SetUInt8Range(min, max uint8) Config { + c.NumberConfig.UInt8().SetRange(min, max) + return c +} + +func (c *ConfigImpl) SetUInt16Range(min, max uint16) Config { + c.NumberConfig.UInt16().SetRange(min, max) + return c +} + +func (c *ConfigImpl) SetUInt32Range(min, max uint32) Config { + c.NumberConfig.UInt32().SetRange(min, max) + return c +} + +func (c *ConfigImpl) SetUInt64Range(min, max uint64) Config { + c.NumberConfig.UInt64().SetRange(min, max) + return c +} + +func (c *ConfigImpl) SetUIntPtr(min, max uintptr) Config { + c.NumberConfig.UIntPtr().SetRange(min, max) + return c +} diff --git a/generator/config/date.go b/generator/config/date.go new file mode 100644 index 0000000..03a44c3 --- /dev/null +++ b/generator/config/date.go @@ -0,0 +1,69 @@ +package config + +import ( + "time" +) + +type DateConfig interface { + SetDateFormat(format string) + GetDateFormat() string + SetDateRange(start time.Time, end time.Time) + SetDateStart(start time.Time) + GetDateStart() time.Time + SetDateEnd(end time.Time) + GetDateEnd() time.Time +} + +type DateConfigImpl struct { + dateFormat string + dateStart time.Time + dateEnd time.Time +} + +var _ DateConfig = &DateConfigImpl{} + +// SetDateStart implements DateConfig. +func (dc *DateConfigImpl) SetDateStart(start time.Time) { + dc.SetDateRange(start, dc.dateEnd) +} + +// SetDateEnd implements DateConfig. +func (dc *DateConfigImpl) SetDateEnd(end time.Time) { + dc.SetDateRange(dc.dateStart, end) +} + +// SetDateFormat implements DateConfig. +func (dc *DateConfigImpl) SetDateFormat(format string) { + dc.dateFormat = format +} + +// SetDateRange implements DateConfig. +func (dc *DateConfigImpl) SetDateRange(start time.Time, end time.Time) { + if start.Before(end) { + dc.dateStart = start + dc.dateEnd = end + } +} + +// GetDateStart implements DateConfig. +func (dc *DateConfigImpl) GetDateStart() time.Time { + return dc.dateStart +} + +// GetDateEnd implements DateConfig. +func (dc *DateConfigImpl) GetDateEnd() time.Time { + return dc.dateEnd +} + +// GetDateFormat implements DateConfig. +func (dc *DateConfigImpl) GetDateFormat() string { + return dc.dateFormat +} + +func NewDateConfig() *DateConfigImpl { + return &DateConfigImpl{ + dateFormat: time.RFC3339, + dateStart: time.Now().Truncate(24 * time.Hour), + dateEnd: time.Now().Truncate(24 * time.Hour).Add(24*time.Hour - time.Nanosecond), + } +} diff --git a/generator/config/number.config.go b/generator/config/number.config.go index 9546062..f0a3ce4 100644 --- a/generator/config/number.config.go +++ b/generator/config/number.config.go @@ -1,157 +1,202 @@ package config -import "github.com/MartinSimango/dstruct/util" - -type NumberConfig interface { - IntRange() (*int, *int) - SetIntRange(min int, max int) NumberConfig - Int8Range() (*int8, *int8) - SetInt8Range(min int8, max int8) NumberConfig - Int16Range() (*int16, *int16) - SetInt16Range(min int16, max int16) NumberConfig - Int32Range() (*int32, *int32) - SetInt32Range(min int32, max int32) NumberConfig - Int64Range() (*int64, *int64) - SetInt64Range(min int64, max int64) NumberConfig - Copy() NumberConfig +type Number interface { + int | int8 | int16 | int32 | int64 | float32 | float64 | + uint | uint8 | uint16 | uint32 | uint64 | uintptr } -type Range[n util.Number] struct { + +type NumRange[n Number] struct { min n max n } +type NumberRangeImpl[n Number] struct { + NumRange[n] +} + +func (nr *NumberRangeImpl[n]) Range() (n, n) { + return nr.min, nr.max +} + +func (nr *NumberRangeImpl[n]) Max() n { + return nr.max + +} +func (nr *NumberRangeImpl[n]) Min() n { + return nr.min + +} + +func (nr *NumberRangeImpl[n]) SetRange(min n, max n) { + if min < max { + nr.min = min + nr.max = max + } + +} + +func (nr *NumberRangeImpl[n]) SetMax(max n) { + nr.SetRange(nr.min, max) + +} +func (nr *NumberRangeImpl[n]) SetMin(min n) { + nr.SetRange(min, nr.max) + +} + +func (nr *NumberRangeImpl[n]) RangeRef() (*n, *n) { + return &nr.min, &nr.max + +} + +type NumberConfig interface { + Int() *NumberRangeImpl[int] + Int8() *NumberRangeImpl[int8] + Int16() *NumberRangeImpl[int16] + Int32() *NumberRangeImpl[int32] + Int64() *NumberRangeImpl[int64] + Float32() *NumberRangeImpl[float32] + Float64() *NumberRangeImpl[float64] + UInt() *NumberRangeImpl[uint] + UInt8() *NumberRangeImpl[uint8] + UInt16() *NumberRangeImpl[uint16] + UInt32() *NumberRangeImpl[uint32] + UInt64() *NumberRangeImpl[uint64] + UIntPtr() *NumberRangeImpl[uintptr] + Copy() NumberConfig +} +type IntConfig NumberRangeImpl[int] type NumberConfigImpl struct { - intRange Range[int] - int8Range Range[int8] - int16Range Range[int16] - int32Range Range[int32] - int64Range Range[int64] + IntConfig NumberRangeImpl[int] + Int8Config NumberRangeImpl[int8] + Int16Config NumberRangeImpl[int16] + Int32Config NumberRangeImpl[int32] + Int64Config NumberRangeImpl[int64] + Float32Config NumberRangeImpl[float32] + Float64Config NumberRangeImpl[float64] + UIntConfig NumberRangeImpl[uint] + UInt8Config NumberRangeImpl[uint8] + UInt16Config NumberRangeImpl[uint16] + UInt32Config NumberRangeImpl[uint32] + UInt64Config NumberRangeImpl[uint64] + UIntPtrConfig NumberRangeImpl[uintptr] +} + +func NumberRangeConfig[N Number](min, max N) NumberRangeImpl[N] { + return NumberRangeImpl[N]{NumRange[N]{min, max}} +} + +func NewNumberConfig() *NumberConfigImpl { + return &NumberConfigImpl{ + IntConfig: NumberRangeConfig[int](0, 10), + Int8Config: NumberRangeConfig[int8](0, 10), + Int16Config: NumberRangeConfig[int16](0, 10), + Int32Config: NumberRangeConfig[int32](0, 10), + Int64Config: NumberRangeConfig[int64](0, 10), + Float32Config: NumberRangeConfig[float32](0, 10), + Float64Config: NumberRangeConfig[float64](0, 10), + UIntConfig: NumberRangeConfig[uint](0, 10), + UInt8Config: NumberRangeConfig[uint8](0, 10), + UInt16Config: NumberRangeConfig[uint16](0, 10), + UInt32Config: NumberRangeConfig[uint32](0, 10), + UInt64Config: NumberRangeConfig[uint64](0, 10), + UIntPtrConfig: NumberRangeConfig[uintptr](0, 10), + } + } var _ NumberConfig = &NumberConfigImpl{} -// Int16Range implements NumberConfig. -func (nc *NumberConfigImpl) Int16Range() (*int16, *int16) { - return &nc.int16Range.min, &nc.int16Range.max +// Float32 implements NumberConfig. +func (nc *NumberConfigImpl) Float32() *NumberRangeImpl[float32] { + return &nc.Float32Config } -// Int32Range implements NumberConfig. -func (nc *NumberConfigImpl) Int32Range() (*int32, *int32) { - return &nc.int32Range.min, &nc.int32Range.max +// Float64 implements NumberConfig. +func (nc *NumberConfigImpl) Float64() *NumberRangeImpl[float64] { + return &nc.Float64Config } -// Int64Range implements NumberConfig. -func (nc *NumberConfigImpl) Int64Range() (*int64, *int64) { - return &nc.int64Range.min, &nc.int64Range.max +// Int implements NumberConfig. +func (nc *NumberConfigImpl) Int() *NumberRangeImpl[int] { + return &nc.IntConfig +} +// Int16 implements NumberConfig. +func (nc *NumberConfigImpl) Int16() *NumberRangeImpl[int16] { + return &nc.Int16Config } -// Int8Range implements NumberConfig. -func (nc *NumberConfigImpl) Int8Range() (*int8, *int8) { - return &nc.int8Range.min, &nc.int8Range.max +// Int32 implements NumberConfig. +func (nc *NumberConfigImpl) Int32() *NumberRangeImpl[int32] { + return &nc.Int32Config } -// SetInt16Range implements NumberConfig. -func (nc *NumberConfigImpl) SetInt16Range(min int16, max int16) NumberConfig { - setRange(min, max, &nc.int16Range.min, &nc.int16Range.max) - return nc +// Int64 implements NumberConfig. +func (nc *NumberConfigImpl) Int64() *NumberRangeImpl[int64] { + return &nc.Int64Config } -// SetInt32Range implements NumberConfig. -func (nc *NumberConfigImpl) SetInt32Range(min int32, max int32) NumberConfig { - setRange(min, max, &nc.int32Range.min, &nc.int32Range.max) - return nc +// Int8 implements NumberConfig. +func (nc *NumberConfigImpl) Int8() *NumberRangeImpl[int8] { + return &nc.Int8Config + } -// SetInt64Range implements NumberConfig. -func (nc *NumberConfigImpl) SetInt64Range(min int64, max int64) NumberConfig { - setRange(min, max, &nc.int64Range.min, &nc.int64Range.max) - return nc +// UInt implements NumberConfig. +func (nc *NumberConfigImpl) UInt() *NumberRangeImpl[uint] { + return &nc.UIntConfig } -// SetInt8Range implements NumberConfig. -func (nc *NumberConfigImpl) SetInt8Range(min int8, max int8) NumberConfig { - setRange(min, max, &nc.int8Range.min, &nc.int8Range.max) - return nc +// UInt16 implements NumberConfig. +func (nc *NumberConfigImpl) UInt16() *NumberRangeImpl[uint16] { + return &nc.UInt16Config } -func (nc *NumberConfigImpl) IntRange() (*int, *int) { - return &nc.intRange.min, &nc.intRange.max +// UInt32 implements NumberConfig. +func (nc *NumberConfigImpl) UInt32() *NumberRangeImpl[uint32] { + return &nc.UInt32Config } -func (nc *NumberConfigImpl) SetIntRange(min, max int) NumberConfig { - setRange(min, max, &nc.intRange.min, &nc.intRange.max) - return nc +// UInt64 implements NumberConfig. +func (nc *NumberConfigImpl) UInt64() *NumberRangeImpl[uint64] { + return &nc.UInt64Config } -// Copy implements NumberConfig. -func (nc *NumberConfigImpl) Copy() NumberConfig { - newNumberConfig := &NumberConfigImpl{} - *newNumberConfig = *nc - return newNumberConfig +// UInt8 implements NumberConfig. +func (nc *NumberConfigImpl) UInt8() *NumberRangeImpl[uint8] { + return &nc.UInt8Config } -func setRange[n util.Number](min, max n, ncMin, ncMax *n) { - if min > max { - return - } - *ncMin, *ncMax = min, max +// UIntptr implements NumberConfig. +func (nc *NumberConfigImpl) UIntPtr() *NumberRangeImpl[uintptr] { + return &nc.UIntPtrConfig } -func NewNumberConfig() *NumberConfigImpl { - return &NumberConfigImpl{ - intRange: Range[int]{0, 10}, - int8Range: Range[int8]{0, 10}, - int16Range: Range[int16]{0, 10}, - int32Range: Range[int32]{0, 10}, - int64Range: Range[int64]{0, 10}, - } -} +// UIntptr implements NumberConfig. +func (nc *NumberConfigImpl) Copy() NumberConfig { + return simplePointerCopy(nc) + // return &NumberConfigImpl{ + // IntConfig: nc.IntConfig, + // Int8Config: nc.Int8Config, + // Int16Config: nc.Int16Config, + // Int32Config: nc.Int32Config, + // Int64Config: nc.Int64Config, + // Float32Config: nc.Float32Config, + // Float64Config: nc.Float64Config, + // UIntConfig: nc.UIntConfig, + // UInt8Config: nc.UInt8Config, + // UInt16Config: nc.UInt16Config, + // UInt32Config: nc.UInt32Config, + // UInt64Config: nc.UInt64Config, + // UIntPtrConfig: nc.UIntPtrConfig, + // } +} + +func simplePointerCopy[T any](p *T) *T { + v := new(T) + *v = *p + return v -// IntRange() (*n, *n) -// SetIntRange(min n, max n) - -// type NumberConfig[n util.Number] interface { -// // TODO pointer values should only be accesible internally move files to same package -// MinRange() *n -// MaxRange() *n -// SetRange(min n, max n) -// Copy() NumberConfig[n] -// } - -// type NumberConfigImpl[n util.Number] struct { -// minRange n -// maxRange n -// } - -// var _ NumberConfig[int] = &NumberConfigImpl[int]{} - -// func NewNumberConfig[n util.Number]() (cfg *NumberConfigImpl[n]) { -// return &NumberConfigImpl[n]{ -// minRange: 0, -// maxRange: 10, -// } -// } - -// func NewNumberConfigWithRange[n util.Number](min, max n) (cfg *NumberConfigImpl[n]) { -// return &NumberConfigImpl[n]{ -// minRange: min, -// maxRange: max, -// } -// } - -// func (nc *NumberConfigImpl[n]) MinRange() *n { -// return &nc.minRange - -// } - -// func (nc *NumberConfigImpl[n]) MaxRange() *n { -// return &nc.maxRange -// } - -// func (nc *NumberConfigImpl[n]) Copy() NumberConfig[n] { -// newNumberConfig := &NumberConfigImpl[n]{} -// *newNumberConfig = *nc -// return newNumberConfig -// } +} diff --git a/generator/core/bool.go b/generator/core/bool.go index 87713fa..f05f086 100644 --- a/generator/core/bool.go +++ b/generator/core/bool.go @@ -1,6 +1,8 @@ package core import ( + "reflect" + "github.com/MartinSimango/dstruct/generator" ) @@ -10,6 +12,6 @@ func GenerateBoolFunc() generator.GenerationFunction { _func: func(parameters ...any) any { return generateNum(0, 1) == 0 }, - // Config: , + kind: reflect.Bool, } } diff --git a/generator/core/core.generation.function.go b/generator/core/core.generation.function.go index 7d1bcd3..8018df9 100644 --- a/generator/core/core.generation.function.go +++ b/generator/core/core.generation.function.go @@ -1,12 +1,15 @@ package core import ( + "reflect" + "github.com/MartinSimango/dstruct/generator" ) type coreGenerationFunction struct { _func func(...any) any args []any + kind reflect.Kind } var _ generator.GenerationFunction = &coreGenerationFunction{} @@ -14,3 +17,7 @@ var _ generator.GenerationFunction = &coreGenerationFunction{} func (cgf *coreGenerationFunction) Generate() any { return cgf._func(cgf.args...) } + +func (cgf *coreGenerationFunction) Kind() reflect.Kind { + return cgf.kind +} diff --git a/generator/core/date.go b/generator/core/date.go index 3553b61..b005259 100644 --- a/generator/core/date.go +++ b/generator/core/date.go @@ -1,9 +1,11 @@ package core import ( + "reflect" "time" "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" ) const ISO8601 string = "2018-03-20T09:12:28Z" @@ -15,13 +17,45 @@ func GenerateDateTimeFunc() generator.GenerationFunction { _func: func(parameters ...any) any { return time.Now().UTC().Format(time.RFC3339) }, + kind: reflect.String, } } -func GenerateDateTimeBetweenDatesFunc(startDate, endDate time.Time) generator.GenerationFunction { - // TODO implement - // f := generateDateTime - // return f - return nil +func GenerateDateTimeBetweenDatesFunc(dc config.DateConfig) generator.GenerationFunction { + + // TODO have a proper implementation + return &coreGenerationFunction{ + _func: func(parameters ...any) any { + return time.Now().UTC().Format(time.RFC3339) + }, + kind: reflect.String, + } + +} + +type DateFunctionHolderFunc func(config.DateConfig) generator.GenerationFunction + +type DateFunctionHolder struct { + BaseFunctionHolder +} + +func NewDateFunctionHolder(f DateFunctionHolderFunc, cfg config.DateConfig) *DateFunctionHolder { + return &DateFunctionHolder{ + BaseFunctionHolder: BaseFunctionHolder{ + config: config.NewConfigBuilder().WithDateConfig(cfg).Build(), + fun: f, + generationFunction: f(cfg), + }, + } +} + +func DefaultDateFunctionHolder(cfg config.DateConfig) *DateFunctionHolder { + return NewDateFunctionHolder(GenerateDateTimeBetweenDatesFunc, cfg) +} + +func (c *DateFunctionHolder) Copy() FunctionHolder { + return &DateFunctionHolder{ + BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + } } diff --git a/generator/core/default.generation.functions.go b/generator/core/default.generation.functions.go index 657979b..95671f3 100644 --- a/generator/core/default.generation.functions.go +++ b/generator/core/default.generation.functions.go @@ -28,10 +28,19 @@ func NewDefaultGenerationFunctions(cfg config.Config) DefaultGenerationFunctions defaultGenerationFunctions := make(DefaultGenerationFunctions) defaultGenerationFunctions[reflect.String] = NewFixedFunctionHolder(GenerateStringFromRegexFunc, "^[a-zA-Z]{3}$") defaultGenerationFunctions[reflect.Int] = NewGenerateNumberFunctionHolder[int](cfg.Number()) + // NewFixedFunctionHolder(GenerateSequential, 0) + // NewGenerateNumberFunctionHolder[int](cfg.Number()) defaultGenerationFunctions[reflect.Int8] = NewGenerateNumberFunctionHolder[int8](cfg.Number()) defaultGenerationFunctions[reflect.Int16] = NewGenerateNumberFunctionHolder[int16](cfg.Number()) defaultGenerationFunctions[reflect.Int32] = NewGenerateNumberFunctionHolder[int32](cfg.Number()) defaultGenerationFunctions[reflect.Int64] = NewGenerateNumberFunctionHolder[int64](cfg.Number()) + + defaultGenerationFunctions[reflect.Uint] = NewGenerateNumberFunctionHolder[uint](cfg.Number()) + defaultGenerationFunctions[reflect.Uint8] = NewGenerateNumberFunctionHolder[uint8](cfg.Number()) + defaultGenerationFunctions[reflect.Uint16] = NewGenerateNumberFunctionHolder[uint16](cfg.Number()) + defaultGenerationFunctions[reflect.Uint32] = NewGenerateNumberFunctionHolder[uint32](cfg.Number()) + defaultGenerationFunctions[reflect.Uint64] = NewGenerateNumberFunctionHolder[uint64](cfg.Number()) + defaultGenerationFunctions[reflect.Float32] = NewGenerateNumberFunctionHolder[float32](cfg.Number()) defaultGenerationFunctions[reflect.Float64] = NewGenerateNumberFunctionHolder[float64](cfg.Number()) defaultGenerationFunctions[reflect.Bool] = NewFunctionHolderNoArgs(GenerateBoolFunc()) diff --git a/generator/core/fixed.go b/generator/core/fixed.go index 6268252..cfc6f40 100644 --- a/generator/core/fixed.go +++ b/generator/core/fixed.go @@ -1,6 +1,8 @@ package core import ( + "reflect" + "github.com/MartinSimango/dstruct/generator" ) @@ -9,5 +11,6 @@ func GenerateFixedValueFunc[T any](n T) generator.GenerationFunction { _func: func(p ...any) any { return n }, + kind: reflect.ValueOf(n).Kind(), } } diff --git a/generator/core/function.holder.go b/generator/core/function.holder.go index 37171ca..88fdd40 100644 --- a/generator/core/function.holder.go +++ b/generator/core/function.holder.go @@ -1,6 +1,8 @@ package core import ( + "reflect" + "github.com/MartinSimango/dstruct/generator" "github.com/MartinSimango/dstruct/generator/config" ) @@ -11,6 +13,7 @@ type FunctionHolder interface { GetConfig() config.Config SetConfig(cfg config.Config) Copy() FunctionHolder + Kind() reflect.Kind } type BaseFunctionHolder struct { @@ -49,3 +52,7 @@ func (c *BaseFunctionHolder) Copy() BaseFunctionHolder { generationFunction: c.generationFunction, } } + +func (c *BaseFunctionHolder) Kind() reflect.Kind { + return c.generationFunction.Kind() +} diff --git a/generator/core/generated.field.go b/generator/core/generated.field.go index fb61b4c..7d4b8b2 100644 --- a/generator/core/generated.field.go +++ b/generator/core/generated.field.go @@ -46,14 +46,20 @@ type GeneratedField struct { func NewGeneratedField(fqn string, value reflect.Value, tag reflect.StructTag, - generatedFieldConfig GeneratedFieldConfig) *GeneratedField { - g := &GeneratedField{ + generatedFieldConfig GeneratedFieldConfig, + config config.Config) *GeneratedField { + generateField := &GeneratedField{ Name: fqn, Value: value, Tag: tag, GeneratedFieldConfig: generatedFieldConfig, } - return g + if value.Kind() == reflect.Slice { + generatedFieldConfig.GenerationFunctions[value.Kind()] = + NewSliceFunctionHolder(GenerateSliceFunc, generateField, config, generateField.GenerationFunctions) + + } + return generateField } func (field *GeneratedField) checkForRecursiveDefinition(fail bool) bool { @@ -121,13 +127,11 @@ func (field *GeneratedField) setStructValues() { } func (field *GeneratedField) getGenerationFunction() generator.GenerationFunction { + kind := field.Value.Kind() tags := field.Tag - // TODO see if needed switch kind { - case reflect.Slice: - return GenerateSliceFunc(field, nil) case reflect.Struct: return GenerateStructFunc(field) case reflect.Ptr: @@ -194,5 +198,6 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio } return task.GenerationFunction(*taskProperties) } + return field.GenerationFunctions[kind].GetFunction() } diff --git a/generator/core/generation.unit.go b/generator/core/generation.unit.go index f22f8a1..fc37e2a 100644 --- a/generator/core/generation.unit.go +++ b/generator/core/generation.unit.go @@ -6,9 +6,8 @@ import ( ) type GenerationUnit struct { - PreviousValueConfig config.GenerationValueConfig - CurrentFunction generator.GenerationFunction - UpdateCurrentFunction bool + PreviousValueConfig config.GenerationValueConfig + CurrentFunction generator.GenerationFunction *GeneratedField count int latestValue any @@ -21,27 +20,30 @@ func NewGenerationUnit(field *GeneratedField) *GenerationUnit { generationConfig: field.GeneratedFieldConfig.GenerationValueConfig, } gu.PreviousValueConfig = gu.generationConfig - gu.CurrentFunction = gu.getGenerationFunction() + // gu.CurrentFunction = gu.getGenerationFunction() return gu } func (gu *GenerationUnit) Generate() any { - // check if important fields have changed and then regenerate the currentfunction + // gu.CurrentFunction = gu.getGenerationFunction() - if gu.configChanged(gu.PreviousValueConfig) || gu.UpdateCurrentFunction { - gu.CurrentFunction = gu.getGenerationFunction() - gu.UpdateCurrentFunction = false + + if gu.configChanged(gu.PreviousValueConfig) { + gu.count = 0 } if gu.generationConfig.ValueGenerationType == config.GenerateOnce && gu.count > 0 { return gu.latestValue } + gu.latestValue = gu.CurrentFunction.Generate() gu.PreviousValueConfig = gu.generationConfig gu.count++ return gu.latestValue } +// When did does a new fuc + func (gu *GenerationUnit) configChanged(previousConfig config.GenerationValueConfig) bool { return gu.generationConfig.ValueGenerationType != previousConfig.ValueGenerationType || diff --git a/generator/core/no.arg.function.holder.go b/generator/core/no.arg.function.holder.go index 81a16f3..eb36987 100644 --- a/generator/core/no.arg.function.holder.go +++ b/generator/core/no.arg.function.holder.go @@ -2,23 +2,31 @@ package core import ( "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" ) type FunctionHolderFuncNoArgs func() generator.GenerationFunction type FunctionHolderWithNoArgs struct { BaseFunctionHolder - generationFunction generator.GenerationFunction } var _ FunctionHolder = &FunctionHolderWithNoArgs{} func NewFunctionHolderNoArgs(generationFunction generator.GenerationFunction) *FunctionHolderWithNoArgs { return &FunctionHolderWithNoArgs{ - generationFunction: generationFunction, + BaseFunctionHolder: BaseFunctionHolder{ + generationFunction: generationFunction, + }, } } +func (c *FunctionHolderWithNoArgs) GetFunction() generator.GenerationFunction { + return c.generationFunction +} + +func (c *FunctionHolderWithNoArgs) SetConfig(config config.Config) {} + func (c *FunctionHolderWithNoArgs) Copy() FunctionHolder { return &FunctionHolderWithNoArgs{ BaseFunctionHolder: c.BaseFunctionHolder.Copy(), diff --git a/generator/core/number.function.holder.go b/generator/core/number.function.holder.go index f2e1fba..c06d684 100644 --- a/generator/core/number.function.holder.go +++ b/generator/core/number.function.holder.go @@ -16,8 +16,9 @@ var _ FunctionHolder = &NumberFunctionHolder{} func NewNumberFunctionHolder(f NumberFunctionHolderFunc, cfg config.NumberConfig) *NumberFunctionHolder { return &NumberFunctionHolder{ BaseFunctionHolder: BaseFunctionHolder{ - config: config.NewConfigBuilder().WithNumberConfig(cfg).Build(), - fun: f, + config: config.NewConfigBuilder().WithNumberConfig(cfg).Build(), + fun: f, + generationFunction: f(cfg), }, } } diff --git a/generator/core/number.go b/generator/core/number.go index f31a355..22f5601 100644 --- a/generator/core/number.go +++ b/generator/core/number.go @@ -1,44 +1,78 @@ package core import ( + "fmt" "math/rand" + "reflect" "github.com/MartinSimango/dstruct/generator" "github.com/MartinSimango/dstruct/generator/config" - "github.com/MartinSimango/dstruct/util" ) -func generateNum[n util.Number](min, max n) n { +func generateNum[n config.Number](min, max n) n { return min + (n(rand.Float64() * float64(max+1-min))) } -func GenerateNumberFunc[n util.Number](cfg config.NumberConfig) generator.GenerationFunction { +func GenerateNumberFunc[n config.Number](cfg config.NumberConfig) generator.GenerationFunction { min, max := getNumberRange[n](cfg) return &coreGenerationFunction{ _func: func(parameters ...any) any { return generateNum(*min, *max) }, + kind: reflect.ValueOf(*new(n)).Kind(), args: []any{min, max}, } } -func getNumberRange[n util.Number](cfg config.NumberConfig) (*n, *n) { +func GenerateSequential(seed int) generator.GenerationFunction { + return &coreGenerationFunction{ + _func: func(parameters ...any) any { + s := parameters[0].(*int) + *s++ + return *s + }, + kind: reflect.Int, + args: []any{&seed}, + } +} + +func getNumberRange[n config.Number](cfg config.NumberConfig) (*n, *n) { var min, max any - switch any(*new(n)).(type) { + v := any(*new(n)) + switch v.(type) { case int: - min, max = cfg.IntRange() - + min, max = cfg.Int().RangeRef() case int8: - min, max = cfg.Int8Range() + min, max = cfg.Int8().RangeRef() + case int16: + min, max = cfg.Int16().RangeRef() case int32: - min, max = cfg.Int32Range() + min, max = cfg.Int32().RangeRef() case int64: - min, max = cfg.Int64Range() + min, max = cfg.Int64().RangeRef() + case uint: + min, max = cfg.UInt().RangeRef() + case uint8: + min, max = cfg.UInt8().RangeRef() + case uint16: + min, max = cfg.UInt16().RangeRef() + case uint32: + min, max = cfg.UInt32().RangeRef() + case uint64: + min, max = cfg.UInt64().RangeRef() + case float32: + min, max = cfg.Float32().RangeRef() + case float64: + min, max = cfg.Float64().RangeRef() + case uintptr: + min, max = cfg.UIntPtr().RangeRef() + default: + panic(fmt.Sprintf("Type not supported for getNumberRange: %s", reflect.TypeOf(v))) } return any(min).(*n), any(max).(*n) } -func NewGenerateNumberFunctionHolder[N util.Number](numberConfig config.NumberConfig) *NumberFunctionHolder { +func NewGenerateNumberFunctionHolder[N config.Number](numberConfig config.NumberConfig) *NumberFunctionHolder { return NewNumberFunctionHolder(GenerateNumberFunc[N], numberConfig) } diff --git a/generator/core/pointer.go b/generator/core/pointer.go index bc2619e..88c2302 100644 --- a/generator/core/pointer.go +++ b/generator/core/pointer.go @@ -28,6 +28,7 @@ func GeneratePointerValueFunc(field *GeneratedField) generator.GenerationFunctio return field.Value.Interface() }, + kind: reflect.Ptr, args: []any{field}, } diff --git a/generator/core/slice.function.holder.go b/generator/core/slice.function.holder.go index ae96d20..1ab14a4 100644 --- a/generator/core/slice.function.holder.go +++ b/generator/core/slice.function.holder.go @@ -1,26 +1,30 @@ package core import ( + "reflect" + "github.com/MartinSimango/dstruct/generator" "github.com/MartinSimango/dstruct/generator/config" ) -type SliceFunctionHolderFunc func(*GeneratedField, config.Config) generator.GenerationFunction +type SliceFunctionHolderFunc func(*GeneratedField, config.Config, DefaultGenerationFunctions) generator.GenerationFunction type SliceFunctionHolder struct { BaseFunctionHolder - field *GeneratedField + field *GeneratedField + generationFunctions DefaultGenerationFunctions } var _ FunctionHolder = &SliceFunctionHolder{} -func NewSliceFunctionHolder(f SliceFunctionHolderFunc, field *GeneratedField, cfg config.Config) *SliceFunctionHolder { +func NewSliceFunctionHolder(f SliceFunctionHolderFunc, field *GeneratedField, cfg config.Config, generationFunctions DefaultGenerationFunctions) *SliceFunctionHolder { return &SliceFunctionHolder{ BaseFunctionHolder: BaseFunctionHolder{ config: cfg, fun: f, }, - field: field, + field: field, + generationFunctions: generationFunctions, } } @@ -28,13 +32,14 @@ func (c *SliceFunctionHolder) GetFunction() generator.GenerationFunction { if c.generationFunction != nil { return c.generationFunction } - c.generationFunction = c.fun.(SliceFunctionHolderFunc)(c.field, c.config) + c.generationFunction = c.fun.(SliceFunctionHolderFunc)(c.field, c.config, c.generationFunctions) return c.generationFunction } func (c *SliceFunctionHolder) Copy() FunctionHolder { - return &NumberFunctionHolder{ - BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + return &SliceFunctionHolder{ + BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + generationFunctions: c.generationFunctions.Copy(reflect.Slice), } } diff --git a/generator/core/slice.go b/generator/core/slice.go index 9a45454..cb3bfc4 100644 --- a/generator/core/slice.go +++ b/generator/core/slice.go @@ -8,7 +8,7 @@ import ( "github.com/MartinSimango/dstruct/generator/config" ) -func GenerateSliceFunc(field *GeneratedField, config config.Config) generator.GenerationFunction { +func GenerateSliceFunc(field *GeneratedField, config config.Config, generationFunctions DefaultGenerationFunctions) generator.GenerationFunction { return &coreGenerationFunction{ _func: func(parameters ...any) any { @@ -25,11 +25,13 @@ func GenerateSliceFunc(field *GeneratedField, config config.Config) generator.Ge for i := 0; i < len; i++ { elemValue := reflect.ValueOf(sliceElement.Interface()).Elem() + fieldConfig := NewGenerateFieldConfig(field.GenerationFunctions[reflect.Slice].GetConfig(), field.GenerationValueConfig) + fieldConfig.GenerationFunctions = generationFunctions newField := &GeneratedField{ Name: fmt.Sprintf("%s#%d", field.Name, i), Value: elemValue, Tag: field.Tag, - GeneratedFieldConfig: NewGenerateFieldConfig(field.GenerationFunctions[reflect.Slice].GetConfig(), field.GenerationValueConfig), + GeneratedFieldConfig: fieldConfig, Parent: field, } @@ -41,6 +43,7 @@ func GenerateSliceFunc(field *GeneratedField, config config.Config) generator.Ge }, args: []any{field}, + kind: reflect.Slice, } } diff --git a/generator/core/string.go b/generator/core/string.go index ea4a5a6..2732291 100644 --- a/generator/core/string.go +++ b/generator/core/string.go @@ -1,6 +1,8 @@ package core import ( + "reflect" + "github.com/MartinSimango/dstruct/generator" "github.com/takahiromiyamoto/go-xeger" ) @@ -16,5 +18,6 @@ func GenerateStringFromRegexFunc(regex string) generator.GenerationFunction { return x.Generate() }, args: []any{regex}, + kind: reflect.Slice, } } diff --git a/generator/core/struct.go b/generator/core/struct.go index 9fba3df..ca4990c 100644 --- a/generator/core/struct.go +++ b/generator/core/struct.go @@ -1,6 +1,8 @@ package core import ( + "reflect" + "github.com/MartinSimango/dstruct/generator" ) @@ -13,6 +15,7 @@ func GenerateStructFunc(field *GeneratedField) generator.GenerationFunction { return field.Value.Interface() }, args: []any{field}, + kind: reflect.Struct, } } diff --git a/generator/functions.go b/generator/functions.go deleted file mode 100644 index 274b215..0000000 --- a/generator/functions.go +++ /dev/null @@ -1,42 +0,0 @@ -package generator - -// var _ GenerationFunction = &generateNumberFunc[int]{} - -// func (gn generateNumberFunc[N]) Copy(newConfig *FunctionGenerationConfig) GenerationFunction { -// return GenerateNumberFunc(*gn.args[0].(*N), *gn.args[1].(*N)).SetGenerationConfig(newConfig) -// } -// func assign[T any](configMin, configMax *T, min, max T) (*T, *T) { -// *configMin = min -// *configMax = max -// return configMin, configMax -// } - -// func (gf *generateNumberFunc[n]) GetGenerationConfig() *FunctionGenerationConfig { -// return gf.FunctionGenerationConfig -// } - -// func (gf *generateNumberFunc[n]) SetGenerationConfig(generationConfig *FunctionGenerationConfig) GenerationFunction { -// min, max := *gf.args[0].(*n), *gf.args[1].(*n) -// paramKind := reflect.ValueOf(min).Kind() -// var param_1, param_2 any -// switch paramKind { -// case reflect.Int: -// param_1, param_2 = assign(&generationConfig.intMin, &generationConfig.intMax, int(min), int(max)) -// case reflect.Int32: -// param_1, param_2 = assign(&generationConfig.int32Min, &generationConfig.int32Max, int32(min), int32(max)) -// case reflect.Int64: -// param_1, param_2 = assign(&generationConfig.int64Min, &generationConfig.int64Max, int64(min), int64(max)) -// case reflect.Float32: -// param_1, param_2 = assign(&generationConfig.float32Min, &generationConfig.float32Max, float32(min), float32(max)) -// case reflect.Float64: -// param_1, param_2 = assign(&generationConfig.float64Min, &generationConfig.float64Max, float64(min), float64(max)) -// default: -// panic(fmt.Sprintf("Invalid number type: %s", paramKind)) -// } -// gf.args = []any{param_1, param_2} -// return gf -// } - -// func (g generateStructFunc) Copy(newConfig *FunctionGenerationConfig) GenerationFunction { -// return GenerateStructFunc(g.field) -// } diff --git a/generator/generated.field.go b/generator/generated.field.go deleted file mode 100644 index 634271f..0000000 --- a/generator/generated.field.go +++ /dev/null @@ -1,205 +0,0 @@ -package generator - -// import ( -// "fmt" -// "reflect" -// "strconv" -// ) - -// // type recursiveField struct { -// // Parent GeneratedField -// // PointerValue reflect.Value -// // } - -// // // FunctionSpy - -// // type GenerationFunctionV1 struct { -// // Config Generation -// // Function GenerationFunction -// // } - -// // type GenerationFieldV1 struct { -// // Name string -// // Value reflect.Value -// // Tag reflect.StructTag - -// // GenerationFunction Function -// // RecursiveField *recursiveField -// // } - -// // type GenerateFieldV1 struct { -// // Name string -// // Value reflect.Value -// // Tag reflect.StructTag -// // Config config. - -// // } - -// type GenerationFieldV1[ConfigType any] struct { -// Name string -// Value reflect.Value -// Tag reflect.StructTag -// FunctionConfig ConfigType -// } - -// type GeneratedField struct { -// Name string -// Value reflect.Value -// Tag reflect.StructTag -// Generator *Generator -// Parent *GeneratedField -// PointerValue *reflect.Value -// } - -// func NewGeneratedField(fqn string, -// value reflect.Value, -// tag reflect.StructTag, -// generator *Generator) *GeneratedField { -// g := &GeneratedField{ -// Name: fqn, -// Value: value, -// Tag: tag, -// Generator: generator, -// } -// return g -// } - -// func (field *GeneratedField) checkForRecursiveDefinition(fail bool) bool { -// var depth uint = 0 -// var matchedField *GeneratedField -// for parent := field.Parent; parent != nil; parent = parent.Parent { -// if parent.Value.Type() == field.Value.Type() { -// if !field.Generator.GenerationConfig.recursiveDefinition.Allow || fail { -// panic(fmt.Sprintf("github.com/MartinSimango/dstruct/generator: recursive definition found for field `%s` of type %s", parent.Name, parent.Value.Type())) -// } -// depth++ -// if depth == 1 { -// matchedField = parent -// } -// } -// if depth == (field.Generator.GenerationConfig.recursiveDefinition.Count + 1) { -// // fmt.Println(":DF ", field.Name, matchedField.Name, field.Value.Type(), matchedField.Value.Type(), matchedField.Value, depth) -// if matchedField.PointerValue != nil { -// matchedField.PointerValue.SetZero() -// } else { -// matchedField.Value.SetZero() -// } -// return true -// } -// } -// return false - -// } - -// func (field *GeneratedField) SetValue() bool { - -// switch field.Value.Kind() { -// case reflect.Struct: -// if field.checkForRecursiveDefinition(false) { -// return true -// } -// GenerateStructFunc(field).Generate() -// case reflect.Pointer: -// GeneratePointerValueFunc(field).Generate() -// case reflect.Slice: -// if field.checkForRecursiveDefinition(true) { -// return true -// } -// field.Value.Set(reflect.ValueOf(GenerateSliceFunc(field).Generate())) -// case reflect.Interface: -// field.Value.Set(reflect.Zero(field.Value.Type())) -// default: -// field.Value.Set(reflect.ValueOf(field.getGenerationFunction().Generate())) -// } -// return false -// } - -// func (field *GeneratedField) setStructValues() { -// for j := 0; j < field.Value.NumField(); j++ { -// structField := &GeneratedField{ -// Name: field.Name + "." + field.Value.Type().Field(j).Name, -// Value: field.Value.Field(j), -// Tag: field.Value.Type().Field(j).Tag, -// Generator: field.Generator.Copy(), -// Parent: field, -// } -// structField.SetValue() -// } -// } - -// func (field *GeneratedField) getGenerationFunction() GenerationFunction { -// kind := field.Value.Kind() -// tags := field.Tag - -// switch kind { -// case reflect.Slice: -// return GenerateSliceFunc(field) -// case reflect.Struct: -// return GenerateStructFunc(field) -// case reflect.Ptr: -// return GeneratePointerValueFunc(field) -// } - -// if field.Generator.GenerationConfig.valueGenerationType == UseDefaults { -// example, ok := tags.Lookup("example") -// if !ok { -// example, ok = tags.Lookup("default") -// } -// if ok { -// switch kind { -// case reflect.Int: -// v, _ := strconv.Atoi(example) -// return GenerateFixedValueFunc(v) -// case reflect.Int32: -// v, _ := strconv.Atoi(example) -// return GenerateFixedValueFunc(int32(v)) -// case reflect.Int64: -// v, _ := strconv.Atoi(example) -// return GenerateFixedValueFunc(int64(v)) -// case reflect.Float64: -// v, _ := strconv.ParseFloat(example, 64) -// return GenerateFixedValueFunc(float64(v)) -// case reflect.String: -// return GenerateFixedValueFunc(example) -// case reflect.Bool: -// v, _ := strconv.ParseBool(example) -// return GenerateFixedValueFunc(v) -// default: -// fmt.Println("Unsupported types for defaults: ", kind, example) -// } - -// } -// } - -// pattern := tags.Get("pattern") -// if pattern != "" { -// return GenerateStringFromRegexFunc(pattern) -// } - -// format := tags.Get("format") - -// switch format { -// case "date-time": -// return GenerateDateTimeFunc() -// } - -// enum, ok := tags.Lookup("enum") -// if ok { -// numEnums, _ := strconv.Atoi(enum) -// return GenerateFixedValueFunc(tags.Get(fmt.Sprintf("enum_%d", generateNum(0, numEnums-1)+1))) -// } - -// _, ok = tags.Lookup("gen_task") -// if ok { -// taskProperties, err := CreateTaskProperties(field.Name, tags) -// if err != nil { -// panic(fmt.Sprintf("Error decoding gen_task: %s", err.Error())) -// } -// task := GetTask(taskProperties.TaskName) -// if task == nil { -// panic(fmt.Sprintf("Unregistered task %s", taskProperties.TaskName)) -// } -// return task.GenerationFunction(*taskProperties) -// } -// return field.Generator.DefaultGenerationFunctions[kind] -// } diff --git a/generator/generation.function.go b/generator/generation.function.go index 8cb2b34..7d70f07 100644 --- a/generator/generation.function.go +++ b/generator/generation.function.go @@ -1,10 +1,8 @@ package generator +import "reflect" + type GenerationFunction interface { Generate() any - // Copy copies the generation config and returns a copy of it with the same config - // as the origin generation config - // Copy(*GenerationConfig) GenerationFunction - // GetGenerationConfig() *GenerationConfig - // SetGenerationConfig(*GenerationConfig) GenerationFunction + Kind() reflect.Kind } diff --git a/generator/generator.go b/generator/generator.go deleted file mode 100644 index 734e55a..0000000 --- a/generator/generator.go +++ /dev/null @@ -1,64 +0,0 @@ -package generator - -// import ( -// "reflect" -// ) - -// type GenerationFunctionImpl struct { -// *FunctionGenerationConfig -// basicGenerationFunction -// } - -// func (bsf basicGenerationFunction) Generate() any { -// return bsf._func(bsf.args...) -// } - -// func (bsf basicGenerationFunction) Copy(*FunctionGenerationConfig) GenerationFunction { -// return bsf -// } - -// func (bsf basicGenerationFunction) GetGenerationConfig() *FunctionGenerationConfig { -// return nil -// } - -// func (bsf basicGenerationFunction) SetGenerationConfig(*FunctionGenerationConfig) GenerationFunction { -// return bsf -// } - -// type DefaultGenerationFunctionType map[reflect.Kind]GenerationFunction - -// type Generator struct { -// GenerationConfig *FunctionGenerationConfig -// DefaultGenerationFunctions DefaultGenerationFunctionType -// } - -// type GeneratorV1[K reflect.Kind] struct { -// } - -// func NewGenerator(gc *FunctionGenerationConfig) *Generator { - -// defaultGenerationFunctions := make(DefaultGenerationFunctionType) -// defaultGenerationFunctions[reflect.String] = GenerateFixedValueFunc("string") -// defaultGenerationFunctions[reflect.Ptr] = GenerateNilValueFunc() -// defaultGenerationFunctions[reflect.Int] = GenerateNumberFunc(gc.intMin, gc.intMax).SetGenerationConfig(gc) -// defaultGenerationFunctions[reflect.Int64] = GenerateNumberFunc(gc.int64Min, gc.int64Max).SetGenerationConfig(gc) -// defaultGenerationFunctions[reflect.Int32] = GenerateNumberFunc(gc.int32Min, gc.int32Max).SetGenerationConfig(gc) -// defaultGenerationFunctions[reflect.Float64] = GenerateNumberFunc(gc.float64Min, gc.float64Max).SetGenerationConfig(gc) -// defaultGenerationFunctions[reflect.Bool] = GenerateBoolFunc() - -// return &Generator{ -// GenerationConfig: gc, -// DefaultGenerationFunctions: defaultGenerationFunctions, -// } -// } - -// func (gd *Generator) Copy() (generationDefaults *Generator) { -// generationDefaults = &Generator{ -// DefaultGenerationFunctions: make(DefaultGenerationFunctionType), -// GenerationConfig: gd.GenerationConfig.Clone(), -// } -// for k, v := range gd.DefaultGenerationFunctions { -// generationDefaults.DefaultGenerationFunctions[k] = v.Copy(generationDefaults.GenerationConfig) -// } -// return -// } diff --git a/generator/generator.type.go b/generator/generator.type.go deleted file mode 100644 index 1a72137..0000000 --- a/generator/generator.type.go +++ /dev/null @@ -1,53 +0,0 @@ -package generator - -// type GeneratorType interface { -// GetGenerationConfig() *GenerationConfig -// GetGenerationFunction(field *GeneratedField) GenerationFunction -// } - -// type StructGenerator struct { -// DefaultGenerationFunctions DefaultGenerationFunctionType -// GenerationConfig *GenerationConfig -// } - -// func NewStructGenerator(generationConfig *GenerationConfig) *StructGenerator { -// return &StructGenerator{ -// GenerationConfig: generationConfig, -// } -// } - -// func (sg *StructGenerator) GetGenerationFunction(field *GeneratedField) GenerationFunction { -// return GenerateStructFunc(sg.Field, sg.GenerationConfig) -// } - -// func (sg *StructGenerator) GetGenerationConfig() *GenerationConfig { -// return sg.GenerationConfig -// } - -// func (sg *StructGenerator) SetDefaultGenerationFunctions() { - -// } - -// type FieldGenerator struct { -// GeneratorFunction GenerationFunction -// } - -// type ArrayGenerator struct { -// } - -// type SliceGenerator struct { -// GenerationConfig -// } - -// // switch gu.Field.Value.Kind() { -// // case reflect.Slice: -// // return GenerateSliceFunc(gu.Field) -// // case reflect.Struct: -// // return GenerateStructFunc(gu.Field) -// // case reflect.Ptr: -// // if gu.generationConfig.setNonRequiredFields { -// // return GeneratePointerValueFunc(gu.Field) -// // } else { -// // return GenerateNilValueFunc() -// // } -// // } diff --git a/tree.builder.go b/tree.builder.go index 718b92d..acb8c25 100644 --- a/tree.builder.go +++ b/tree.builder.go @@ -170,19 +170,20 @@ func (db *treeBuilderImpl) buildStruct(tree *Node[structField]) any { func (dsb *treeBuilderImpl) addFieldToTree(name string, typ interface{}, pkgPath string, anonymous bool, tag reflect.StructTag, root *Node[structField]) reflect.Type { value := reflect.ValueOf(typ) if !value.IsValid() { - panic(fmt.Sprintf("Cannot determine type of field %s", name)) + panic(fmt.Sprintf("Cannot determine type of field '%s'", name)) } if root.data.numberOfSubFields == nil { root.data.numberOfSubFields = new(int) } else { *root.data.numberOfSubFields++ } - + goType := reflect.TypeOf(value.Interface()) field := &structField{ name: name, value: value, tag: tag, - typ: reflect.TypeOf(value.Interface()), + typ: goType, + goType: goType, pkgPath: pkgPath, anonymous: anonymous, jsonName: strings.Split(tag.Get("json"), ",")[0], @@ -193,6 +194,7 @@ func (dsb *treeBuilderImpl) addFieldToTree(name string, typ interface{}, pkgPath root.AddNode(name, field) + // don't add struct fields if special kind switch value.Kind() { case reflect.Struct: field.typ = dsb.addStructFields(value, root.children[name], 0, anonymous) diff --git a/util/number.go b/util/number.go deleted file mode 100644 index 342af7b..0000000 --- a/util/number.go +++ /dev/null @@ -1,6 +0,0 @@ -package util - -type Number interface { - int | int8 | int16 | int32 | int64 | float32 | float64 | - uint | uint8 | uint16 | uint32 | uint64 | uintptr -} From 6708da89d3276e93764273b9c2cc7ba3afeda046 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Mon, 11 Dec 2023 22:48:55 +0200 Subject: [PATCH 08/31] add support for custom types allowing dates to be generated --- dreflect/dreflect.go | 3 +- generated.struct.go | 79 ++++++++++++------- generator/core/date.go | 9 ++- .../core/default.generation.functions.go | 1 - generator/core/generated.field.go | 23 +++++- generator/core/generation.unit.go | 2 +- generator/core/kind.go | 17 ++++ generator/core/number.go | 1 - generator/core/slice.function.holder.go | 7 +- generator/core/slice.go | 2 + 10 files changed, 102 insertions(+), 42 deletions(-) create mode 100644 generator/core/kind.go diff --git a/dreflect/dreflect.go b/dreflect/dreflect.go index e4bf979..7c87d69 100644 --- a/dreflect/dreflect.go +++ b/dreflect/dreflect.go @@ -82,7 +82,8 @@ func convert(src reflect.Value, dst reflect.Value) reflect.Value { if src.IsNil() { return reflect.Zero(dst.Type()) } - dstSliceType := GetSliceType(dst.Interface()) + + dstSliceType := dst.Type().Elem() newSliceType := getSliceArrayType(src, dstSliceType) newSlice := reflect.MakeSlice(reflect.SliceOf(newSliceType), src.Len(), src.Cap()) diff --git a/generated.struct.go b/generated.struct.go index f045340..879d82a 100644 --- a/generated.struct.go +++ b/generated.struct.go @@ -56,68 +56,88 @@ type GeneratedStructImpl[T any] struct { generatedFieldConfig core.GeneratedFieldConfig config config.Config instance T - excludedTypes map[reflect.Type]bool + customTypes map[reflect.Type]core.FunctionHolder +} + +type CustomType struct { + Value any + FunctionHolder core.FunctionHolder } // var _ GeneratedStruct = &GeneratedStructImpl[int]{} func NewGeneratedStruct[T any](val T) *GeneratedStructImpl[T] { + return NewGeneratedStructWithConfig(val, config.NewConfig()) } func NewGeneratedStructWithConfig[T any](val T, - cfg config.Config) *GeneratedStructImpl[T] { + cfg config.Config, + customTypes ...CustomType) *GeneratedStructImpl[T] { generatedStruct := &GeneratedStructImpl[T]{ DynamicStructModifierImpl: ExtendStruct(val).Build().(*DynamicStructModifierImpl), generatedFields: make(GenerationFields), config: cfg, generatedFieldConfig: core.NewGenerateFieldConfig(cfg, config.DefaultGenerationValueConfig()), - excludedTypes: make(map[reflect.Type]bool), + customTypes: make(map[reflect.Type]core.FunctionHolder), } - generatedStruct.ExcludeType(time.Time{}, core.DefaultDateFunctionHolder(cfg.Date())) + + for _, v := range customTypes { + generatedStruct.addCustomType(v) + } + generatedStruct.addCustomTypes() generatedStruct.populateGeneratedFields(generatedStruct.root) return generatedStruct } -func (gs *GeneratedStructImpl[T]) ExcludeType(val any, function core.FunctionHolder) { - gs.excludedTypes[reflect.TypeOf(val)] = true +func (gs *GeneratedStructImpl[T]) addCustomTypes() { + gs.addCustomType(CustomType{time.Time{}, core.DefaultDateFunctionHolder(gs.config.Date())}) + +} + +func (gs *GeneratedStructImpl[T]) addCustomType(customType CustomType) { + gs.customTypes[reflect.TypeOf(customType.Value)] = customType.FunctionHolder + gs.generatedFieldConfig.GenerationFunctions[customType.FunctionHolder.Kind()] = customType.FunctionHolder + +} + +func (gs *GeneratedStructImpl[T]) createGeneratedField(field *Node[structField], kind reflect.Kind) *core.GeneratedField { + return core.NewGeneratedField(field.data.fqn, + field.data.value, + field.data.tag, + gs.generatedFieldConfig.Copy(kind), + gs.config.Copy(), // TODO account for nil + gs.customTypes, + field.data.goType, + ) } func (gs *GeneratedStructImpl[T]) populateGeneratedFields(node *Node[structField]) { - for _, field := range node.children { + if field.HasChildren() { - if gs.excludedTypes[field.data.goType] { - field.data.value.Set(reflect.ValueOf(time.Now())) + if customType := gs.customTypes[field.data.goType]; customType != nil { + gs.generatedFields[field.data.fqn] = core.NewGenerationUnit(gs.createGeneratedField(field, customType.Kind())) continue } gs.populateGeneratedFields(field) continue } - fieldKind := field.data.value.Kind() - - generatedField := core.NewGeneratedField(field.data.fqn, - field.data.value, - field.data.tag, - gs.generatedFieldConfig.Copy(fieldKind), - gs.config.Copy(), // TODO account for nil - ) - - gs.generatedFields[field.data.fqn] = core.NewGenerationUnit(generatedField) - + gs.generatedFields[field.data.fqn] = core.NewGenerationUnit(gs.createGeneratedField(field, field.data.value.Kind())) } } func (gs *GeneratedStructImpl[T]) Generate() { + gs.generateFields() - v := any(*new(T)) - switch v.(type) { + switch any(*new(T)).(type) { case nil: gs.instance = gs.DynamicStructModifierImpl.Instance().(T) return } + gs.instance = ToType[T](gs.DynamicStructModifierImpl) } @@ -127,12 +147,16 @@ func (gs *GeneratedStructImpl[T]) GenerateAndUpdate() { } func (gs *GeneratedStructImpl[T]) changeChildrenConfig(node *Node[structField], cfg config.Config) { - for _, v := range node.children { - if v.HasChildren() { - gs.changeChildrenConfig(v, cfg) + for _, field := range node.children { + if customType := gs.customTypes[field.data.goType]; customType != nil { + gs.generatedFields[field.data.fqn].GenerationFunctions[customType.Kind()].SetConfig(cfg) continue } - gs.generatedFields[v.data.fqn].GenerationFunctions[v.data.typ.Kind()].SetConfig(cfg) + if field.HasChildren() { + gs.changeChildrenConfig(field, cfg) + continue + } + gs.generatedFields[field.data.fqn].GenerationFunctions[field.data.typ.Kind()].SetConfig(cfg) } } @@ -228,10 +252,9 @@ func ToPointerType[T any](gs DynamicStructModifier) *T { func (gs *GeneratedStructImpl[T]) generateFields() { for k, genFunc := range gs.generatedFields { if err := gs.Set(k, genFunc.Generate()); err != nil { - fmt.Println("E", err) + fmt.Println(err) } } - } func (gs *GeneratedStructImpl[T]) Instance() T { diff --git a/generator/core/date.go b/generator/core/date.go index b005259..1637d5e 100644 --- a/generator/core/date.go +++ b/generator/core/date.go @@ -1,7 +1,6 @@ package core import ( - "reflect" "time" "github.com/MartinSimango/dstruct/generator" @@ -17,7 +16,7 @@ func GenerateDateTimeFunc() generator.GenerationFunction { _func: func(parameters ...any) any { return time.Now().UTC().Format(time.RFC3339) }, - kind: reflect.String, + kind: NewKind(time.Time{}), } } @@ -27,9 +26,11 @@ func GenerateDateTimeBetweenDatesFunc(dc config.DateConfig) generator.Generation // TODO have a proper implementation return &coreGenerationFunction{ _func: func(parameters ...any) any { - return time.Now().UTC().Format(time.RFC3339) + timeDiffInSeconds := dc.GetDateEnd().Sub(dc.GetDateStart()).Seconds() + return dc.GetDateStart().Add(time.Second * time.Duration(generateNum(0, timeDiffInSeconds))) + }, - kind: reflect.String, + kind: NewKind(time.Time{}), } } diff --git a/generator/core/default.generation.functions.go b/generator/core/default.generation.functions.go index 95671f3..cd4b56f 100644 --- a/generator/core/default.generation.functions.go +++ b/generator/core/default.generation.functions.go @@ -45,7 +45,6 @@ func NewDefaultGenerationFunctions(cfg config.Config) DefaultGenerationFunctions defaultGenerationFunctions[reflect.Float64] = NewGenerateNumberFunctionHolder[float64](cfg.Number()) defaultGenerationFunctions[reflect.Bool] = NewFunctionHolderNoArgs(GenerateBoolFunc()) defaultGenerationFunctions[reflect.Ptr] = NewFunctionHolderNoArgs(GenerateNilValueFunc()) - return defaultGenerationFunctions } diff --git a/generator/core/generated.field.go b/generator/core/generated.field.go index 7d4b8b2..e964bf2 100644 --- a/generator/core/generated.field.go +++ b/generator/core/generated.field.go @@ -41,21 +41,29 @@ type GeneratedField struct { GeneratedFieldConfig Parent *GeneratedField PointerValue *reflect.Value + customTypes map[reflect.Type]FunctionHolder + goType reflect.Type } func NewGeneratedField(fqn string, value reflect.Value, tag reflect.StructTag, generatedFieldConfig GeneratedFieldConfig, - config config.Config) *GeneratedField { + config config.Config, + customTypes map[reflect.Type]FunctionHolder, + goType reflect.Type, + +) *GeneratedField { generateField := &GeneratedField{ Name: fqn, Value: value, Tag: tag, GeneratedFieldConfig: generatedFieldConfig, + customTypes: customTypes, + goType: goType, } if value.Kind() == reflect.Slice { - generatedFieldConfig.GenerationFunctions[value.Kind()] = + generatedFieldConfig.GenerationFunctions[reflect.Slice] = NewSliceFunctionHolder(GenerateSliceFunc, generateField, config, generateField.GenerationFunctions) } @@ -90,6 +98,10 @@ func (field *GeneratedField) checkForRecursiveDefinition(fail bool) bool { } func (field *GeneratedField) SetValue() bool { + if customType := field.customTypes[field.goType]; customType != nil { + field.Value.Set(reflect.ValueOf(customType.GetFunction().Generate())) + return false + } kind := field.Value.Kind() switch kind { case reflect.Struct: @@ -121,6 +133,8 @@ func (field *GeneratedField) setStructValues() { Tag: field.Value.Type().Field(j).Tag, GeneratedFieldConfig: field.GeneratedFieldConfig.Copy(field.Value.Field(j).Kind()), Parent: field, + customTypes: field.customTypes, + goType: field.Value.Field(j).Type(), } structField.SetValue() } @@ -128,6 +142,10 @@ func (field *GeneratedField) setStructValues() { func (field *GeneratedField) getGenerationFunction() generator.GenerationFunction { + if field.customTypes[field.goType] != nil { + return field.customTypes[field.goType].GetFunction() + } + kind := field.Value.Kind() tags := field.Tag @@ -176,6 +194,7 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio format := tags.Get("format") switch format { + // TODO replace or remove this case "date-time": return GenerateDateTimeFunc() } diff --git a/generator/core/generation.unit.go b/generator/core/generation.unit.go index fc37e2a..721cd17 100644 --- a/generator/core/generation.unit.go +++ b/generator/core/generation.unit.go @@ -42,7 +42,7 @@ func (gu *GenerationUnit) Generate() any { return gu.latestValue } -// When did does a new fuc +// TODO when does config need to be changed? func (gu *GenerationUnit) configChanged(previousConfig config.GenerationValueConfig) bool { diff --git a/generator/core/kind.go b/generator/core/kind.go new file mode 100644 index 0000000..3f68a4b --- /dev/null +++ b/generator/core/kind.go @@ -0,0 +1,17 @@ +package core + +import "reflect" + +var customKind map[reflect.Type]*reflect.Kind = make(map[reflect.Type]*reflect.Kind) +var latestKind = reflect.UnsafePointer + +func NewKind(val any) reflect.Kind { + customKindType := reflect.TypeOf(val) + if customKind[customKindType] == nil { + customKind[customKindType] = new(reflect.Kind) + latestKind++ + *customKind[customKindType] = latestKind + return latestKind + } + return *customKind[customKindType] +} diff --git a/generator/core/number.go b/generator/core/number.go index 22f5601..4ce550d 100644 --- a/generator/core/number.go +++ b/generator/core/number.go @@ -20,7 +20,6 @@ func GenerateNumberFunc[n config.Number](cfg config.NumberConfig) generator.Gene return generateNum(*min, *max) }, kind: reflect.ValueOf(*new(n)).Kind(), - args: []any{min, max}, } } diff --git a/generator/core/slice.function.holder.go b/generator/core/slice.function.holder.go index 1ab14a4..2f6fd2b 100644 --- a/generator/core/slice.function.holder.go +++ b/generator/core/slice.function.holder.go @@ -1,8 +1,6 @@ package core import ( - "reflect" - "github.com/MartinSimango/dstruct/generator" "github.com/MartinSimango/dstruct/generator/config" ) @@ -39,7 +37,8 @@ func (c *SliceFunctionHolder) GetFunction() generator.GenerationFunction { func (c *SliceFunctionHolder) Copy() FunctionHolder { return &SliceFunctionHolder{ - BaseFunctionHolder: c.BaseFunctionHolder.Copy(), - generationFunctions: c.generationFunctions.Copy(reflect.Slice), + BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + // TODO address this + // generationFunctions: c.generationFunctions.Copy(reflect.Slice), } } diff --git a/generator/core/slice.go b/generator/core/slice.go index cb3bfc4..56d68a8 100644 --- a/generator/core/slice.go +++ b/generator/core/slice.go @@ -33,6 +33,8 @@ func GenerateSliceFunc(field *GeneratedField, config config.Config, generationFu Tag: field.Tag, GeneratedFieldConfig: fieldConfig, Parent: field, + customTypes: field.customTypes, + goType: elemValue.Type(), } newField.SetValue() From 779983894ff55c7372ba57865da5354e4b895566 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Mon, 24 Jun 2024 19:30:09 +0200 Subject: [PATCH 09/31] Add more documentation --- dreflect/dreflect.go | 26 ++++++++++------ field.go | 28 ++++++++--------- generated.struct.go | 70 +++++++++++++++++++++++++++--------------- generator/core/kind.go | 11 +++++-- limitations/README.md | 2 +- merger.go | 6 ++-- modifier.go | 4 +-- tree.builder.go | 14 ++++----- 8 files changed, 97 insertions(+), 64 deletions(-) diff --git a/dreflect/dreflect.go b/dreflect/dreflect.go index 7c87d69..9b2b379 100644 --- a/dreflect/dreflect.go +++ b/dreflect/dreflect.go @@ -6,36 +6,42 @@ import ( "unsafe" ) -func GetPointerOfValueType(str any) any { - ptr := reflect.New(reflect.ValueOf(str).Type()) - ptr.Elem().Set(reflect.ValueOf(str)) +// GetPointerOfValueType returns a pointer to a new value of the same type as the input value `val`. +// The new value is initialized to the input value. +func GetPointerOfValueType(val any) any { + ptr := reflect.New(reflect.ValueOf(val).Type()) + ptr.Elem().Set(reflect.ValueOf(val)) return ptr.Interface() } +// GetUnderlyingPointerValue dereferences the input pointer `ptr` and returns the value it points to. func GetUnderlyingPointerValue(ptr any) any { return reflect.ValueOf(ptr).Elem().Interface() } +// GetSliceType returns the element type of the input slice `slice`. func GetSliceType(slice any) reflect.Type { return reflect.TypeOf(slice).Elem() } +// ConvertToType converts the input value `val` to the specified type `T` and returns it. func ConvertToType[T any](val any) T { return Convert(reflect.ValueOf(val), reflect.TypeOf(*new(T))).Interface().(T) } -// Convert extends the reflect.Convert function an proceeds to convert subtypes -func Convert(value reflect.Value, t reflect.Type) reflect.Value { +// Convert converts the input reflect.Value `val` to the specified reflect.Type `typ` and returns the result. +// Convert extends the functionality of the reflect.Convert function by also convert subtypes. +func Convert(val reflect.Value, typ reflect.Type) reflect.Value { defer func() { if r := recover(); r != nil { - panic(fmt.Sprintf("dreflect.Convert: value of type %v cannot be converted to type %v", value.Type(), t)) + panic(fmt.Sprintf("dreflect.Convert: value of type %v cannot be converted to type %v", val.Type(), typ)) } }() - dst := reflect.New(t).Elem() - if value.Type().ConvertibleTo(t) { - return value.Convert(t) + dst := reflect.New(typ).Elem() + if val.Type().ConvertibleTo(typ) { + return val.Convert(typ) } - return convert(value, dst) + return convert(val, dst) } func convertibleTo(src, dst reflect.Type) bool { diff --git a/field.go b/field.go index e370b81..b6d401d 100644 --- a/field.go +++ b/field.go @@ -7,18 +7,18 @@ import ( ) type structField struct { - name string - tag reflect.StructTag - value reflect.Value - typ reflect.Type - goType reflect.Type - pkgPath string - anonymous bool - jsonName string - ptrDepth int - fqn string - structIndex *int - numberOfSubFields *int + name string + tag reflect.StructTag + value reflect.Value + typ reflect.Type + goType reflect.Type + pkgPath string + anonymous bool + jsonName string + ptrDepth int + fullyQualifiedName string + structIndex *int + numberOfSubFields *int } func (f structField) GetFieldName() string { @@ -33,8 +33,8 @@ func (f structField) GetType() reflect.Type { return f.typ } -func (f structField) GetFieldFQName() string { - return f.fqn +func (f structField) GetFieldFullyQualifiedName() string { + return f.fullyQualifiedName } func (f structField) GetTag(t string) string { diff --git a/generated.struct.go b/generated.struct.go index 879d82a..27cdd05 100644 --- a/generated.struct.go +++ b/generated.struct.go @@ -67,19 +67,22 @@ type CustomType struct { // var _ GeneratedStruct = &GeneratedStructImpl[int]{} func NewGeneratedStruct[T any](val T) *GeneratedStructImpl[T] { - return NewGeneratedStructWithConfig(val, config.NewConfig()) } func NewGeneratedStructWithConfig[T any](val T, cfg config.Config, - customTypes ...CustomType) *GeneratedStructImpl[T] { + customTypes ...CustomType, +) *GeneratedStructImpl[T] { generatedStruct := &GeneratedStructImpl[T]{ DynamicStructModifierImpl: ExtendStruct(val).Build().(*DynamicStructModifierImpl), generatedFields: make(GenerationFields), config: cfg, - generatedFieldConfig: core.NewGenerateFieldConfig(cfg, config.DefaultGenerationValueConfig()), - customTypes: make(map[reflect.Type]core.FunctionHolder), + generatedFieldConfig: core.NewGenerateFieldConfig( + cfg, + config.DefaultGenerationValueConfig(), + ), + customTypes: make(map[reflect.Type]core.FunctionHolder), } for _, v := range customTypes { @@ -92,17 +95,20 @@ func NewGeneratedStructWithConfig[T any](val T, func (gs *GeneratedStructImpl[T]) addCustomTypes() { gs.addCustomType(CustomType{time.Time{}, core.DefaultDateFunctionHolder(gs.config.Date())}) - } func (gs *GeneratedStructImpl[T]) addCustomType(customType CustomType) { gs.customTypes[reflect.TypeOf(customType.Value)] = customType.FunctionHolder + // the function holder kind is the find that the function retrusn which could either be an existing kind or a new kind i.ie time.Time would be a new kind + // idea: GenerationsFunction key should be type of CustomKind and not reflect.Kind gs.generatedFieldConfig.GenerationFunctions[customType.FunctionHolder.Kind()] = customType.FunctionHolder - } -func (gs *GeneratedStructImpl[T]) createGeneratedField(field *Node[structField], kind reflect.Kind) *core.GeneratedField { - return core.NewGeneratedField(field.data.fqn, +func (gs *GeneratedStructImpl[T]) createGeneratedField( + field *Node[structField], + kind reflect.Kind, +) *core.GeneratedField { + return core.NewGeneratedField(field.data.fullyQualifiedName, field.data.value, field.data.tag, gs.generatedFieldConfig.Copy(kind), @@ -117,19 +123,21 @@ func (gs *GeneratedStructImpl[T]) populateGeneratedFields(node *Node[structField if field.HasChildren() { if customType := gs.customTypes[field.data.goType]; customType != nil { - gs.generatedFields[field.data.fqn] = core.NewGenerationUnit(gs.createGeneratedField(field, customType.Kind())) + gs.generatedFields[field.data.fullyQualifiedName] = core.NewGenerationUnit( + gs.createGeneratedField(field, customType.Kind()), + ) continue } gs.populateGeneratedFields(field) continue } - gs.generatedFields[field.data.fqn] = core.NewGenerationUnit(gs.createGeneratedField(field, field.data.value.Kind())) + gs.generatedFields[field.data.fullyQualifiedName] = core.NewGenerationUnit( + gs.createGeneratedField(field, field.data.value.Kind()), + ) } - } func (gs *GeneratedStructImpl[T]) Generate() { - gs.generateFields() switch any(*new(T)).(type) { @@ -149,16 +157,15 @@ func (gs *GeneratedStructImpl[T]) GenerateAndUpdate() { func (gs *GeneratedStructImpl[T]) changeChildrenConfig(node *Node[structField], cfg config.Config) { for _, field := range node.children { if customType := gs.customTypes[field.data.goType]; customType != nil { - gs.generatedFields[field.data.fqn].GenerationFunctions[customType.Kind()].SetConfig(cfg) + gs.generatedFields[field.data.fullyQualifiedName].GenerationFunctions[customType.Kind()].SetConfig(cfg) continue } if field.HasChildren() { gs.changeChildrenConfig(field, cfg) continue } - gs.generatedFields[field.data.fqn].GenerationFunctions[field.data.typ.Kind()].SetConfig(cfg) + gs.generatedFields[field.data.fullyQualifiedName].GenerationFunctions[field.data.typ.Kind()].SetConfig(cfg) } - } func (gs *GeneratedStructImpl[T]) SetFieldGenerationConfig(field string, cfg config.Config) error { @@ -180,15 +187,16 @@ func (gs *GeneratedStructImpl[T]) SetFieldGenerationConfig(field string, cfg con } func (gs *GeneratedStructImpl[T]) SetFieldGenerationFunction(field string, - functionHolder core.FunctionHolder) { - + functionHolder core.FunctionHolder, +) { // kind := gs.fieldNodeMap[field].data.GetType().Kind() // _ = functionHolder.(*core.FunctionHolderWithNoArgs) gs.generatedFields[field].GeneratedField.GenerationFunctions[functionHolder.Kind()] = functionHolder } func (gs *GeneratedStructImpl[T]) SetFieldGenerationFunctions(field string, - defaultGenerationFunctions core.DefaultGenerationFunctions) { + defaultGenerationFunctions core.DefaultGenerationFunctions, +) { gs.generatedFields[field].GenerationFunctions = defaultGenerationFunctions } @@ -201,7 +209,10 @@ func (gs *GeneratedStructImpl[T]) SetGenerationConfig(config config.Config) { } } -func (gs *GeneratedStructImpl[T]) SetFieldGenerationValueConfig(field string, config config.GenerationValueConfig) { +func (gs *GeneratedStructImpl[T]) SetFieldGenerationValueConfig( + field string, + config config.GenerationValueConfig, +) { gs.generatedFields[field].GenerationValueConfig = config } @@ -215,7 +226,9 @@ func (gs *GeneratedStructImpl[T]) SetGenerationValueConfig(config config.Generat } // GetFieldValueGenerationConfig implements GeneratedStruct. -func (gs *GeneratedStructImpl[T]) GetFieldValueGenerationConfig(field string) config.GenerationValueConfig { +func (gs *GeneratedStructImpl[T]) GetFieldValueGenerationConfig( + field string, +) config.GenerationValueConfig { return gs.generatedFields[field].GenerationValueConfig } @@ -230,15 +243,24 @@ func (gs *GeneratedStructImpl[T]) GetValueGenerationConfig() config.GenerationVa return gs.generatedFieldConfig.GenerationValueConfig } -func (gs *GeneratedStructImpl[T]) SetFieldFromTask(field string, task generator.Task, params ...any) error { - taskProperties, err := generator.CreateTaskProperties(field, generator.GetTagForTask(generator.TaskName(task.Name()), params...)) +func (gs *GeneratedStructImpl[T]) SetFieldFromTask( + field string, + task generator.Task, + params ...any, +) error { + taskProperties, err := generator.CreateTaskProperties( + field, + generator.GetTagForTask(generator.TaskName(task.Name()), params...), + ) if err != nil { return err } - gs.SetFieldGenerationFunction(field, core.NewFunctionHolderNoArgs(task.GenerationFunction(*taskProperties))) + gs.SetFieldGenerationFunction( + field, + core.NewFunctionHolderNoArgs(task.GenerationFunction(*taskProperties)), + ) return nil - } func ToType[T any](gs DynamicStructModifier) T { diff --git a/generator/core/kind.go b/generator/core/kind.go index 3f68a4b..0b2e06f 100644 --- a/generator/core/kind.go +++ b/generator/core/kind.go @@ -1,10 +1,15 @@ package core -import "reflect" +import ( + "reflect" +) -var customKind map[reflect.Type]*reflect.Kind = make(map[reflect.Type]*reflect.Kind) -var latestKind = reflect.UnsafePointer +var ( + customKind map[reflect.Type]*reflect.Kind = make(map[reflect.Type]*reflect.Kind) + latestKind = reflect.UnsafePointer +) +// NewKind returns a new reflect.Kind for a custom type func NewKind(val any) reflect.Kind { customKindType := reflect.TypeOf(val) if customKind[customKindType] == nil { diff --git a/limitations/README.md b/limitations/README.md index 3d2b426..ea5b8e1 100644 --- a/limitations/README.md +++ b/limitations/README.md @@ -56,6 +56,6 @@ func main() { } -* Generated structs cannot generate slice fields whose types have recursive definition. +* Generated structs cannot generate slice or array fields whose types have recursive definition. ``` \ No newline at end of file diff --git a/merger.go b/merger.go index 7278c33..f730819 100644 --- a/merger.go +++ b/merger.go @@ -49,17 +49,17 @@ func mergeStructs(left, right Builder, parentKind reflect.Kind) (any, error) { newStruct.AddField(elementName, field.data.value.Interface(), string(field.data.tag)) continue } - if err := validateTypes(field.data.value, cV.data.value, field.data.fqn); err != nil { + if err := validateTypes(field.data.value, cV.data.value, field.data.fullyQualifiedName); err != nil { return nil, err } if field.data.value.Kind() == reflect.Slice { vSliceType := dreflect.GetSliceType(field.data.value.Interface()) cVSliceType := dreflect.GetSliceType(cV.data.value.Interface()) - if err := validateSliceTypes(vSliceType, cVSliceType, field.data.value, cV.data.value, field.data.fqn); err != nil { + if err := validateSliceTypes(vSliceType, cVSliceType, field.data.value, cV.data.value, field.data.fullyQualifiedName); err != nil { return nil, err } - newStruct.RemoveField(field.data.fqn) + newStruct.RemoveField(field.data.fullyQualifiedName) if cVSliceType.Kind() == reflect.Struct { newSliceTypeStruct, err := mergeStructs(left.GetField(name), right.GetField(name), reflect.Slice) diff --git a/modifier.go b/modifier.go index 06deddc..5970e7d 100644 --- a/modifier.go +++ b/modifier.go @@ -65,8 +65,8 @@ func newStruct(strct any, rootNode *Node[structField]) *DynamicStructModifierImp func (dm *DynamicStructModifierImpl) createFieldToNodeMappings(rootNode *Node[structField]) { for _, field := range rootNode.children { - dm.fieldNodeMap[field.data.fqn] = field - dm.fieldData[field.data.fqn] = *field.data + dm.fieldNodeMap[field.data.fullyQualifiedName] = field + dm.fieldData[field.data.fullyQualifiedName] = *field.data dm.createFieldToNodeMappings(field) } diff --git a/tree.builder.go b/tree.builder.go index acb8c25..0da16f7 100644 --- a/tree.builder.go +++ b/tree.builder.go @@ -20,9 +20,9 @@ var _ Builder = &treeBuilderImpl{} func createRoot() *Node[structField] { return &Node[structField]{ data: &structField{ - name: "#root", - value: reflect.ValueOf(nil), - fqn: "#root", + name: "#root", + value: reflect.ValueOf(nil), + fullyQualifiedName: "#root", }, children: make(map[string]*Node[structField]), } @@ -76,7 +76,7 @@ func newBuilderFromNode(node *Node[structField], resetFQN bool) *treeBuilderImpl func resetNodeFieldsFQN(node *Node[structField]) *Node[structField] { for _, v := range node.children { - v.data.fqn = getFQN(node.data.name, v.data.name) + v.data.fullyQualifiedName = getFQN(node.data.name, v.data.name) resetNodeFieldsFQN(v) } return node @@ -119,7 +119,7 @@ func (dsb *treeBuilderImpl) GetField(field string) Builder { func (dsb *treeBuilderImpl) NewBuilderFromField(field string) Builder { copyNode := dsb.getNode(field).Copy() copyNode.data.name = "#root" - copyNode.data.fqn = "#root" + copyNode.data.fullyQualifiedName = "#root" return newBuilderFromNode(copyNode, true) } @@ -147,7 +147,7 @@ func (db *treeBuilderImpl) Build() DynamicStructModifier { // Ensure that the current node is treated is root when struct is built if db.root.parent != nil { rootCopy.data.name = "#root" - rootCopy.data.fqn = "#root" + rootCopy.data.fullyQualifiedName = "#root" rootCopy = resetNodeFieldsFQN(rootCopy) } return newStruct(db.buildStruct(rootCopy), rootCopy) @@ -190,7 +190,7 @@ func (dsb *treeBuilderImpl) addFieldToTree(name string, typ interface{}, pkgPath } field.structIndex = new(int) *field.structIndex = *root.data.numberOfSubFields - field.fqn = getFQN(root.data.GetFieldFQName(), field.name) + field.fullyQualifiedName = getFQN(root.data.GetFieldFullyQualifiedName(), field.name) root.AddNode(name, field) From a060bc22cda40a248e705d570b7a2e62abf07bfd Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Tue, 25 Jun 2024 11:52:36 +0200 Subject: [PATCH 10/31] Document the config package --- .vscode/settings.json | 1 + generated.struct.go | 2 +- generator/config/config.builder.go | 41 ---- generator/config/config.go | 183 ++++------------ generator/config/date.go | 69 ------ generator/config/date.range.go | 30 +++ generator/config/dstruct.config.builder.go | 53 +++++ generator/config/dstruct.config.go | 132 ++++++++++++ generator/config/dstruct.date.range.config.go | 60 ++++++ .../config/dstruct.number.range.config.go | 111 ++++++++++ generator/config/dstruct.slice.config.go | 41 ++++ generator/config/generation.value.config.go | 38 ++++ generator/config/number.config.go | 202 ------------------ generator/config/number.range.config.go | 96 +++++++++ generator/config/slice.config.go | 44 +--- generator/core/date.go | 14 +- generator/core/number.function.holder.go | 6 +- generator/core/number.go | 6 +- 18 files changed, 621 insertions(+), 508 deletions(-) delete mode 100644 generator/config/config.builder.go delete mode 100644 generator/config/date.go create mode 100644 generator/config/date.range.go create mode 100644 generator/config/dstruct.config.builder.go create mode 100644 generator/config/dstruct.config.go create mode 100644 generator/config/dstruct.date.range.config.go create mode 100644 generator/config/dstruct.number.range.config.go create mode 100644 generator/config/dstruct.slice.config.go create mode 100644 generator/config/generation.value.config.go delete mode 100644 generator/config/number.config.go create mode 100644 generator/config/number.range.config.go diff --git a/.vscode/settings.json b/.vscode/settings.json index a11edd7..c946a44 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ + "Dstruct", "Unexported" ] } \ No newline at end of file diff --git a/generated.struct.go b/generated.struct.go index 27cdd05..1006fc9 100644 --- a/generated.struct.go +++ b/generated.struct.go @@ -67,7 +67,7 @@ type CustomType struct { // var _ GeneratedStruct = &GeneratedStructImpl[int]{} func NewGeneratedStruct[T any](val T) *GeneratedStructImpl[T] { - return NewGeneratedStructWithConfig(val, config.NewConfig()) + return NewGeneratedStructWithConfig(val, config.NewDstructConfig()) } func NewGeneratedStructWithConfig[T any](val T, diff --git a/generator/config/config.builder.go b/generator/config/config.builder.go deleted file mode 100644 index b7edd64..0000000 --- a/generator/config/config.builder.go +++ /dev/null @@ -1,41 +0,0 @@ -package config - -type ConfigBuilder interface { - WithNumberConfig(NumberConfig) ConfigBuilder - WithSliceConfig(SliceConfig) ConfigBuilder - WithDateConfig(DateConfig) ConfigBuilder - Build() *ConfigImpl -} - -type ConfigBuilderImpl struct { - config *ConfigImpl -} - -var _ ConfigBuilder = &ConfigBuilderImpl{} - -func NewConfigBuilder() *ConfigBuilderImpl { - return &ConfigBuilderImpl{ - config: &ConfigImpl{}, - } -} - -// WithSliceConfig implements ConfigBuilder. -func (cb *ConfigBuilderImpl) WithNumberConfig(nc NumberConfig) ConfigBuilder { - cb.config.NumberConfig = nc - return cb -} - -// WithSliceConfig implements ConfigBuilder. -func (cb *ConfigBuilderImpl) WithSliceConfig(sc SliceConfig) ConfigBuilder { - cb.config.SliceConfig = sc - return cb -} - -func (cb *ConfigBuilderImpl) WithDateConfig(dc DateConfig) ConfigBuilder { - cb.config.DateConfig = dc - return cb -} - -func (cb *ConfigBuilderImpl) Build() *ConfigImpl { - return cb.config -} diff --git a/generator/config/config.go b/generator/config/config.go index f1fbbeb..0cdff7d 100644 --- a/generator/config/config.go +++ b/generator/config/config.go @@ -1,169 +1,60 @@ +// Package config provides the configuration for the generation of dynamic structs. package config -type ConfigType uint - -type ValueGenerationType uint8 +// Config represents the generation config for a dynamic struct. +type Config interface { -const ( - Generate ValueGenerationType = iota // will generate all field - GenerateOnce // will generate the fields once - UseDefaults -) + // Number returns the number configuration. + Number() NumberRangeConfig -type RecursiveDefinition struct { - Allow bool - Depth uint -} - -type GenerationValueConfig struct { - ValueGenerationType ValueGenerationType - SetNonRequiredFields bool - RecursiveDefinition RecursiveDefinition -} - -func DefaultGenerationValueConfig() GenerationValueConfig { - return GenerationValueConfig{ - ValueGenerationType: UseDefaults, - SetNonRequiredFields: false, - RecursiveDefinition: RecursiveDefinition{ - Allow: false, - Depth: 1, - }, - } -} + // Slice returns the slice configuration. + Slice() SliceConfig -func NewConfig() *ConfigImpl { - return NewConfigBuilder(). - WithNumberConfig(NewNumberConfig()). - WithSliceConfig(NewSliceConfig()). - WithDateConfig(NewDateConfig()). - Build() -} + // Date returns the date configuration. + Date() DateRangeConfig -type Config interface { - ConfigBuilder - Number() NumberConfig - Slice() SliceConfig - Date() DateConfig + // SetSliceLength sets the length range for the slice. SetSliceLength(min, max int) Config + + // SetIntRange sets the range for int values. SetIntRange(min, max int) Config + + // SetInt8Range sets the range for int8 values. SetInt8Range(min, max int8) Config + + // SetInt16Range sets the range for int16 values. SetInt16Range(min, max int16) Config + + // SetInt32Range sets the range for int32 values. SetInt32Range(min, max int32) Config + + // SetInt64Range sets the range for int64 values. SetInt64Range(min, max int64) Config + + // SetFloat32Range sets the range for float32 values. SetFloat32Range(min, max float32) Config + + // SetFloat64Range sets the range for float64 values. SetFloat64Range(min, max float64) Config + + // SetUIntRange sets the range for uint values. SetUIntRange(min, max uint) Config + + // SetUInt8Range sets the range for uint8 values. SetUInt8Range(min, max uint8) Config + + // SetUInt16Range sets the range for uint16 values. SetUInt16Range(min, max uint16) Config + + // SetUInt32Range sets the range for uint32 values. SetUInt32Range(min, max uint32) Config + + // SetUInt64Range sets the range for the uint64 value. SetUInt64Range(min, max uint64) Config + + // SetUIntPtrRange sets the range for the uintptr value. SetUIntPtr(min, max uintptr) Config + // Copy returns a copy of the configuration. Copy() Config } - -type ConfigImpl struct { - ConfigBuilderImpl - SliceConfig SliceConfig - NumberConfig NumberConfig - DateConfig DateConfig -} - -var _ Config = &ConfigImpl{} - -func (c *ConfigImpl) Copy() Config { - newConfig := &ConfigImpl{} - if c.SliceConfig != nil { - newConfig.SliceConfig = c.SliceConfig.Copy() - } - - if c.NumberConfig != nil { - newConfig.NumberConfig = c.NumberConfig.Copy() - } - - return newConfig - -} - -func (c *ConfigImpl) Slice() SliceConfig { - return c.SliceConfig -} - -func (c *ConfigImpl) Date() DateConfig { - return c.DateConfig - -} - -func (c *ConfigImpl) Number() NumberConfig { - return c.NumberConfig -} - -func (c *ConfigImpl) SetSliceLength(min, max int) Config { - c.SliceConfig.SetLengthRange(min, max) - return c -} - -func (c *ConfigImpl) SetIntRange(min, max int) Config { - c.NumberConfig.Int().SetRange(min, max) - return c -} - -func (c *ConfigImpl) SetInt8Range(min, max int8) Config { - c.NumberConfig.Int8().SetRange(min, max) - return c -} - -func (c *ConfigImpl) SetInt16Range(min, max int16) Config { - c.NumberConfig.Int16().SetRange(min, max) - return c -} - -func (c *ConfigImpl) SetInt32Range(min, max int32) Config { - c.NumberConfig.Int32().SetRange(min, max) - return c -} - -func (c *ConfigImpl) SetInt64Range(min, max int64) Config { - c.NumberConfig.Int64().SetRange(min, max) - return c -} - -func (c *ConfigImpl) SetFloat32Range(min, max float32) Config { - c.NumberConfig.Float32().SetRange(min, max) - return c -} -func (c *ConfigImpl) SetFloat64Range(min, max float64) Config { - c.NumberConfig.Float64().SetRange(min, max) - return c -} - -func (c *ConfigImpl) SetUIntRange(min, max uint) Config { - c.NumberConfig.UInt().SetRange(min, max) - return c -} - -func (c *ConfigImpl) SetUInt8Range(min, max uint8) Config { - c.NumberConfig.UInt8().SetRange(min, max) - return c -} - -func (c *ConfigImpl) SetUInt16Range(min, max uint16) Config { - c.NumberConfig.UInt16().SetRange(min, max) - return c -} - -func (c *ConfigImpl) SetUInt32Range(min, max uint32) Config { - c.NumberConfig.UInt32().SetRange(min, max) - return c -} - -func (c *ConfigImpl) SetUInt64Range(min, max uint64) Config { - c.NumberConfig.UInt64().SetRange(min, max) - return c -} - -func (c *ConfigImpl) SetUIntPtr(min, max uintptr) Config { - c.NumberConfig.UIntPtr().SetRange(min, max) - return c -} diff --git a/generator/config/date.go b/generator/config/date.go deleted file mode 100644 index 03a44c3..0000000 --- a/generator/config/date.go +++ /dev/null @@ -1,69 +0,0 @@ -package config - -import ( - "time" -) - -type DateConfig interface { - SetDateFormat(format string) - GetDateFormat() string - SetDateRange(start time.Time, end time.Time) - SetDateStart(start time.Time) - GetDateStart() time.Time - SetDateEnd(end time.Time) - GetDateEnd() time.Time -} - -type DateConfigImpl struct { - dateFormat string - dateStart time.Time - dateEnd time.Time -} - -var _ DateConfig = &DateConfigImpl{} - -// SetDateStart implements DateConfig. -func (dc *DateConfigImpl) SetDateStart(start time.Time) { - dc.SetDateRange(start, dc.dateEnd) -} - -// SetDateEnd implements DateConfig. -func (dc *DateConfigImpl) SetDateEnd(end time.Time) { - dc.SetDateRange(dc.dateStart, end) -} - -// SetDateFormat implements DateConfig. -func (dc *DateConfigImpl) SetDateFormat(format string) { - dc.dateFormat = format -} - -// SetDateRange implements DateConfig. -func (dc *DateConfigImpl) SetDateRange(start time.Time, end time.Time) { - if start.Before(end) { - dc.dateStart = start - dc.dateEnd = end - } -} - -// GetDateStart implements DateConfig. -func (dc *DateConfigImpl) GetDateStart() time.Time { - return dc.dateStart -} - -// GetDateEnd implements DateConfig. -func (dc *DateConfigImpl) GetDateEnd() time.Time { - return dc.dateEnd -} - -// GetDateFormat implements DateConfig. -func (dc *DateConfigImpl) GetDateFormat() string { - return dc.dateFormat -} - -func NewDateConfig() *DateConfigImpl { - return &DateConfigImpl{ - dateFormat: time.RFC3339, - dateStart: time.Now().Truncate(24 * time.Hour), - dateEnd: time.Now().Truncate(24 * time.Hour).Add(24*time.Hour - time.Nanosecond), - } -} diff --git a/generator/config/date.range.go b/generator/config/date.range.go new file mode 100644 index 0000000..695efb2 --- /dev/null +++ b/generator/config/date.range.go @@ -0,0 +1,30 @@ +package config + +import ( + "time" +) + +// DateRangeConfig defines the configuration for a date range for a dynamic struct. +type DateRangeConfig interface { + + // SetDateFormat sets the date format. + SetDateFormat(format string) + + // GetDateFormat returns the date format. + GetDateFormat() string + + // SetDateRange sets the date range. + SetDateRange(start time.Time, end time.Time) + + // SetStartDate sets the start date for the date range. + SetStartDate(start time.Time) + + // GetStartDate returns the start date for the date range. + GetStartDate() time.Time + + // SetEndDate sets the end date for the date range. + SetEndDate(end time.Time) + + // GetEndDate returns the end date for the date range. + GetEndDate() time.Time +} diff --git a/generator/config/dstruct.config.builder.go b/generator/config/dstruct.config.builder.go new file mode 100644 index 0000000..7ee5b70 --- /dev/null +++ b/generator/config/dstruct.config.builder.go @@ -0,0 +1,53 @@ +package config + +// ConfigBuilder is an interface of a DstructConfig builder. +type DstructConfigBuilder interface { + + // WithNumberConfig sets the NumberConfig in the builder. + WithNumberConfig(numberConfig NumberRangeConfig) DstructConfigBuilder + + // WithSliceConfig sets the SliceConfig in the builder. + WithSliceConfig(sliceConfig SliceConfig) DstructConfigBuilder + + // WithDateRangeConfig sets the DateConfig in the builder. + WithDateRangeConfig(DateRangeConfig DateRangeConfig) DstructConfigBuilder + + // Build builds the configuration object + Build() *DstructConfig +} + +type dstructConfigBuilder struct { + config *DstructConfig +} + +var _ DstructConfigBuilder = &dstructConfigBuilder{} + +// NewDstructConfigBuilder is a constructor for DstructConfigBuilder. +func NewDstructConfigBuilder() *dstructConfigBuilder { + return &dstructConfigBuilder{ + config: &DstructConfig{}, + } +} + +// WithNumberConfig implements ConfigBuilder.WithNumberConfig. +func (dcb *dstructConfigBuilder) WithNumberConfig(nc NumberRangeConfig) DstructConfigBuilder { + dcb.config.NumberConfig = nc + return dcb +} + +// WithSliceConfig implements ConfigBuilder.WithSliceConfig. +func (dcb *dstructConfigBuilder) WithSliceConfig(sc SliceConfig) DstructConfigBuilder { + dcb.config.SliceConfig = sc + return dcb +} + +// WithDateRangeConfig implements ConfigBuilder.WithDateRangeConfig. +func (dcb *dstructConfigBuilder) WithDateRangeConfig(dc DateRangeConfig) DstructConfigBuilder { + dcb.config.DateConfig = dc + return dcb +} + +// Build implements ConfigBuilder.Build. +func (dcb *dstructConfigBuilder) Build() *DstructConfig { + return dcb.config +} diff --git a/generator/config/dstruct.config.go b/generator/config/dstruct.config.go new file mode 100644 index 0000000..64f1702 --- /dev/null +++ b/generator/config/dstruct.config.go @@ -0,0 +1,132 @@ +package config + +// DstructConfig defines the configuration for a dynamic struct. +type DstructConfig struct { + SliceConfig SliceConfig + NumberConfig NumberRangeConfig + DateConfig DateRangeConfig +} + +// NewDstructConfig is a constructor for DstructConfig. +func NewDstructConfig() *DstructConfig { + return NewDstructConfigBuilder(). + WithNumberConfig(NewNumberRangeConfig()). + WithSliceConfig(NewSliceConfig()). + WithDateRangeConfig(NewDstructDateRangeConfig()). + Build() +} + +var _ Config = &DstructConfig{} + +// Copy implements Config. +func (c *DstructConfig) Copy() Config { + newConfig := &DstructConfig{} + if c.SliceConfig != nil { + newConfig.SliceConfig = c.SliceConfig.Copy() + } + + if c.NumberConfig != nil { + newConfig.NumberConfig = c.NumberConfig.Copy() + } + + return newConfig +} + +// Slice implements Config.Slice. +func (c *DstructConfig) Slice() SliceConfig { + return c.SliceConfig +} + +// Date implements Config.Date. +func (c *DstructConfig) Date() DateRangeConfig { + return c.DateConfig +} + +// Number implements Config.Number. +func (c *DstructConfig) Number() NumberRangeConfig { + return c.NumberConfig +} + +// SetSliceLength implements Config.SetSliceLength. +func (c *DstructConfig) SetSliceLength(min, max int) Config { + c.SliceConfig.SetLengthRange(min, max) + return c +} + +// SetIntRange implements Config.SetIntRange. +func (c *DstructConfig) SetIntRange(min, max int) Config { + c.NumberConfig.Int().SetRange(min, max) + return c +} + +// SetInt8Range implements Config.SetInt8Range. +func (c *DstructConfig) SetInt8Range(min, max int8) Config { + c.NumberConfig.Int8().SetRange(min, max) + return c +} + +// SetInt16Range implements Config.SetInt16Range. +func (c *DstructConfig) SetInt16Range(min, max int16) Config { + c.NumberConfig.Int16().SetRange(min, max) + return c +} + +// SetInt32Range implements Config.SetInt32Range. +func (c *DstructConfig) SetInt32Range(min, max int32) Config { + c.NumberConfig.Int32().SetRange(min, max) + return c +} + +// SetInt64Range implements Config.SetInt64Range. +func (c *DstructConfig) SetInt64Range(min, max int64) Config { + c.NumberConfig.Int64().SetRange(min, max) + return c +} + +// SetFloat32Range implements Config.SetFloat32Range. +func (c *DstructConfig) SetFloat32Range(min, max float32) Config { + c.NumberConfig.Float32().SetRange(min, max) + return c +} + +// SetFloat64Range implements Config.SetFloat64Range. +func (c *DstructConfig) SetFloat64Range(min, max float64) Config { + c.NumberConfig.Float64().SetRange(min, max) + return c +} + +// SetUIntRange implements Config.SetUIntRange. +func (c *DstructConfig) SetUIntRange(min, max uint) Config { + c.NumberConfig.UInt().SetRange(min, max) + return c +} + +// SetUInt8Range implements Config.SetUInt8Range. +func (c *DstructConfig) SetUInt8Range(min, max uint8) Config { + c.NumberConfig.UInt8().SetRange(min, max) + return c +} + +// SetUInt16Range implements Config.SetUInt16Range. +func (c *DstructConfig) SetUInt16Range(min, max uint16) Config { + c.NumberConfig.UInt16().SetRange(min, max) + return c +} + +// SetUInt32Range implements Config.SetUInt32Range. +func (c *DstructConfig) SetUInt32Range(min, max uint32) Config { + c.NumberConfig.UInt32().SetRange(min, max) + return c +} + +// SetUInt64Range implements Config.SetUInt64Range. +func (c *DstructConfig) SetUInt64Range(min, max uint64) Config { + c.NumberConfig.UInt64().SetRange(min, max) + return c +} + +// SetUIntPtrRange implements Config.SetUIntPtrRange. +func (c *DstructConfig) SetUIntPtr(min, max uintptr) Config { + c.NumberConfig.UIntPtr().SetRange(min, max) + return c +} diff --git a/generator/config/dstruct.date.range.config.go b/generator/config/dstruct.date.range.config.go new file mode 100644 index 0000000..9e2e963 --- /dev/null +++ b/generator/config/dstruct.date.range.config.go @@ -0,0 +1,60 @@ +package config + +import "time" + +// DstructDateRangeConfig implements the DateRangeConfig interface. +type DstructDateRangeConfig struct { + dateFormat string + startDate time.Time + endDate time.Time +} + +var _ DateRangeConfig = &DstructDateRangeConfig{} + +// SetStartDate implements DateConfig.SetStartDate. +func (dc *DstructDateRangeConfig) SetStartDate(start time.Time) { + dc.SetDateRange(start, dc.endDate) +} + +// SetEndDate implements DateConfig.SetEndDate. +func (dc *DstructDateRangeConfig) SetEndDate(end time.Time) { + dc.SetDateRange(dc.startDate, end) +} + +// SetDateFormat implements DateConfig.SetDateFormat. +func (dc *DstructDateRangeConfig) SetDateFormat(format string) { + dc.dateFormat = format +} + +// SetDateRange implements DateConfig.SetDateRange. +func (dc *DstructDateRangeConfig) SetDateRange(start time.Time, end time.Time) { + if start.Before(end) { + dc.startDate = start + dc.endDate = end + } +} + +// GetStartDate implements DateConfig.GetStartDate. +func (dc *DstructDateRangeConfig) GetStartDate() time.Time { + return dc.startDate +} + +// GetEndDate implements DateConfig.GetEndDate. +func (dc *DstructDateRangeConfig) GetEndDate() time.Time { + return dc.endDate +} + +// GetDateFormat implements DateConfig.GetDateFormat. +func (dc *DstructDateRangeConfig) GetDateFormat() string { + return dc.dateFormat +} + +// NewDstructDateRangeConfig is a constructor for DstructDateRangeConfig. +// It initializes the date format to RFC3339, the start date to the current date truncated to the day and the end date to the next day. +func NewDstructDateRangeConfig() *DstructDateRangeConfig { + return &DstructDateRangeConfig{ + dateFormat: time.RFC3339, + startDate: time.Now().Truncate(24 * time.Hour), + endDate: time.Now().Truncate(24 * time.Hour).Add(24*time.Hour - time.Nanosecond), + } +} diff --git a/generator/config/dstruct.number.range.config.go b/generator/config/dstruct.number.range.config.go new file mode 100644 index 0000000..088e5b0 --- /dev/null +++ b/generator/config/dstruct.number.range.config.go @@ -0,0 +1,111 @@ +package config + +// DstructNumberRangeConfig implements NumberRangeConfig. +type DstructNumberRangeConfig struct { + IntConfig NumberRange[int] + Int8Config NumberRange[int8] + Int16Config NumberRange[int16] + Int32Config NumberRange[int32] + Int64Config NumberRange[int64] + Float32Config NumberRange[float32] + Float64Config NumberRange[float64] + UIntConfig NumberRange[uint] + UInt8Config NumberRange[uint8] + UInt16Config NumberRange[uint16] + UInt32Config NumberRange[uint32] + UInt64Config NumberRange[uint64] + UIntPtrConfig NumberRange[uintptr] +} + +// NewNumberRangeConfig is a constructor for DstructNumberRangeConfig. +func NewNumberRangeConfig() *DstructNumberRangeConfig { + return &DstructNumberRangeConfig{ + IntConfig: numberRange[int](0, 10), + Int8Config: numberRange[int8](0, 10), + Int16Config: numberRange[int16](0, 10), + Int32Config: numberRange[int32](0, 10), + Int64Config: numberRange[int64](0, 10), + Float32Config: numberRange[float32](0, 10), + Float64Config: numberRange[float64](0, 10), + UIntConfig: numberRange[uint](0, 10), + UInt8Config: numberRange[uint8](0, 10), + UInt16Config: numberRange[uint16](0, 10), + UInt32Config: numberRange[uint32](0, 10), + UInt64Config: numberRange[uint64](0, 10), + UIntPtrConfig: numberRange[uintptr](0, 10), + } +} + +var _ NumberRangeConfig = &DstructNumberRangeConfig{} + +// Float32 implements NumberConfig.Float32. +func (nc *DstructNumberRangeConfig) Float32() *NumberRange[float32] { + return &nc.Float32Config +} + +// Float64 implements NumberConfig.Float64. +func (nc *DstructNumberRangeConfig) Float64() *NumberRange[float64] { + return &nc.Float64Config +} + +// Int implements NumberConfig.Int. +func (nc *DstructNumberRangeConfig) Int() *NumberRange[int] { + return &nc.IntConfig +} + +// Int16 implements NumberConfig.Int16. +func (nc *DstructNumberRangeConfig) Int16() *NumberRange[int16] { + return &nc.Int16Config +} + +// Int32 implements NumberConfig.Int32. +func (nc *DstructNumberRangeConfig) Int32() *NumberRange[int32] { + return &nc.Int32Config +} + +// Int64 implements NumberConfig.Int64. +func (nc *DstructNumberRangeConfig) Int64() *NumberRange[int64] { + return &nc.Int64Config +} + +// Int8 implements NumberConfig.Int8. +func (nc *DstructNumberRangeConfig) Int8() *NumberRange[int8] { + return &nc.Int8Config +} + +// UInt implements NumberConfig.UInt. +func (nc *DstructNumberRangeConfig) UInt() *NumberRange[uint] { + return &nc.UIntConfig +} + +// UInt16 implements NumberConfig.UInt16. +func (nc *DstructNumberRangeConfig) UInt16() *NumberRange[uint16] { + return &nc.UInt16Config +} + +// UInt32 implements NumberConfig.UInt32, +func (nc *DstructNumberRangeConfig) UInt32() *NumberRange[uint32] { + return &nc.UInt32Config +} + +// UInt64 implements NumberConfig.UInt64. +func (nc *DstructNumberRangeConfig) UInt64() *NumberRange[uint64] { + return &nc.UInt64Config +} + +// UInt8 implements NumberConfig.UInt8. +func (nc *DstructNumberRangeConfig) UInt8() *NumberRange[uint8] { + return &nc.UInt8Config +} + +// UIntptr implements NumberConfig.UIntPtr. +func (nc *DstructNumberRangeConfig) UIntPtr() *NumberRange[uintptr] { + return &nc.UIntPtrConfig +} + +// Copy implements NumberConfig.Copy. +func (nc *DstructNumberRangeConfig) Copy() NumberRangeConfig { + numberConfigCopy := new(DstructNumberRangeConfig) + *numberConfigCopy = *nc + return numberConfigCopy +} diff --git a/generator/config/dstruct.slice.config.go b/generator/config/dstruct.slice.config.go new file mode 100644 index 0000000..bcd65e7 --- /dev/null +++ b/generator/config/dstruct.slice.config.go @@ -0,0 +1,41 @@ +package config + +// DstructSliceConfig implements SliceConfig. +type DstructSliceConfig struct { + minLength int + maxLength int +} + +var _ SliceConfig = &DstructSliceConfig{} + +// SetLengthRange implements SliceConfig.SetLengthRange. +func (sc *DstructSliceConfig) SetLengthRange(min, max int) SliceConfig { + if min > max { + return sc + } + sc.minLength, sc.maxLength = min, max + return sc + +} + +// MinLength implements SliceConfig.MinLength. +func (sc *DstructSliceConfig) MinLength() int { + return sc.minLength + +} + +// MaxLength implements SliceConfig.MaxLength. +func (sc *DstructSliceConfig) MaxLength() int { + return sc.maxLength +} + +// Copy implements SliceConfig.Copy. +func (sc *DstructSliceConfig) Copy() SliceConfig { + s := *sc + return &s +} + +// NewSliceConfig is a constructor for DstructSliceConfig. +func NewSliceConfig() *DstructSliceConfig { + return &DstructSliceConfig{0, 10} +} diff --git a/generator/config/generation.value.config.go b/generator/config/generation.value.config.go new file mode 100644 index 0000000..4a49b3d --- /dev/null +++ b/generator/config/generation.value.config.go @@ -0,0 +1,38 @@ +package config + +type ConfigType uint + +type ValueGenerationType uint8 + +const ( + Generate ValueGenerationType = iota // will generate all field + GenerateOnce // will generate the fields once + UseDefaults +) + +// RecursiveDefinition defines the configuration for recursive definitions within a dynamic struct. +type RecursiveDefinition struct { + // Allow defines if recursive definitions are allowed. + Allow bool + // Depth defines the depth of the recursive definition if recursion is allowed. + Depth uint +} + +// GenerationValueConfig defines how values are generated within a dynamic struct. +type GenerationValueConfig struct { + ValueGenerationType ValueGenerationType + SetNonRequiredFields bool + RecursiveDefinition RecursiveDefinition +} + +// DefaultGenerationValueConfig returns a default configuration for generation values. +func DefaultGenerationValueConfig() GenerationValueConfig { + return GenerationValueConfig{ + ValueGenerationType: UseDefaults, + SetNonRequiredFields: false, + RecursiveDefinition: RecursiveDefinition{ + Allow: false, + Depth: 1, + }, + } +} diff --git a/generator/config/number.config.go b/generator/config/number.config.go deleted file mode 100644 index f0a3ce4..0000000 --- a/generator/config/number.config.go +++ /dev/null @@ -1,202 +0,0 @@ -package config - -type Number interface { - int | int8 | int16 | int32 | int64 | float32 | float64 | - uint | uint8 | uint16 | uint32 | uint64 | uintptr -} - -type NumRange[n Number] struct { - min n - max n -} -type NumberRangeImpl[n Number] struct { - NumRange[n] -} - -func (nr *NumberRangeImpl[n]) Range() (n, n) { - return nr.min, nr.max -} - -func (nr *NumberRangeImpl[n]) Max() n { - return nr.max - -} -func (nr *NumberRangeImpl[n]) Min() n { - return nr.min - -} - -func (nr *NumberRangeImpl[n]) SetRange(min n, max n) { - if min < max { - nr.min = min - nr.max = max - } - -} - -func (nr *NumberRangeImpl[n]) SetMax(max n) { - nr.SetRange(nr.min, max) - -} -func (nr *NumberRangeImpl[n]) SetMin(min n) { - nr.SetRange(min, nr.max) - -} - -func (nr *NumberRangeImpl[n]) RangeRef() (*n, *n) { - return &nr.min, &nr.max - -} - -type NumberConfig interface { - Int() *NumberRangeImpl[int] - Int8() *NumberRangeImpl[int8] - Int16() *NumberRangeImpl[int16] - Int32() *NumberRangeImpl[int32] - Int64() *NumberRangeImpl[int64] - Float32() *NumberRangeImpl[float32] - Float64() *NumberRangeImpl[float64] - UInt() *NumberRangeImpl[uint] - UInt8() *NumberRangeImpl[uint8] - UInt16() *NumberRangeImpl[uint16] - UInt32() *NumberRangeImpl[uint32] - UInt64() *NumberRangeImpl[uint64] - UIntPtr() *NumberRangeImpl[uintptr] - Copy() NumberConfig -} - -type IntConfig NumberRangeImpl[int] -type NumberConfigImpl struct { - IntConfig NumberRangeImpl[int] - Int8Config NumberRangeImpl[int8] - Int16Config NumberRangeImpl[int16] - Int32Config NumberRangeImpl[int32] - Int64Config NumberRangeImpl[int64] - Float32Config NumberRangeImpl[float32] - Float64Config NumberRangeImpl[float64] - UIntConfig NumberRangeImpl[uint] - UInt8Config NumberRangeImpl[uint8] - UInt16Config NumberRangeImpl[uint16] - UInt32Config NumberRangeImpl[uint32] - UInt64Config NumberRangeImpl[uint64] - UIntPtrConfig NumberRangeImpl[uintptr] -} - -func NumberRangeConfig[N Number](min, max N) NumberRangeImpl[N] { - return NumberRangeImpl[N]{NumRange[N]{min, max}} -} - -func NewNumberConfig() *NumberConfigImpl { - return &NumberConfigImpl{ - IntConfig: NumberRangeConfig[int](0, 10), - Int8Config: NumberRangeConfig[int8](0, 10), - Int16Config: NumberRangeConfig[int16](0, 10), - Int32Config: NumberRangeConfig[int32](0, 10), - Int64Config: NumberRangeConfig[int64](0, 10), - Float32Config: NumberRangeConfig[float32](0, 10), - Float64Config: NumberRangeConfig[float64](0, 10), - UIntConfig: NumberRangeConfig[uint](0, 10), - UInt8Config: NumberRangeConfig[uint8](0, 10), - UInt16Config: NumberRangeConfig[uint16](0, 10), - UInt32Config: NumberRangeConfig[uint32](0, 10), - UInt64Config: NumberRangeConfig[uint64](0, 10), - UIntPtrConfig: NumberRangeConfig[uintptr](0, 10), - } - -} - -var _ NumberConfig = &NumberConfigImpl{} - -// Float32 implements NumberConfig. -func (nc *NumberConfigImpl) Float32() *NumberRangeImpl[float32] { - return &nc.Float32Config -} - -// Float64 implements NumberConfig. -func (nc *NumberConfigImpl) Float64() *NumberRangeImpl[float64] { - return &nc.Float64Config -} - -// Int implements NumberConfig. -func (nc *NumberConfigImpl) Int() *NumberRangeImpl[int] { - return &nc.IntConfig -} - -// Int16 implements NumberConfig. -func (nc *NumberConfigImpl) Int16() *NumberRangeImpl[int16] { - return &nc.Int16Config -} - -// Int32 implements NumberConfig. -func (nc *NumberConfigImpl) Int32() *NumberRangeImpl[int32] { - return &nc.Int32Config -} - -// Int64 implements NumberConfig. -func (nc *NumberConfigImpl) Int64() *NumberRangeImpl[int64] { - return &nc.Int64Config -} - -// Int8 implements NumberConfig. -func (nc *NumberConfigImpl) Int8() *NumberRangeImpl[int8] { - return &nc.Int8Config - -} - -// UInt implements NumberConfig. -func (nc *NumberConfigImpl) UInt() *NumberRangeImpl[uint] { - return &nc.UIntConfig -} - -// UInt16 implements NumberConfig. -func (nc *NumberConfigImpl) UInt16() *NumberRangeImpl[uint16] { - return &nc.UInt16Config -} - -// UInt32 implements NumberConfig. -func (nc *NumberConfigImpl) UInt32() *NumberRangeImpl[uint32] { - return &nc.UInt32Config - -} - -// UInt64 implements NumberConfig. -func (nc *NumberConfigImpl) UInt64() *NumberRangeImpl[uint64] { - return &nc.UInt64Config -} - -// UInt8 implements NumberConfig. -func (nc *NumberConfigImpl) UInt8() *NumberRangeImpl[uint8] { - return &nc.UInt8Config -} - -// UIntptr implements NumberConfig. -func (nc *NumberConfigImpl) UIntPtr() *NumberRangeImpl[uintptr] { - return &nc.UIntPtrConfig -} - -// UIntptr implements NumberConfig. -func (nc *NumberConfigImpl) Copy() NumberConfig { - return simplePointerCopy(nc) - // return &NumberConfigImpl{ - // IntConfig: nc.IntConfig, - // Int8Config: nc.Int8Config, - // Int16Config: nc.Int16Config, - // Int32Config: nc.Int32Config, - // Int64Config: nc.Int64Config, - // Float32Config: nc.Float32Config, - // Float64Config: nc.Float64Config, - // UIntConfig: nc.UIntConfig, - // UInt8Config: nc.UInt8Config, - // UInt16Config: nc.UInt16Config, - // UInt32Config: nc.UInt32Config, - // UInt64Config: nc.UInt64Config, - // UIntPtrConfig: nc.UIntPtrConfig, - // } -} - -func simplePointerCopy[T any](p *T) *T { - v := new(T) - *v = *p - return v - -} diff --git a/generator/config/number.range.config.go b/generator/config/number.range.config.go new file mode 100644 index 0000000..62c33e4 --- /dev/null +++ b/generator/config/number.range.config.go @@ -0,0 +1,96 @@ +package config + +// Number is a type that represents a number. +type Number interface { + int | int8 | int16 | int32 | int64 | float32 | float64 | + uint | uint8 | uint16 | uint32 | uint64 | uintptr +} + +// numRange represents the range of a number. +type numRange[n Number] struct { + min n + max n +} +type NumberRange[n Number] struct { + numRange[n] +} + +func (nr *NumberRange[n]) Range() (n, n) { + return nr.min, nr.max +} + +func (nr *NumberRange[n]) Max() n { + return nr.max +} + +func (nr *NumberRange[n]) Min() n { + return nr.min +} + +func (nr *NumberRange[n]) SetRange(min n, max n) { + if min < max { + nr.min = min + nr.max = max + } +} + +func (nr *NumberRange[n]) SetMax(max n) { + nr.SetRange(nr.min, max) +} + +func (nr *NumberRange[n]) SetMin(min n) { + nr.SetRange(min, nr.max) +} + +func (nr *NumberRange[n]) RangeRef() (*n, *n) { + return &nr.min, &nr.max +} + +func numberRange[N Number](min, max N) NumberRange[N] { + return NumberRange[N]{numRange[N]{min, max}} +} + +// NumberRangeConfig represents the configuration for a number range within a dynamic struct. +type NumberRangeConfig interface { + // Int returns the configuration for an int number range. + Int() *NumberRange[int] + + // Int8 returns the configuration for an int8 number range. + Int8() *NumberRange[int8] + + // Int16 returns the configuration for an int16 number range. + Int16() *NumberRange[int16] + + // Int32 returns the configuration for an int32 number range. + Int32() *NumberRange[int32] + + // Int64 returns the configuration for an int64 number range. + Int64() *NumberRange[int64] + + // Float32 returns the configuration for a float32 number range. + Float32() *NumberRange[float32] + + // Float64 returns the configuration for a float64 number range. + Float64() *NumberRange[float64] + + // UInt returns the configuration for a uint number range. + UInt() *NumberRange[uint] + + // UInt8 returns the configuration for a uint8 number range. + UInt8() *NumberRange[uint8] + + // UInt16 returns the configuration for a uint16 number range. + UInt16() *NumberRange[uint16] + + // Uint32 returns the configuration for a uint32 number range. + UInt32() *NumberRange[uint32] + + // UInt64 returns the configuration for a uint64 number range. + UInt64() *NumberRange[uint64] + + // UIntPtr returns the configuration for a uintptr number range. + UIntPtr() *NumberRange[uintptr] + + // Copy returns a copy of the configuration. + Copy() NumberRangeConfig +} diff --git a/generator/config/slice.config.go b/generator/config/slice.config.go index 6ddeee9..aa2092a 100644 --- a/generator/config/slice.config.go +++ b/generator/config/slice.config.go @@ -1,44 +1,16 @@ package config type SliceConfig interface { - SetLengthRange(min, max int) SliceConfig - MinLength() int - MaxLength() int - Copy() SliceConfig -} -type SliceConfigImpl struct { - minLength int - maxLength int -} - -var _ SliceConfig = &SliceConfigImpl{} - -func (sc *SliceConfigImpl) SetLengthRange(min, max int) SliceConfig { - if min > max { - return sc - } - sc.minLength, sc.maxLength = min, max - return sc - -} - -// MinLength implements SliceConfig. -func (sc *SliceConfigImpl) MinLength() int { - return sc.minLength - -} + // SetLengthRange sets the length range for the slice. + SetLengthRange(min, max int) SliceConfig -// MaxLength implements SliceConfig. -func (sc *SliceConfigImpl) MaxLength() int { - return sc.maxLength -} + // MinLength returns the minimum length of the slice. + MinLength() int -func (sc *SliceConfigImpl) Copy() SliceConfig { - s := *sc - return &s -} + // MaxLength returns the maximum length of the slice. + MaxLength() int -func NewSliceConfig() *SliceConfigImpl { - return &SliceConfigImpl{0, 10} + // Copy returns a copy of the slice configuration. + Copy() SliceConfig } diff --git a/generator/core/date.go b/generator/core/date.go index 1637d5e..d57920f 100644 --- a/generator/core/date.go +++ b/generator/core/date.go @@ -21,13 +21,13 @@ func GenerateDateTimeFunc() generator.GenerationFunction { } -func GenerateDateTimeBetweenDatesFunc(dc config.DateConfig) generator.GenerationFunction { +func GenerateDateTimeBetweenDatesFunc(dc config.DateRangeConfig) generator.GenerationFunction { // TODO have a proper implementation return &coreGenerationFunction{ _func: func(parameters ...any) any { - timeDiffInSeconds := dc.GetDateEnd().Sub(dc.GetDateStart()).Seconds() - return dc.GetDateStart().Add(time.Second * time.Duration(generateNum(0, timeDiffInSeconds))) + timeDiffInSeconds := dc.GetEndDate().Sub(dc.GetStartDate()).Seconds() + return dc.GetStartDate().Add(time.Second * time.Duration(generateNum(0, timeDiffInSeconds))) }, kind: NewKind(time.Time{}), @@ -35,23 +35,23 @@ func GenerateDateTimeBetweenDatesFunc(dc config.DateConfig) generator.Generation } -type DateFunctionHolderFunc func(config.DateConfig) generator.GenerationFunction +type DateFunctionHolderFunc func(config.DateRangeConfig) generator.GenerationFunction type DateFunctionHolder struct { BaseFunctionHolder } -func NewDateFunctionHolder(f DateFunctionHolderFunc, cfg config.DateConfig) *DateFunctionHolder { +func NewDateFunctionHolder(f DateFunctionHolderFunc, cfg config.DateRangeConfig) *DateFunctionHolder { return &DateFunctionHolder{ BaseFunctionHolder: BaseFunctionHolder{ - config: config.NewConfigBuilder().WithDateConfig(cfg).Build(), + config: config.NewDstructConfigBuilder().WithDateRangeConfig(cfg).Build(), fun: f, generationFunction: f(cfg), }, } } -func DefaultDateFunctionHolder(cfg config.DateConfig) *DateFunctionHolder { +func DefaultDateFunctionHolder(cfg config.DateRangeConfig) *DateFunctionHolder { return NewDateFunctionHolder(GenerateDateTimeBetweenDatesFunc, cfg) } diff --git a/generator/core/number.function.holder.go b/generator/core/number.function.holder.go index c06d684..ae69ea4 100644 --- a/generator/core/number.function.holder.go +++ b/generator/core/number.function.holder.go @@ -5,7 +5,7 @@ import ( "github.com/MartinSimango/dstruct/generator/config" ) -type NumberFunctionHolderFunc func(config.NumberConfig) generator.GenerationFunction +type NumberFunctionHolderFunc func(config.NumberRangeConfig) generator.GenerationFunction type NumberFunctionHolder struct { BaseFunctionHolder @@ -13,10 +13,10 @@ type NumberFunctionHolder struct { var _ FunctionHolder = &NumberFunctionHolder{} -func NewNumberFunctionHolder(f NumberFunctionHolderFunc, cfg config.NumberConfig) *NumberFunctionHolder { +func NewNumberFunctionHolder(f NumberFunctionHolderFunc, cfg config.NumberRangeConfig) *NumberFunctionHolder { return &NumberFunctionHolder{ BaseFunctionHolder: BaseFunctionHolder{ - config: config.NewConfigBuilder().WithNumberConfig(cfg).Build(), + config: config.NewDstructConfigBuilder().WithNumberConfig(cfg).Build(), fun: f, generationFunction: f(cfg), }, diff --git a/generator/core/number.go b/generator/core/number.go index 4ce550d..d715548 100644 --- a/generator/core/number.go +++ b/generator/core/number.go @@ -13,7 +13,7 @@ func generateNum[n config.Number](min, max n) n { return min + (n(rand.Float64() * float64(max+1-min))) } -func GenerateNumberFunc[n config.Number](cfg config.NumberConfig) generator.GenerationFunction { +func GenerateNumberFunc[n config.Number](cfg config.NumberRangeConfig) generator.GenerationFunction { min, max := getNumberRange[n](cfg) return &coreGenerationFunction{ _func: func(parameters ...any) any { @@ -36,7 +36,7 @@ func GenerateSequential(seed int) generator.GenerationFunction { } } -func getNumberRange[n config.Number](cfg config.NumberConfig) (*n, *n) { +func getNumberRange[n config.Number](cfg config.NumberRangeConfig) (*n, *n) { var min, max any v := any(*new(n)) switch v.(type) { @@ -72,6 +72,6 @@ func getNumberRange[n config.Number](cfg config.NumberConfig) (*n, *n) { return any(min).(*n), any(max).(*n) } -func NewGenerateNumberFunctionHolder[N config.Number](numberConfig config.NumberConfig) *NumberFunctionHolder { +func NewGenerateNumberFunctionHolder[N config.Number](numberConfig config.NumberRangeConfig) *NumberFunctionHolder { return NewNumberFunctionHolder(GenerateNumberFunc[N], numberConfig) } From 20c6bf1932966c236cf6515c3d661afc922eee2a Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Tue, 27 Aug 2024 23:35:55 +0200 Subject: [PATCH 11/31] Clean up interface for generated struct and implement --- dstruct.generated.struct.go | 376 ++++++++++++++++++ generated.struct.go | 288 ++------------ ...value.config.go => generation.settings.go} | 15 +- generator/core/array.go | 2 + generator/core/bool.go | 1 + .../core/default.generation.functions.go | 30 +- generator/core/generated.field.context.go | 59 +++ generator/core/generated.field.go | 145 +++++-- generator/core/generation.unit.go | 51 --- generator/core/number.go | 11 +- generator/core/pointer.go | 5 +- generator/core/slice.go | 30 +- modifier.go | 7 +- 13 files changed, 634 insertions(+), 386 deletions(-) create mode 100644 dstruct.generated.struct.go rename generator/config/{generation.value.config.go => generation.settings.go} (65%) create mode 100644 generator/core/generated.field.context.go delete mode 100644 generator/core/generation.unit.go diff --git a/dstruct.generated.struct.go b/dstruct.generated.struct.go new file mode 100644 index 0000000..780b070 --- /dev/null +++ b/dstruct.generated.struct.go @@ -0,0 +1,376 @@ +package dstruct + +import ( + "fmt" + "reflect" + "time" + + "github.com/MartinSimango/dstruct/dreflect" + "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" + "github.com/MartinSimango/dstruct/generator/core" +) + +// CustomType allows types with the same type of 'Value' to have a specific generation function that +// is stored by the 'FunctionHolder' field. This is useful for types that are not supported by the +// default generation functions. +type CustomType struct { + Value any + FunctionHolder core.FunctionHolder +} + +// DStructGeneratedStruct implements GeneratedStruct. +type DStructGeneratedStruct[T any] struct { + *DynamicStructModifierImpl + fieldContexts GeneratedFieldContexts + structConfig core.GeneratedFieldConfig + instance T + customTypes map[reflect.Type]core.FunctionHolder +} + +var _ GeneratedStruct = &DStructGeneratedStruct[int]{} + +func NewGeneratedStruct[T any](val T) *DStructGeneratedStruct[T] { + return NewGeneratedStructWithConfig( + val, + config.NewDstructConfig(), + config.DefaultGenerationSettings(), + ) +} + +func NewGeneratedStructWithConfig[T any](val T, + cfg config.Config, + settings config.GenerationSettings, + customTypes ...CustomType, +) *DStructGeneratedStruct[T] { + generatedStruct := &DStructGeneratedStruct[T]{ + DynamicStructModifierImpl: ExtendStruct(val).Build().(*DynamicStructModifierImpl), + fieldContexts: make(GeneratedFieldContexts), + structConfig: core.NewGenerateFieldConfig( + cfg, + settings, + ), + customTypes: make(map[reflect.Type]core.FunctionHolder), + } + + for _, v := range customTypes { + generatedStruct.addCustomType(v) + } + generatedStruct.addCustomTypes() + generatedStruct.populateGeneratedFields(generatedStruct.root) + return generatedStruct +} + +// InstanceT returns the instance of the generated struct. +func (gs *DStructGeneratedStruct[T]) InstanceT() T { + return gs.instance +} + +// NewT returns a new instance of the generated struct +func (gs *DStructGeneratedStruct[T]) NewT() *T { + gs.DynamicStructModifierImpl.New() + return &gs.instance +} + +// Generate implements GeneratedStruct.Generate. +func (gs *DStructGeneratedStruct[T]) Generate() { + gs.generateFields() + + switch any(*new(T)).(type) { + case nil: + gs.instance = gs.DynamicStructModifierImpl.Instance().(T) + return + } + + gs.instance = toType[T](gs.DynamicStructModifierImpl) + + // TODO: should only be called if new struct fields are generated + // an idea is to have generatedFields return a bool to indicate if new fields were generated + // gs.Update() + +} + +// SetFieldGenerationSettings implements GeneratedStruct.SetFieldGenerationSettings +func (gs *DStructGeneratedStruct[T]) SetFieldGenerationSettings( + field string, + settings config.GenerationSettings, +) error { + if err := gs.assertFieldExists(field); err != nil { + return err + } + + if gs.fieldContexts[field] == nil { + gs.propagateSettings(gs.fieldNodeMap[field], settings) + } else { + gs.fieldContexts[field].GeneratedField.SetGenerationSettings(settings) + } + + return nil +} + +// GetFieldGenerationSettings implements GeneratedStruct.GetFieldGenerationSettings +func (gs *DStructGeneratedStruct[T]) GetFieldGenerationSettings( + field string, +) (config.GenerationSettings, error) { + if err := gs.assertFieldExists(field); err != nil { + return config.GenerationSettings{}, err + } + + if gs.fieldContexts[field] == nil { + return config.GenerationSettings{}, fmt.Errorf( + "field %s does not have any generation settings", + field, + ) + } + + return gs.fieldContexts[field].GeneratedField.Config.GenerationSettings, nil +} + +// SetGenerationSettings implements GeneratedStruct.SetGenerationSettings +func (gs *DStructGeneratedStruct[T]) SetGenerationSettings( + settings config.GenerationSettings, +) { + gs.structConfig.GenerationSettings = settings + + gs.propagateSettings(gs.root, gs.structConfig.GenerationSettings) +} + +// GetGenerationSettings implements GeneratedStruct.GetGenerationSettings +func (gs *DStructGeneratedStruct[T]) GetGenerationSettings() config.GenerationSettings { + return gs.structConfig.GenerationSettings +} + +// SetFieldGenerationConfig implements GeneratedStruct.SetFieldGenerationConfig +func (gs *DStructGeneratedStruct[T]) SetFieldGenerationConfig( + field string, + cfg config.Config, +) error { + if err := gs.assertFieldExists(field); err != nil { + return err + } + + if gs.fieldContexts[field] == nil { + gs.propagateConfig(gs.fieldNodeMap[field], cfg) + } else { + // this will be fields that are either custom types or fields that have no children + gs.fieldContexts[field].GeneratedField.SetConfig(cfg) + } + + return nil +} + +func (gs *DStructGeneratedStruct[T]) GetFieldGenerationConfig(field string) (config.Config, error) { + if err := gs.assertFieldExists(field); err != nil { + return nil, err + } + + if gs.fieldContexts[field] == nil { + return nil, fmt.Errorf( + "field %s does not have a generation config", + field, + ) + } + + return gs.fieldContexts[field].GeneratedField.Config.GenerationConfig, nil +} + +// SetGenerationConfig implements GeneratedStruct.SetGenerationConfig +func (gs *DStructGeneratedStruct[T]) SetGenerationConfig(cfg config.Config) { + gs.structConfig.GenerationConfig = cfg + + gs.propagateConfig(gs.root, gs.structConfig.GenerationConfig) +} + +// GetGenerationConfig implements GeneratedStruct.GetGenerationConfig +func (gs *DStructGeneratedStruct[T]) GetGenerationConfig() config.Config { + return gs.structConfig.GenerationConfig +} + +// SetFieldGenerationFunction implements GeneratedStruct.SetFieldGenerationFunction +func (gs *DStructGeneratedStruct[T]) SetFieldGenerationFunction(field string, + functionHolder core.FunctionHolder, +) error { + if err := gs.assertFieldExists(field); err != nil { + return err + } + + if gs.fieldContexts[field] == nil { + return fmt.Errorf("field %s does not have a generation function", field) + } + + gs.fieldContexts[field].GeneratedField.SetGenerationFunction(functionHolder) + + return nil +} + +// SetFieldGenerationFunctions implements GeneratedStruct.SetFieldGenerationFunctions +func (gs *DStructGeneratedStruct[T]) SetFieldGenerationFunctions( + field string, + functions core.DefaultGenerationFunctions, +) error { + if err := gs.assertFieldExists(field); err != nil { + return err + } + + if gs.fieldContexts[field] == nil { + gs.propagateGenerationFunctions(gs.fieldNodeMap[field], functions) + } else { + gs.fieldContexts[field].GeneratedField.SetGenerationFunctions(functions) + } + + return nil +} + +// SetGenerationFunctions implements GeneratedStruct.SetGenerationFunctions +func (gs *DStructGeneratedStruct[T]) SetGenerationFunctions( + functions core.DefaultGenerationFunctions, +) { + gs.structConfig.GenerationFunctions = functions + gs.propagateGenerationFunctions(gs.root, functions) +} + +// SetFieldFromTask implements GeneratedStruct.SetFieldFromTask +func (gs *DStructGeneratedStruct[T]) SetFieldFromTask( + field string, + task generator.Task, + params ...any, +) error { + taskProperties, err := generator.CreateTaskProperties( + field, + generator.GetTagForTask(generator.TaskName(task.Name()), params...), + ) + if err != nil { + return err + } + + gs.SetFieldGenerationFunction( + field, + core.NewFunctionHolderNoArgs(task.GenerationFunction(*taskProperties)), + ) + return nil +} + +func toType[T any](gs DynamicStructModifier) T { + return dreflect.ConvertToType[T](gs.Instance()) +} + +func toPointerType[T any](gs DynamicStructModifier) *T { + return dreflect.ConvertToType[*T](gs.New()) +} + +func (gs *DStructGeneratedStruct[T]) generateFields() { + for k, genFunc := range gs.fieldContexts { + if err := gs.Set(k, genFunc.Generate()); err != nil { + fmt.Println(err) + } + } +} + +func (gs *DStructGeneratedStruct[T]) addCustomTypes() { + gs.addCustomType( + CustomType{ + time.Time{}, + core.DefaultDateFunctionHolder(gs.structConfig.GenerationConfig.Date()), + }, + ) +} + +func (gs *DStructGeneratedStruct[T]) addCustomType(customType CustomType) { + // TODO: restrict some types from being added such as nil, ints etc + gs.customTypes[reflect.TypeOf(customType.Value)] = customType.FunctionHolder + // the function holder kind is the find that the function retrusn which could either be an existing kind or a new kind i.ie time.Time would be a new kind + // TODO:idea: GenerationsFunction key should be type of CustomKind and not reflect.Kind + gs.structConfig.GenerationFunctions[customType.FunctionHolder.Kind()] = customType.FunctionHolder +} + +func (gs *DStructGeneratedStruct[T]) createGeneratedField( + field *Node[structField], + kind reflect.Kind, +) *core.GeneratedField { + return core.NewGeneratedField(field.data.fullyQualifiedName, + field.data.value, + field.data.tag, + gs.structConfig.Copy(kind), + gs.customTypes, + field.data.goType) +} + +func (gs *DStructGeneratedStruct[T]) populateGeneratedFields(node *Node[structField]) { + for _, field := range node.children { + if customType := gs.customTypes[field.data.goType]; customType != nil { + gs.fieldContexts[field.data.fullyQualifiedName] = core.NewGeneratedFieldContext( + gs.createGeneratedField(field, customType.Kind()), + ) + } else if field.HasChildren() { + gs.populateGeneratedFields(field) + } else { + gs.fieldContexts[field.data.fullyQualifiedName] = core.NewGeneratedFieldContext( + gs.createGeneratedField(field, field.data.value.Kind()), + ) + } + } +} + +func (gs *DStructGeneratedStruct[T]) propagateConfig( + node *Node[structField], + cfg config.Config, +) { + for _, field := range node.children { + // Don't propagate changes to children nodes if the field is a custom type + if field.HasChildren() && + !gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.IsCustomType() { + gs.propagateConfig(field, cfg) + } else { + gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.SetConfig(cfg) + } + } +} + +func (gs *DStructGeneratedStruct[T]) propagateSettings( + node *Node[structField], + settings config.GenerationSettings, +) { + for _, field := range node.children { + if field.HasChildren() && + !gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.IsCustomType() { + gs.propagateSettings(field, settings) + } else { + gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.SetGenerationSettings(settings) + } + } +} + +func (gs *DStructGeneratedStruct[T]) propagateGenerationFunctions( + node *Node[structField], + functions core.DefaultGenerationFunctions, +) { + for _, field := range node.children { + if field.HasChildren() && + !gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.IsCustomType() { + gs.propagateGenerationFunctions(field, functions) + } else { + gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.SetGenerationFunctions(functions) + } + } +} + +func (gs *DStructGeneratedStruct[T]) propagateGenerationFunction( + node *Node[structField], + functionHolder core.FunctionHolder, +) { + for _, field := range node.children { + if field.HasChildren() && + !gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.IsCustomType() { + gs.propagateGenerationFunction(field, functionHolder) + } else { + gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.SetGenerationFunction(functionHolder) + } + } +} + +func (gs *DStructGeneratedStruct[T]) assertFieldExists(field string) error { + if gs.fieldNodeMap[field] == nil { + return fmt.Errorf("field %s does not exist within the struct", field) + } + return nil +} diff --git a/generated.struct.go b/generated.struct.go index 1006fc9..d113eb1 100644 --- a/generated.struct.go +++ b/generated.struct.go @@ -1,33 +1,36 @@ package dstruct import ( - "fmt" - "reflect" - "time" - - "github.com/MartinSimango/dstruct/dreflect" "github.com/MartinSimango/dstruct/generator" "github.com/MartinSimango/dstruct/generator/config" "github.com/MartinSimango/dstruct/generator/core" ) -type GenerationFields map[string]*core.GenerationUnit +type GeneratedFieldContexts map[string]*core.GeneratedFieldContext type GeneratedStruct interface { DynamicStructModifier - // Generate generates fields for the struct + // Generate generates fields for the struct. If new fields are generated, the root tree for the underlying + // struct is updated. This allows new generated fields to be accessed and modified by Set and Get methods Generate() - // GenerateAndUpdate Generates fields and updates the root tree for the underlying struct. Allowing - // new generated fields to be accessed and modified by Set and Get methods. - GenerateAndUpdate() + // SetFieldGenerationSettings sets the generation value config for field within the struct. + // If the field does not exist or if the field has no generation settings an error is returned. + SetFieldGenerationSettings( + field string, + settings config.GenerationSettings, + ) error + + // GetFieldGenerationSettings gets the generation config for field within the struct. + // If the field does not exist or if the field has no generation settings an error is returned. + GetFieldGenerationSettings(field string) (config.GenerationSettings, error) - SetFieldGenerationValueConfig(field string, config config.GenerationValueConfig) + // SetGenerationSettings sets the generation settings for the struct and propagates the settings to all fields - // GetFieldValueGenerationConfig gets the generation config for field within the struct. - GetFieldValueGenerationConfig(field string) config.GenerationValueConfig + SetGenerationSettings(settings config.GenerationSettings) - GetValueGenerationConfig() config.GenerationValueConfig + // GetGenerationSettings gets the generation settings for the struct. + GetGenerationSettings() config.GenerationSettings // SetFieldGenerationConfig sets the generation config for field within the struct. It returns // an error if the field does not exist or if the field cannot be generated. @@ -37,253 +40,26 @@ type GeneratedStruct interface { // Fields types that cannot be generated: structs, func, chan, any (will default to a nil value being generated). // // Note: Pointers to structs can be generated. - SetFieldConfig(field string, generationConfig config.Config) error - - GetFieldConfig(field string) config.Config - - SetConfig(config config.Config) - - GetConfig() config.Config - - SetGenerationValueConfig(config config.GenerationValueConfig) - - SetFieldFromTask(field string, task generator.Task, params ...any) error -} - -type GeneratedStructImpl[T any] struct { - *DynamicStructModifierImpl - generatedFields GenerationFields - generatedFieldConfig core.GeneratedFieldConfig - config config.Config - instance T - customTypes map[reflect.Type]core.FunctionHolder -} - -type CustomType struct { - Value any - FunctionHolder core.FunctionHolder -} - -// var _ GeneratedStruct = &GeneratedStructImpl[int]{} - -func NewGeneratedStruct[T any](val T) *GeneratedStructImpl[T] { - return NewGeneratedStructWithConfig(val, config.NewDstructConfig()) -} - -func NewGeneratedStructWithConfig[T any](val T, - cfg config.Config, - customTypes ...CustomType, -) *GeneratedStructImpl[T] { - generatedStruct := &GeneratedStructImpl[T]{ - DynamicStructModifierImpl: ExtendStruct(val).Build().(*DynamicStructModifierImpl), - generatedFields: make(GenerationFields), - config: cfg, - generatedFieldConfig: core.NewGenerateFieldConfig( - cfg, - config.DefaultGenerationValueConfig(), - ), - customTypes: make(map[reflect.Type]core.FunctionHolder), - } - - for _, v := range customTypes { - generatedStruct.addCustomType(v) - } - generatedStruct.addCustomTypes() - generatedStruct.populateGeneratedFields(generatedStruct.root) - return generatedStruct -} - -func (gs *GeneratedStructImpl[T]) addCustomTypes() { - gs.addCustomType(CustomType{time.Time{}, core.DefaultDateFunctionHolder(gs.config.Date())}) -} - -func (gs *GeneratedStructImpl[T]) addCustomType(customType CustomType) { - gs.customTypes[reflect.TypeOf(customType.Value)] = customType.FunctionHolder - // the function holder kind is the find that the function retrusn which could either be an existing kind or a new kind i.ie time.Time would be a new kind - // idea: GenerationsFunction key should be type of CustomKind and not reflect.Kind - gs.generatedFieldConfig.GenerationFunctions[customType.FunctionHolder.Kind()] = customType.FunctionHolder -} - -func (gs *GeneratedStructImpl[T]) createGeneratedField( - field *Node[structField], - kind reflect.Kind, -) *core.GeneratedField { - return core.NewGeneratedField(field.data.fullyQualifiedName, - field.data.value, - field.data.tag, - gs.generatedFieldConfig.Copy(kind), - gs.config.Copy(), // TODO account for nil - gs.customTypes, - field.data.goType, - ) -} - -func (gs *GeneratedStructImpl[T]) populateGeneratedFields(node *Node[structField]) { - for _, field := range node.children { - - if field.HasChildren() { - if customType := gs.customTypes[field.data.goType]; customType != nil { - gs.generatedFields[field.data.fullyQualifiedName] = core.NewGenerationUnit( - gs.createGeneratedField(field, customType.Kind()), - ) - continue - } - gs.populateGeneratedFields(field) - continue - } - gs.generatedFields[field.data.fullyQualifiedName] = core.NewGenerationUnit( - gs.createGeneratedField(field, field.data.value.Kind()), - ) - } -} - -func (gs *GeneratedStructImpl[T]) Generate() { - gs.generateFields() - - switch any(*new(T)).(type) { - case nil: - gs.instance = gs.DynamicStructModifierImpl.Instance().(T) - return - } - - gs.instance = ToType[T](gs.DynamicStructModifierImpl) -} - -func (gs *GeneratedStructImpl[T]) GenerateAndUpdate() { - gs.Generate() - gs.Update() -} - -func (gs *GeneratedStructImpl[T]) changeChildrenConfig(node *Node[structField], cfg config.Config) { - for _, field := range node.children { - if customType := gs.customTypes[field.data.goType]; customType != nil { - gs.generatedFields[field.data.fullyQualifiedName].GenerationFunctions[customType.Kind()].SetConfig(cfg) - continue - } - if field.HasChildren() { - gs.changeChildrenConfig(field, cfg) - continue - } - gs.generatedFields[field.data.fullyQualifiedName].GenerationFunctions[field.data.typ.Kind()].SetConfig(cfg) - } -} - -func (gs *GeneratedStructImpl[T]) SetFieldGenerationConfig(field string, cfg config.Config) error { - if gs.fieldNodeMap[field] == nil { - return fmt.Errorf("field %s does not exist within the struct", field) - } - // TODO TEST for structs - if gs.fieldNodeMap[field].HasChildren() { - gs.changeChildrenConfig(gs.fieldNodeMap[field], cfg) - } - - if gs.generatedFields[field] == nil { - return fmt.Errorf("cannot set config for field %s", field) - } - kind := gs.generatedFields[field].Value.Kind() - - gs.generatedFields[field].GeneratedField.GenerationFunctions[kind].SetConfig(cfg) - return nil -} + SetFieldGenerationConfig(field string, generationConfig config.Config) error -func (gs *GeneratedStructImpl[T]) SetFieldGenerationFunction(field string, - functionHolder core.FunctionHolder, -) { - // kind := gs.fieldNodeMap[field].data.GetType().Kind() - // _ = functionHolder.(*core.FunctionHolderWithNoArgs) - gs.generatedFields[field].GeneratedField.GenerationFunctions[functionHolder.Kind()] = functionHolder -} + // GetFieldGenerationConfig gets the generation config for field within the struct. + GetFieldGenerationConfig(field string) (config.Config, error) -func (gs *GeneratedStructImpl[T]) SetFieldGenerationFunctions(field string, - defaultGenerationFunctions core.DefaultGenerationFunctions, -) { - gs.generatedFields[field].GenerationFunctions = defaultGenerationFunctions -} + // SetGenerationConfig sets the generation config for the struct and propagates the settings to all fields + SetGenerationConfig(config config.Config) -func (gs *GeneratedStructImpl[T]) SetGenerationConfig(config config.Config) { - for name, field := range gs.fieldNodeMap { - if field.HasChildren() { - continue - } - gs.generatedFields[name].SetConfig(config.Copy()) - } -} + // GetGenerationConfig gets the generation config for the struct. + GetGenerationConfig() config.Config -func (gs *GeneratedStructImpl[T]) SetFieldGenerationValueConfig( - field string, - config config.GenerationValueConfig, -) { - gs.generatedFields[field].GenerationValueConfig = config -} + // SetFieldGenerationFunction sets the generation function for field within the struct. It returns an error if the field does not exist or if the field cannot be generated. + SetFieldGenerationFunction(field string, functionHolder core.FunctionHolder) error -func (gs *GeneratedStructImpl[T]) SetGenerationValueConfig(config config.GenerationValueConfig) { - for name, field := range gs.fieldNodeMap { - if field.HasChildren() { - continue - } - gs.generatedFields[name].GenerationValueConfig = config - } -} + // SetFieldDefaultFunctions sets the default generation functions for field within the struct. It returns an error if the field does not exist or if the field cannot be generated. + SetFieldGenerationFunctions(field string, functions core.DefaultGenerationFunctions) error -// GetFieldValueGenerationConfig implements GeneratedStruct. -func (gs *GeneratedStructImpl[T]) GetFieldValueGenerationConfig( - field string, -) config.GenerationValueConfig { - return gs.generatedFields[field].GenerationValueConfig -} + // SetGenerationFunctions sets the generation functions for the struct and propagates the settings to all fields + SetGenerationFunctions(functions core.DefaultGenerationFunctions) -// GetFieldValueGenerationConfig implements GeneratedStruct. -func (gs *GeneratedStructImpl[T]) GetFieldGenerationConfig(field string) config.Config { - k := gs.generatedFields[field].GeneratedField.Value.Kind() - return gs.generatedFields[field].GeneratedField.GenerationFunctions[k].GetConfig() -} - -// GetValueGenerationConfig implements GeneratedStruct. -func (gs *GeneratedStructImpl[T]) GetValueGenerationConfig() config.GenerationValueConfig { - return gs.generatedFieldConfig.GenerationValueConfig -} - -func (gs *GeneratedStructImpl[T]) SetFieldFromTask( - field string, - task generator.Task, - params ...any, -) error { - taskProperties, err := generator.CreateTaskProperties( - field, - generator.GetTagForTask(generator.TaskName(task.Name()), params...), - ) - if err != nil { - return err - } - - gs.SetFieldGenerationFunction( - field, - core.NewFunctionHolderNoArgs(task.GenerationFunction(*taskProperties)), - ) - return nil -} - -func ToType[T any](gs DynamicStructModifier) T { - return dreflect.ConvertToType[T](gs.Instance()) -} - -func ToPointerType[T any](gs DynamicStructModifier) *T { - return dreflect.ConvertToType[*T](gs.New()) -} - -func (gs *GeneratedStructImpl[T]) generateFields() { - for k, genFunc := range gs.generatedFields { - if err := gs.Set(k, genFunc.Generate()); err != nil { - fmt.Println(err) - } - } -} - -func (gs *GeneratedStructImpl[T]) Instance() T { - return gs.instance -} - -func (gs *GeneratedStructImpl[T]) New() *T { - gs.DynamicStructModifierImpl.New() - return &gs.instance + // SetFieldFromTask sets the field value from the task. The task is used to generate the value for the field. + SetFieldFromTask(field string, task generator.Task, params ...any) error } diff --git a/generator/config/generation.value.config.go b/generator/config/generation.settings.go similarity index 65% rename from generator/config/generation.value.config.go rename to generator/config/generation.settings.go index 4a49b3d..13a6a53 100644 --- a/generator/config/generation.value.config.go +++ b/generator/config/generation.settings.go @@ -18,16 +18,21 @@ type RecursiveDefinition struct { Depth uint } -// GenerationValueConfig defines how values are generated within a dynamic struct. -type GenerationValueConfig struct { +// GenerationSettings defines how values are generated within a dynamic struct. +type GenerationSettings struct { ValueGenerationType ValueGenerationType SetNonRequiredFields bool RecursiveDefinition RecursiveDefinition } -// DefaultGenerationValueConfig returns a default configuration for generation values. -func DefaultGenerationValueConfig() GenerationValueConfig { - return GenerationValueConfig{ +func (gs *GenerationSettings) WithNonRequiredFields(required bool) GenerationSettings { + gs.SetNonRequiredFields = required + return *gs +} + +// DefaultGenerationSettings returns a default configuration for generation values. +func DefaultGenerationSettings() GenerationSettings { + return GenerationSettings{ ValueGenerationType: UseDefaults, SetNonRequiredFields: false, RecursiveDefinition: RecursiveDefinition{ diff --git a/generator/core/array.go b/generator/core/array.go index 9a8bc95..adf6477 100644 --- a/generator/core/array.go +++ b/generator/core/array.go @@ -1 +1,3 @@ package core + +// TODO: to be implemented diff --git a/generator/core/bool.go b/generator/core/bool.go index f05f086..73dc306 100644 --- a/generator/core/bool.go +++ b/generator/core/bool.go @@ -6,6 +6,7 @@ import ( "github.com/MartinSimango/dstruct/generator" ) +// GenerateBoolFunc returns a GenerationFunction that generates a boolean value. func GenerateBoolFunc() generator.GenerationFunction { return &coreGenerationFunction{ diff --git a/generator/core/default.generation.functions.go b/generator/core/default.generation.functions.go index cd4b56f..1d21eff 100644 --- a/generator/core/default.generation.functions.go +++ b/generator/core/default.generation.functions.go @@ -20,13 +20,14 @@ func (d DefaultGenerationFunctions) Copy(kind reflect.Kind) (dgf DefaultGenerati dgf[kind] = d[kind].Copy() } return dgf - } func NewDefaultGenerationFunctions(cfg config.Config) DefaultGenerationFunctions { - defaultGenerationFunctions := make(DefaultGenerationFunctions) - defaultGenerationFunctions[reflect.String] = NewFixedFunctionHolder(GenerateStringFromRegexFunc, "^[a-zA-Z]{3}$") + defaultGenerationFunctions[reflect.String] = NewFixedFunctionHolder( + GenerateStringFromRegexFunc, + "^[a-zA-Z]{3}$", + ) defaultGenerationFunctions[reflect.Int] = NewGenerateNumberFunctionHolder[int](cfg.Number()) // NewFixedFunctionHolder(GenerateSequential, 0) // NewGenerateNumberFunctionHolder[int](cfg.Number()) @@ -37,14 +38,23 @@ func NewDefaultGenerationFunctions(cfg config.Config) DefaultGenerationFunctions defaultGenerationFunctions[reflect.Uint] = NewGenerateNumberFunctionHolder[uint](cfg.Number()) defaultGenerationFunctions[reflect.Uint8] = NewGenerateNumberFunctionHolder[uint8](cfg.Number()) - defaultGenerationFunctions[reflect.Uint16] = NewGenerateNumberFunctionHolder[uint16](cfg.Number()) - defaultGenerationFunctions[reflect.Uint32] = NewGenerateNumberFunctionHolder[uint32](cfg.Number()) - defaultGenerationFunctions[reflect.Uint64] = NewGenerateNumberFunctionHolder[uint64](cfg.Number()) - - defaultGenerationFunctions[reflect.Float32] = NewGenerateNumberFunctionHolder[float32](cfg.Number()) - defaultGenerationFunctions[reflect.Float64] = NewGenerateNumberFunctionHolder[float64](cfg.Number()) + defaultGenerationFunctions[reflect.Uint16] = NewGenerateNumberFunctionHolder[uint16]( + cfg.Number(), + ) + defaultGenerationFunctions[reflect.Uint32] = NewGenerateNumberFunctionHolder[uint32]( + cfg.Number(), + ) + defaultGenerationFunctions[reflect.Uint64] = NewGenerateNumberFunctionHolder[uint64]( + cfg.Number(), + ) + + defaultGenerationFunctions[reflect.Float32] = NewGenerateNumberFunctionHolder[float32]( + cfg.Number(), + ) + defaultGenerationFunctions[reflect.Float64] = NewGenerateNumberFunctionHolder[float64]( + cfg.Number(), + ) defaultGenerationFunctions[reflect.Bool] = NewFunctionHolderNoArgs(GenerateBoolFunc()) defaultGenerationFunctions[reflect.Ptr] = NewFunctionHolderNoArgs(GenerateNilValueFunc()) return defaultGenerationFunctions - } diff --git a/generator/core/generated.field.context.go b/generator/core/generated.field.context.go new file mode 100644 index 0000000..6a5d58f --- /dev/null +++ b/generator/core/generated.field.context.go @@ -0,0 +1,59 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" +) + +type GeneratedFieldContext struct { + PreviousGenerationSettings config.GenerationSettings + CurrentFunction generator.GenerationFunction + GeneratedField *GeneratedField + + count int + latestValue any + generationSettings config.GenerationSettings +} + +func NewGeneratedFieldContext(field *GeneratedField) *GeneratedFieldContext { + gfc := &GeneratedFieldContext{ + GeneratedField: field, + generationSettings: field.Config.GenerationSettings, + } + gfc.PreviousGenerationSettings = gfc.generationSettings + // gu.CurrentFunction = gu.getGenerationFunction() + return gfc +} + +func (gfc *GeneratedFieldContext) Generate() any { + // + gfc.CurrentFunction = gfc.GeneratedField.getGenerationFunction() + + if gfc.configChanged(gfc.PreviousGenerationSettings) { + gfc.count = 0 + } + + if gfc.generationSettings.ValueGenerationType == config.GenerateOnce && gfc.count > 0 { + return gfc.latestValue + } + + gfc.latestValue = gfc.CurrentFunction.Generate() + gfc.PreviousGenerationSettings = gfc.generationSettings + gfc.count++ + return gfc.latestValue +} + +func (gfc *GeneratedFieldContext) SetGeneratedFieldConfig(cfg config.Config) { + gfc.GeneratedField.SetConfig(cfg) +} + +func (gfc *GeneratedFieldContext) SetGeneratedFieldSettings(settings config.GenerationSettings) { + gfc.GeneratedField.SetGenerationSettings(settings) +} + +// TODO: when does config need to be changed? + +func (gu *GeneratedFieldContext) configChanged(previousConfig config.GenerationSettings) bool { + return gu.generationSettings.ValueGenerationType != previousConfig.ValueGenerationType || + gu.generationSettings.SetNonRequiredFields != previousConfig.SetNonRequiredFields +} diff --git a/generator/core/generated.field.go b/generator/core/generated.field.go index e964bf2..c661469 100644 --- a/generator/core/generated.field.go +++ b/generator/core/generated.field.go @@ -10,35 +10,42 @@ import ( ) type GeneratedFieldConfig struct { - GenerationFunctions DefaultGenerationFunctions - GenerationValueConfig config.GenerationValueConfig + GenerationFunctions DefaultGenerationFunctions + GenerationSettings config.GenerationSettings + GenerationConfig config.Config } func (gf *GeneratedFieldConfig) Copy(kind reflect.Kind) (gfc GeneratedFieldConfig) { return GeneratedFieldConfig{ - GenerationFunctions: gf.GenerationFunctions.Copy(kind), - GenerationValueConfig: gf.GenerationValueConfig, + GenerationFunctions: gf.GenerationFunctions.Copy(kind), + GenerationSettings: gf.GenerationSettings, + GenerationConfig: gf.GenerationConfig.Copy(), } } func (gf *GeneratedFieldConfig) SetConfig(cfg config.Config) { + gf.GenerationConfig = cfg for _, v := range gf.GenerationFunctions { v.SetConfig(cfg) } } -func NewGenerateFieldConfig(cfg config.Config, gvc config.GenerationValueConfig) GeneratedFieldConfig { +func NewGenerateFieldConfig( + cfg config.Config, + settings config.GenerationSettings, +) GeneratedFieldConfig { return GeneratedFieldConfig{ - GenerationFunctions: NewDefaultGenerationFunctions(cfg), - GenerationValueConfig: gvc, + GenerationFunctions: NewDefaultGenerationFunctions(cfg), + GenerationSettings: settings, + GenerationConfig: cfg, } } type GeneratedField struct { - Name string - Value reflect.Value - Tag reflect.StructTag - GeneratedFieldConfig + Name string + Value reflect.Value + Tag reflect.StructTag + Config GeneratedFieldConfig Parent *GeneratedField PointerValue *reflect.Value customTypes map[reflect.Type]FunctionHolder @@ -48,42 +55,92 @@ type GeneratedField struct { func NewGeneratedField(fqn string, value reflect.Value, tag reflect.StructTag, - generatedFieldConfig GeneratedFieldConfig, - config config.Config, + config GeneratedFieldConfig, customTypes map[reflect.Type]FunctionHolder, goType reflect.Type, - ) *GeneratedField { generateField := &GeneratedField{ - Name: fqn, - Value: value, - Tag: tag, - GeneratedFieldConfig: generatedFieldConfig, - customTypes: customTypes, - goType: goType, + Name: fqn, + Value: value, + Tag: tag, + Config: config, + customTypes: customTypes, + goType: goType, } + // TODO: add custom type to GenerationFunctions if value.Kind() == reflect.Slice { - generatedFieldConfig.GenerationFunctions[reflect.Slice] = - NewSliceFunctionHolder(GenerateSliceFunc, generateField, config, generateField.GenerationFunctions) - + config.GenerationFunctions[reflect.Slice] = NewSliceFunctionHolder( + GenerateSliceFunc, + generateField, + config.GenerationConfig, + generateField.Config.GenerationFunctions, + ) } + + // if field.IsCustomType() { + // config.GenerationFunctions[field.customTypeFunctionHolder().GetFunction().Kind()] = field.customTypeFunctionHolder() + // } + return generateField } +func (field *GeneratedField) IsCustomType() bool { + return field.customTypes[field.goType] != nil +} + +func (field *GeneratedField) customTypeFunctionHolder() FunctionHolder { + return field.customTypes[field.goType] +} + +func (field *GeneratedField) SetConfig(cfg config.Config) { + kind := field.Value.Kind() + if field.IsCustomType() { + field.customTypeFunctionHolder().SetConfig(cfg) + } else if field.Config.GenerationFunctions[kind] != nil { + field.Config.GenerationFunctions[kind].SetConfig(cfg) + } else { + field.Config.SetConfig(cfg) + } +} + +func (field *GeneratedField) SetGenerationSettings(settings config.GenerationSettings) { + field.Config.GenerationSettings = settings +} + +func (field *GeneratedField) SetGenerationFunction( + functionHolder FunctionHolder, +) { + if field.IsCustomType() { + field.customTypes[field.goType] = functionHolder + } else if field.Config.GenerationFunctions[field.Value.Kind()] != nil { + field.Config.GenerationFunctions[field.Value.Kind()] = functionHolder + } +} + +func (field *GeneratedField) SetGenerationFunctions(functions DefaultGenerationFunctions) { + field.Config.GenerationFunctions = functions +} + func (field *GeneratedField) checkForRecursiveDefinition(fail bool) bool { var depth uint = 0 var matchedField *GeneratedField for parent := field.Parent; parent != nil; parent = parent.Parent { if parent.Value.Type() == field.Value.Type() { - if !field.GenerationValueConfig.RecursiveDefinition.Allow || fail { - panic(fmt.Sprintf("github.com/MartinSimango/dstruct/generator: recursive definition found for field `%s` of type %s", parent.Name, parent.Value.Type())) + if !field.Config.GenerationSettings.RecursiveDefinition.Allow || fail { + panic( + fmt.Sprintf( + "github.com/MartinSimango/dstruct/generator: recursive definition found for field `%s` of type %s", + parent.Name, + parent.Value.Type(), + ), + ) } depth++ if depth == 1 { matchedField = parent } } - if depth == (field.GenerationValueConfig.RecursiveDefinition.Depth + 1) { + if depth == (field.Config.GenerationSettings.RecursiveDefinition.Depth + 1) { // fmt.Println(":DF ", field.Name, matchedField.Name, field.Value.Type(), matchedField.Value.Type(), matchedField.Value, depth) if matchedField.PointerValue != nil { matchedField.PointerValue.SetZero() @@ -94,10 +151,10 @@ func (field *GeneratedField) checkForRecursiveDefinition(fail bool) bool { } } return false - } func (field *GeneratedField) SetValue() bool { + // check if the current field is a custom type with it's own generation function if customType := field.customTypes[field.goType]; customType != nil { field.Value.Set(reflect.ValueOf(customType.GetFunction().Generate())) return false @@ -116,7 +173,9 @@ func (field *GeneratedField) SetValue() bool { if field.checkForRecursiveDefinition(true) { return true } - field.Value.Set(reflect.ValueOf(field.GenerationFunctions[kind].GetFunction().Generate())) + field.Value.Set( + reflect.ValueOf(field.Config.GenerationFunctions[kind].GetFunction().Generate()), + ) case reflect.Interface: field.Value.Set(reflect.Zero(field.Value.Type())) default: @@ -128,20 +187,20 @@ func (field *GeneratedField) SetValue() bool { func (field *GeneratedField) setStructValues() { for j := 0; j < field.Value.NumField(); j++ { structField := &GeneratedField{ - Name: field.Name + "." + field.Value.Type().Field(j).Name, - Value: field.Value.Field(j), - Tag: field.Value.Type().Field(j).Tag, - GeneratedFieldConfig: field.GeneratedFieldConfig.Copy(field.Value.Field(j).Kind()), - Parent: field, - customTypes: field.customTypes, - goType: field.Value.Field(j).Type(), + Name: field.Name + "." + field.Value.Type().Field(j).Name, + Value: field.Value.Field(j), + Tag: field.Value.Type().Field(j).Tag, + Config: field.Config.Copy(field.Value.Field(j).Kind()), + Parent: field, + customTypes: field.customTypes, + goType: field.Value.Field(j).Type(), } structField.SetValue() } } func (field *GeneratedField) getGenerationFunction() generator.GenerationFunction { - + // check if field is a custom type with it's own generation function if field.customTypes[field.goType] != nil { return field.customTypes[field.goType].GetFunction() } @@ -155,7 +214,8 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio case reflect.Ptr: return GeneratePointerValueFunc(field) } - if field.GenerationValueConfig.ValueGenerationType == config.UseDefaults { + + if field.Config.GenerationSettings.ValueGenerationType == config.UseDefaults { example, ok := tags.Lookup("example") if !ok { example, ok = tags.Lookup("default") @@ -182,7 +242,6 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio default: fmt.Println("Unsupported types for defaults: ", kind, example) } - } } @@ -194,7 +253,7 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio format := tags.Get("format") switch format { - // TODO replace or remove this + // TODO : replace or remove this case "date-time": return GenerateDateTimeFunc() } @@ -202,7 +261,9 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio enum, ok := tags.Lookup("enum") if ok { numEnums, _ := strconv.Atoi(enum) - return GenerateFixedValueFunc(tags.Get(fmt.Sprintf("enum_%d", generateNum(0, numEnums-1)+1))) + return GenerateFixedValueFunc( + tags.Get(fmt.Sprintf("enum_%d", generateNum(0, numEnums-1)+1)), + ) } _, ok = tags.Lookup("gen_task") @@ -218,5 +279,7 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio return task.GenerationFunction(*taskProperties) } - return field.GenerationFunctions[kind].GetFunction() + // if we get no match, we default to the default generation function for the kind + // kidds of type Slice will be handled here as their default generation function for a slice will be overwritten when the generated field is created. + return field.Config.GenerationFunctions[kind].GetFunction() } diff --git a/generator/core/generation.unit.go b/generator/core/generation.unit.go deleted file mode 100644 index 721cd17..0000000 --- a/generator/core/generation.unit.go +++ /dev/null @@ -1,51 +0,0 @@ -package core - -import ( - "github.com/MartinSimango/dstruct/generator" - "github.com/MartinSimango/dstruct/generator/config" -) - -type GenerationUnit struct { - PreviousValueConfig config.GenerationValueConfig - CurrentFunction generator.GenerationFunction - *GeneratedField - count int - latestValue any - generationConfig config.GenerationValueConfig -} - -func NewGenerationUnit(field *GeneratedField) *GenerationUnit { - gu := &GenerationUnit{ - GeneratedField: field, - generationConfig: field.GeneratedFieldConfig.GenerationValueConfig, - } - gu.PreviousValueConfig = gu.generationConfig - // gu.CurrentFunction = gu.getGenerationFunction() - return gu -} - -func (gu *GenerationUnit) Generate() any { - // - gu.CurrentFunction = gu.getGenerationFunction() - - if gu.configChanged(gu.PreviousValueConfig) { - gu.count = 0 - } - - if gu.generationConfig.ValueGenerationType == config.GenerateOnce && gu.count > 0 { - return gu.latestValue - } - - gu.latestValue = gu.CurrentFunction.Generate() - gu.PreviousValueConfig = gu.generationConfig - gu.count++ - return gu.latestValue -} - -// TODO when does config need to be changed? - -func (gu *GenerationUnit) configChanged(previousConfig config.GenerationValueConfig) bool { - - return gu.generationConfig.ValueGenerationType != previousConfig.ValueGenerationType || - gu.generationConfig.SetNonRequiredFields != previousConfig.SetNonRequiredFields -} diff --git a/generator/core/number.go b/generator/core/number.go index d715548..ced8107 100644 --- a/generator/core/number.go +++ b/generator/core/number.go @@ -13,15 +13,18 @@ func generateNum[n config.Number](min, max n) n { return min + (n(rand.Float64() * float64(max+1-min))) } -func GenerateNumberFunc[n config.Number](cfg config.NumberRangeConfig) generator.GenerationFunction { +func GenerateNumberFunc[n config.Number]( + cfg config.NumberRangeConfig, +) generator.GenerationFunction { + // get reference to min and max so when the function is called, it will use the current value min, max := getNumberRange[n](cfg) return &coreGenerationFunction{ _func: func(parameters ...any) any { + // TODO: LOG return generateNum(*min, *max) }, kind: reflect.ValueOf(*new(n)).Kind(), } - } func GenerateSequential(seed int) generator.GenerationFunction { @@ -72,6 +75,8 @@ func getNumberRange[n config.Number](cfg config.NumberRangeConfig) (*n, *n) { return any(min).(*n), any(max).(*n) } -func NewGenerateNumberFunctionHolder[N config.Number](numberConfig config.NumberRangeConfig) *NumberFunctionHolder { +func NewGenerateNumberFunctionHolder[N config.Number]( + numberConfig config.NumberRangeConfig, +) *NumberFunctionHolder { return NewNumberFunctionHolder(GenerateNumberFunc[N], numberConfig) } diff --git a/generator/core/pointer.go b/generator/core/pointer.go index 88c2302..c0027a4 100644 --- a/generator/core/pointer.go +++ b/generator/core/pointer.go @@ -7,11 +7,10 @@ import ( ) func GeneratePointerValueFunc(field *GeneratedField) generator.GenerationFunction { - return &coreGenerationFunction{ _func: func(parameters ...any) any { field := parameters[0].(*GeneratedField) - if !field.GenerationValueConfig.SetNonRequiredFields { + if !field.Config.GenerationSettings.SetNonRequiredFields { return nil } @@ -26,10 +25,8 @@ func GeneratePointerValueFunc(field *GeneratedField) generator.GenerationFunctio } return field.Value.Interface() - }, kind: reflect.Ptr, args: []any{field}, } - } diff --git a/generator/core/slice.go b/generator/core/slice.go index 56d68a8..a96d943 100644 --- a/generator/core/slice.go +++ b/generator/core/slice.go @@ -8,12 +8,15 @@ import ( "github.com/MartinSimango/dstruct/generator/config" ) -func GenerateSliceFunc(field *GeneratedField, config config.Config, generationFunctions DefaultGenerationFunctions) generator.GenerationFunction { +func GenerateSliceFunc( + field *GeneratedField, + config config.Config, + generationFunctions DefaultGenerationFunctions, +) generator.GenerationFunction { return &coreGenerationFunction{ _func: func(parameters ...any) any { - field := parameters[0].(*GeneratedField) - sliceConfig := field.GenerationFunctions[reflect.Slice].GetConfig().Slice() + sliceConfig := field.Config.GenerationFunctions[reflect.Slice].GetConfig().Slice() sliceType := reflect.TypeOf(field.Value.Interface()).Elem() min := sliceConfig.MinLength() max := sliceConfig.MaxLength() @@ -25,16 +28,19 @@ func GenerateSliceFunc(field *GeneratedField, config config.Config, generationFu for i := 0; i < len; i++ { elemValue := reflect.ValueOf(sliceElement.Interface()).Elem() - fieldConfig := NewGenerateFieldConfig(field.GenerationFunctions[reflect.Slice].GetConfig(), field.GenerationValueConfig) + fieldConfig := NewGenerateFieldConfig( + field.Config.GenerationFunctions[reflect.Slice].GetConfig(), + field.Config.GenerationSettings, + ) fieldConfig.GenerationFunctions = generationFunctions newField := &GeneratedField{ - Name: fmt.Sprintf("%s#%d", field.Name, i), - Value: elemValue, - Tag: field.Tag, - GeneratedFieldConfig: fieldConfig, - Parent: field, - customTypes: field.customTypes, - goType: elemValue.Type(), + Name: fmt.Sprintf("%s#%d", field.Name, i), + Value: elemValue, + Tag: field.Tag, + Config: fieldConfig, + Parent: field, + customTypes: field.customTypes, + goType: elemValue.Type(), } newField.SetValue() @@ -42,10 +48,8 @@ func GenerateSliceFunc(field *GeneratedField, config config.Config, generationFu slice = reflect.Append(slice, sliceElement.Elem()) } return slice.Interface() - }, args: []any{field}, kind: reflect.Slice, } - } diff --git a/modifier.go b/modifier.go index 5970e7d..528d37b 100644 --- a/modifier.go +++ b/modifier.go @@ -63,25 +63,23 @@ func newStruct(strct any, rootNode *Node[structField]) *DynamicStructModifierImp } func (dm *DynamicStructModifierImpl) createFieldToNodeMappings(rootNode *Node[structField]) { - for _, field := range rootNode.children { dm.fieldNodeMap[field.data.fullyQualifiedName] = field dm.fieldData[field.data.fullyQualifiedName] = *field.data dm.createFieldToNodeMappings(field) } - } func (dm *DynamicStructModifierImpl) New() any { return dm.strct } + func (dm *DynamicStructModifierImpl) Instance() any { return dreflect.GetUnderlyingPointerValue(dm.strct) } func (dm *DynamicStructModifierImpl) get(field string) (n *Node[structField]) { return dm.fieldNodeMap[field] - } func (dm *DynamicStructModifierImpl) Get(field string) (any, error) { @@ -104,6 +102,7 @@ func isFieldExported(field string) bool { } return true } + func (dm *DynamicStructModifierImpl) Set(field string, value any) error { var f *Node[structField] if f = dm.get(field); f == nil { @@ -128,6 +127,8 @@ func (dm *DynamicStructModifierImpl) Set(field string, value any) error { fieldValue.Set(dreflect.Convert(reflect.ValueOf(value), fieldValue.Type())) + // TODO: should we update the struct here? and then remove the Update and Apply methods + return nil } From bc8473b4de253ce192e18ee172ffbb486a6c5987 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Fri, 30 Aug 2024 01:11:51 +0200 Subject: [PATCH 12/31] Fix bug where unexported fields were not intially being set --- dstruct.generated.struct.go | 3 +-- tree.builder.go | 50 ++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/dstruct.generated.struct.go b/dstruct.generated.struct.go index 780b070..dd97523 100644 --- a/dstruct.generated.struct.go +++ b/dstruct.generated.struct.go @@ -86,8 +86,7 @@ func (gs *DStructGeneratedStruct[T]) Generate() { // TODO: should only be called if new struct fields are generated // an idea is to have generatedFields return a bool to indicate if new fields were generated - // gs.Update() - + gs.Update() } // SetFieldGenerationSettings implements GeneratedStruct.SetFieldGenerationSettings diff --git a/tree.builder.go b/tree.builder.go index 0da16f7..1055a35 100644 --- a/tree.builder.go +++ b/tree.builder.go @@ -61,10 +61,9 @@ func ExtendStruct(val any) *treeBuilderImpl { } return b - } -func newBuilderFromNode(node *Node[structField], resetFQN bool) *treeBuilderImpl { +func newBuilderFromNode(node *Node[structField], resetFQN bool) *treeBuilderImpl { if resetFQN { resetNodeFieldsFQN(node) } @@ -129,7 +128,6 @@ func (dsb *treeBuilderImpl) GetFieldCopy(field string) Builder { } func (dsb *treeBuilderImpl) getNode(field string) *Node[structField] { - fields := strings.Split(field, ".") node := dsb.root @@ -139,7 +137,6 @@ func (dsb *treeBuilderImpl) getNode(field string) *Node[structField] { } } return node - } func (db *treeBuilderImpl) Build() DynamicStructModifier { @@ -156,6 +153,8 @@ func (db *treeBuilderImpl) Build() DynamicStructModifier { func (db *treeBuilderImpl) buildStruct(tree *Node[structField]) any { structValue := reflect.ValueOf(dreflect.GetPointerOfValueType(treeToStruct(tree))) tree.data.value = structValue + // set the value of the struct fields. Currently the tree structure contains the values of the fields + // so we need to copy the values to the struct fields if db.setValues { if structValue.Elem().Kind() == reflect.Ptr { setPointerFieldValue(structValue.Elem(), tree) @@ -167,7 +166,14 @@ func (db *treeBuilderImpl) buildStruct(tree *Node[structField]) any { return structValue.Interface() } -func (dsb *treeBuilderImpl) addFieldToTree(name string, typ interface{}, pkgPath string, anonymous bool, tag reflect.StructTag, root *Node[structField]) reflect.Type { +func (dsb *treeBuilderImpl) addFieldToTree( + name string, + typ interface{}, + pkgPath string, + anonymous bool, + tag reflect.StructTag, + root *Node[structField], +) reflect.Type { value := reflect.ValueOf(typ) if !value.IsValid() { panic(fmt.Sprintf("Cannot determine type of field '%s'", name)) @@ -180,7 +186,7 @@ func (dsb *treeBuilderImpl) addFieldToTree(name string, typ interface{}, pkgPath goType := reflect.TypeOf(value.Interface()) field := &structField{ name: name, - value: value, + value: value, // this will initally be unaddressable until the struct is built tag: tag, typ: goType, goType: goType, @@ -188,6 +194,7 @@ func (dsb *treeBuilderImpl) addFieldToTree(name string, typ interface{}, pkgPath anonymous: anonymous, jsonName: strings.Split(tag.Get("json"), ",")[0], } + field.structIndex = new(int) *field.structIndex = *root.data.numberOfSubFields field.fullyQualifiedName = getFQN(root.data.GetFieldFullyQualifiedName(), field.name) @@ -216,10 +223,12 @@ func sortKeys(root *Node[structField]) (keys []string) { return } +// this only allocates memory for the struct and its fields and does not set any values +// so the returned value will be an uninitialized struct func treeToStruct(root *Node[structField]) any { var structFields []reflect.StructField - //sort the keys to ensure type of struct produced is always the same + // sort the keys to ensure type of struct produced is always the same var keys []string = sortKeys(root) for _, fieldName := range keys { @@ -266,16 +275,16 @@ func setStructFieldValues(strct reflect.Value, root *Node[structField]) { reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())). Elem(). Set(currentNode.data.value) + } + // make the tree node value point to the struct field - ensuring that when the node value changes the struct field value changes currentNode.data.value = field - // fmt.Println("C: ", currentNode.data.anonymous, currentNode.data.fqn) // if currentNode.data.anonymous { // db.addAnonymousSubfields(currentNode) // } } - } func setPointerFieldValue(field reflect.Value, currentNode *Node[structField]) { @@ -286,10 +295,14 @@ func setPointerFieldValue(field reflect.Value, currentNode *Node[structField]) { f := field if currentNode.data.numberOfSubFields != nil { // node is a struct with subfields that needs to be dereferenced for i := 0; i < currentNode.data.ptrDepth; i++ { - f.Set(reflect.New(f.Type().Elem())) + // We don't use f.Set(reflect.New(f.Type().Elem())) because it panics when the field is unexported + // se we need to access the memory address of the field and set the value which bypasses the panic + reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())). + Elem(). + Set(reflect.New(f.Type().Elem())) + // f.Set(reflect.New(f.Type().Elem())) f = f.Elem() } - } switch f.Kind() { @@ -299,10 +312,14 @@ func setPointerFieldValue(field reflect.Value, currentNode *Node[structField]) { field.Set(currentNode.data.value) } currentNode.data.value = field - } -func (dsb *treeBuilderImpl) addStructFields(strct reflect.Value, root *Node[structField], ptrDepth int, anon bool) reflect.Type { +func (dsb *treeBuilderImpl) addStructFields( + strct reflect.Value, + root *Node[structField], + ptrDepth int, + anon bool, +) reflect.Type { var structFields []reflect.StructField // Create pointer to struct to allow unexported field values to be read in order @@ -354,8 +371,11 @@ func getPtrValue(value reflect.Value, ptrDepth int) (reflect.Value, int) { return value, ptrDepth } -func (dsb *treeBuilderImpl) addPtrField(value reflect.Value, node *Node[structField], anonymous bool) reflect.Type { - +func (dsb *treeBuilderImpl) addPtrField( + value reflect.Value, + node *Node[structField], + anonymous bool, +) reflect.Type { if value.IsNil() { return reflect.TypeOf(value.Interface()) } From 633d2077295e2c979dcb2f1b1af84fdccd27408c Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sat, 5 Oct 2024 14:40:37 +0200 Subject: [PATCH 13/31] refactor generation function code --- .gitignore | 3 +- Makefile | 6 ++- dstruct.generated.struct.go | 53 +++++++++++++++++++ generated.struct.go | 12 +++++ generator/config/dstruct.config.go | 1 + .../config/dstruct.number.range.config.go | 6 +++ generator/core/function.holder.go | 2 +- generator/core/generated.field.go | 24 ++++++--- generator/core/number.function.holder.go | 6 ++- generator/core/number.go | 1 - modifier.go | 11 ++++ 11 files changed, 112 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 125c3eb..1a3e026 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ main/ dist/ -.DS_Store \ No newline at end of file +.DS_Store +examples/ diff --git a/Makefile b/Makefile index 8e1df5c..ddec718 100644 --- a/Makefile +++ b/Makefile @@ -41,4 +41,8 @@ test-bench: bench: - go test -bench=. \ No newline at end of file + go test -bench=. + + +task-example: + go run examples/task/main.go diff --git a/dstruct.generated.struct.go b/dstruct.generated.struct.go index dd97523..010effd 100644 --- a/dstruct.generated.struct.go +++ b/dstruct.generated.struct.go @@ -202,6 +202,35 @@ func (gs *DStructGeneratedStruct[T]) SetFieldGenerationFunction(field string, return nil } +// // GetFieldGenerationFunction implements GeneratedStruct.GetFieldGenerationFunction +// func (gs *DStructGeneratedStruct[T]) GetFieldGenerationFunction( +// field string, +// ) (core.FunctionHolder, error) { +// if err := gs.assertFieldExists(field); err != nil { +// return nil, err +// } +// +// if gs.fieldContexts[field] == nil { +// return nil, fmt.Errorf("field %s does not have a generation function", field) +// } +// +// return gs.fieldContexts[field].GeneratedgField. , nil +// } +// +// GetFieldGenerationFunction_ implements GeneratedStruct.GetFieldGenerationFunction_ + +// func (gs *DStructGeneratedStruct[T]) GetFieldGenerationFunction_(field string) core.FunctionHolder { +// if err := gs.assertFieldExists(field); err != nil { +// panic(err) +// } +// +// if gs.fieldContexts[field] == nil { +// panic(fmt.Errorf("field %s does not have a generation function", field)) +// } +// +// return gs.fieldContexts[field].GeneratedField.GenerationFunction +// } +// // SetFieldGenerationFunctions implements GeneratedStruct.SetFieldGenerationFunctions func (gs *DStructGeneratedStruct[T]) SetFieldGenerationFunctions( field string, @@ -249,6 +278,30 @@ func (gs *DStructGeneratedStruct[T]) SetFieldFromTask( return nil } +// GetFieldGenerationConfig_ implements GeneratedStruct. +func (gs *DStructGeneratedStruct[T]) GetFieldGenerationConfig_(field string) config.Config { + if err := gs.assertFieldExists(field); err != nil { + panic(err) + } + if gs.fieldContexts[field] == nil { + panic(fmt.Errorf("field %s does not have a generation config", field)) + } + return gs.fieldContexts[field].GeneratedField.Config.GenerationConfig +} + +// GetFieldGenerationSettings_ implements GeneratedStruct. +func (gs *DStructGeneratedStruct[T]) GetFieldGenerationSettings_( + field string, +) config.GenerationSettings { + if err := gs.assertFieldExists(field); err != nil { + panic(err) + } + if gs.fieldContexts[field] == nil { + panic(fmt.Errorf("field %s does not have any generation settings", field)) + } + return gs.fieldContexts[field].GeneratedField.Config.GenerationSettings +} + func toType[T any](gs DynamicStructModifier) T { return dreflect.ConvertToType[T](gs.Instance()) } diff --git a/generated.struct.go b/generated.struct.go index d113eb1..be409f6 100644 --- a/generated.struct.go +++ b/generated.struct.go @@ -25,6 +25,9 @@ type GeneratedStruct interface { // If the field does not exist or if the field has no generation settings an error is returned. GetFieldGenerationSettings(field string) (config.GenerationSettings, error) + // GetFieldGenerationSettings_ is like GetFieldGenerationSettings but panics if an error occurs. + GetFieldGenerationSettings_(field string) config.GenerationSettings + // SetGenerationSettings sets the generation settings for the struct and propagates the settings to all fields SetGenerationSettings(settings config.GenerationSettings) @@ -45,6 +48,9 @@ type GeneratedStruct interface { // GetFieldGenerationConfig gets the generation config for field within the struct. GetFieldGenerationConfig(field string) (config.Config, error) + // GetFieldGenerationConfig_ is like GetFieldGenerationConfig but panics if an error occurs. + GetFieldGenerationConfig_(field string) config.Config + // SetGenerationConfig sets the generation config for the struct and propagates the settings to all fields SetGenerationConfig(config config.Config) @@ -54,6 +60,12 @@ type GeneratedStruct interface { // SetFieldGenerationFunction sets the generation function for field within the struct. It returns an error if the field does not exist or if the field cannot be generated. SetFieldGenerationFunction(field string, functionHolder core.FunctionHolder) error + // // GetFieldGenerationConfig gets the generation function for field within the struct. + // GetFieldGenerationFunction(field string) (core.FunctionHolder, error) + // + // // GetFieldGenerationFunction_ is like GetFieldGenerationFunction but panics if an error occurs. + // GetFieldGenerationFunction_(field string) core.FunctionHolder + // // SetFieldDefaultFunctions sets the default generation functions for field within the struct. It returns an error if the field does not exist or if the field cannot be generated. SetFieldGenerationFunctions(field string, functions core.DefaultGenerationFunctions) error diff --git a/generator/config/dstruct.config.go b/generator/config/dstruct.config.go index 64f1702..a475f62 100644 --- a/generator/config/dstruct.config.go +++ b/generator/config/dstruct.config.go @@ -19,6 +19,7 @@ func NewDstructConfig() *DstructConfig { var _ Config = &DstructConfig{} // Copy implements Config. +// TODO: implement this func (c *DstructConfig) Copy() Config { newConfig := &DstructConfig{} if c.SliceConfig != nil { diff --git a/generator/config/dstruct.number.range.config.go b/generator/config/dstruct.number.range.config.go index 088e5b0..d7824ba 100644 --- a/generator/config/dstruct.number.range.config.go +++ b/generator/config/dstruct.number.range.config.go @@ -1,5 +1,9 @@ package config +import ( + "fmt" +) + // DstructNumberRangeConfig implements NumberRangeConfig. type DstructNumberRangeConfig struct { IntConfig NumberRange[int] @@ -105,6 +109,8 @@ func (nc *DstructNumberRangeConfig) UIntPtr() *NumberRange[uintptr] { // Copy implements NumberConfig.Copy. func (nc *DstructNumberRangeConfig) Copy() NumberRangeConfig { + fmt.Println("Copying DstructNumberRangeConfig ") + fmt.Println(nc.Int().RangeRef()) numberConfigCopy := new(DstructNumberRangeConfig) *numberConfigCopy = *nc return numberConfigCopy diff --git a/generator/core/function.holder.go b/generator/core/function.holder.go index 88fdd40..aa882eb 100644 --- a/generator/core/function.holder.go +++ b/generator/core/function.holder.go @@ -40,7 +40,7 @@ func (c *BaseFunctionHolder) SetConfig(cfg config.Config) { } func (c *BaseFunctionHolder) Copy() BaseFunctionHolder { - // TODO should this panic? + // TODO: should this panic? var configCopy config.Config if c.config != nil { configCopy = c.config.Copy() diff --git a/generator/core/generated.field.go b/generator/core/generated.field.go index c661469..24d1cbf 100644 --- a/generator/core/generated.field.go +++ b/generator/core/generated.field.go @@ -42,14 +42,15 @@ func NewGenerateFieldConfig( } type GeneratedField struct { - Name string - Value reflect.Value - Tag reflect.StructTag - Config GeneratedFieldConfig - Parent *GeneratedField - PointerValue *reflect.Value - customTypes map[reflect.Type]FunctionHolder - goType reflect.Type + Name string + Value reflect.Value + Tag reflect.StructTag + Config GeneratedFieldConfig + Parent *GeneratedField + PointerValue *reflect.Value + customTypes map[reflect.Type]FunctionHolder + goType reflect.Type + currentGenerationFunction generator.GenerationFunction } func NewGeneratedField(fqn string, @@ -113,6 +114,7 @@ func (field *GeneratedField) SetGenerationFunction( if field.IsCustomType() { field.customTypes[field.goType] = functionHolder } else if field.Config.GenerationFunctions[field.Value.Kind()] != nil { + field.Tag = reflect.StructTag("") // remove tags to ensure the field is generated with the new function field.Config.GenerationFunctions[field.Value.Kind()] = functionHolder } } @@ -200,6 +202,12 @@ func (field *GeneratedField) setStructValues() { } func (field *GeneratedField) getGenerationFunction() generator.GenerationFunction { + // keep cache of the current generation function + if field.currentGenerationFunction != nil { + // TODO: implement cache + return field.currentGenerationFunction + } + // check if field is a custom type with it's own generation function if field.customTypes[field.goType] != nil { return field.customTypes[field.goType].GetFunction() diff --git a/generator/core/number.function.holder.go b/generator/core/number.function.holder.go index ae69ea4..211d92f 100644 --- a/generator/core/number.function.holder.go +++ b/generator/core/number.function.holder.go @@ -5,6 +5,7 @@ import ( "github.com/MartinSimango/dstruct/generator/config" ) +// TODO: rename to NumberFunctionHolderFunc? type NumberFunctionHolderFunc func(config.NumberRangeConfig) generator.GenerationFunction type NumberFunctionHolder struct { @@ -13,7 +14,10 @@ type NumberFunctionHolder struct { var _ FunctionHolder = &NumberFunctionHolder{} -func NewNumberFunctionHolder(f NumberFunctionHolderFunc, cfg config.NumberRangeConfig) *NumberFunctionHolder { +func NewNumberFunctionHolder( + f NumberFunctionHolderFunc, + cfg config.NumberRangeConfig, +) *NumberFunctionHolder { return &NumberFunctionHolder{ BaseFunctionHolder: BaseFunctionHolder{ config: config.NewDstructConfigBuilder().WithNumberConfig(cfg).Build(), diff --git a/generator/core/number.go b/generator/core/number.go index ced8107..a57bc33 100644 --- a/generator/core/number.go +++ b/generator/core/number.go @@ -20,7 +20,6 @@ func GenerateNumberFunc[n config.Number]( min, max := getNumberRange[n](cfg) return &coreGenerationFunction{ _func: func(parameters ...any) any { - // TODO: LOG return generateNum(*min, *max) }, kind: reflect.ValueOf(*new(n)).Kind(), diff --git a/modifier.go b/modifier.go index 528d37b..39facbf 100644 --- a/modifier.go +++ b/modifier.go @@ -23,6 +23,9 @@ type DynamicStructModifier interface { // Get gets the value of the struct field `field` and returns an error if the field is not found Get(field string) (any, error) + // Get_ gets the value of the struct field `field` and panics if the field is not found + Get_(field string) any + // Set sets the value of the struct field `field` and returns an error if the field is not found. // // The program will panic if the type of value does not match the type of the struct field `field`. @@ -90,6 +93,14 @@ func (dm *DynamicStructModifierImpl) Get(field string) (any, error) { } } +func (dm *DynamicStructModifierImpl) Get_(field string) any { + if f := dm.get(field); f == nil { + panic(fmt.Errorf("field %s does not exists in struct", field)) + } else { + return f.data.value.Interface() + } +} + func isFieldExported(field string) bool { fields := strings.Split(field, ".") From 35a411f8e85d8374cb96f3ac3a3324605dc91040 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sun, 13 Oct 2024 23:03:18 +0200 Subject: [PATCH 14/31] fix bug where generation function was not restting after function holder was copied --- dstruct.generated.struct.go | 11 ++++++++- generator/config/dstruct.config.go | 4 ++++ .../config/dstruct.number.range.config.go | 6 ----- generator/core/function.holder.go | 7 ++++-- generator/core/generated.field.go | 4 ++++ generator/core/number.function.holder.go | 23 ++++++++----------- generator/core/number.go | 3 +++ 7 files changed, 35 insertions(+), 23 deletions(-) diff --git a/dstruct.generated.struct.go b/dstruct.generated.struct.go index 010effd..4bb29d3 100644 --- a/dstruct.generated.struct.go +++ b/dstruct.generated.struct.go @@ -339,12 +339,21 @@ func (gs *DStructGeneratedStruct[T]) createGeneratedField( field *Node[structField], kind reflect.Kind, ) *core.GeneratedField { - return core.NewGeneratedField(field.data.fullyQualifiedName, + v := core.NewGeneratedField(field.data.fullyQualifiedName, field.data.value, field.data.tag, gs.structConfig.Copy(kind), gs.customTypes, field.data.goType) + fmt.Printf( + "\nStruct Config address: %p\n", + gs.structConfig.GenerationFunctions[reflect.Int].GetConfig().Number(), + ) + fmt.Printf( + "\nConfig address: %p\n", + v.Config.GenerationFunctions[reflect.Int].GetConfig().Number(), + ) + return v } func (gs *DStructGeneratedStruct[T]) populateGeneratedFields(node *Node[structField]) { diff --git a/generator/config/dstruct.config.go b/generator/config/dstruct.config.go index a475f62..aacc632 100644 --- a/generator/config/dstruct.config.go +++ b/generator/config/dstruct.config.go @@ -5,6 +5,7 @@ type DstructConfig struct { SliceConfig SliceConfig NumberConfig NumberRangeConfig DateConfig DateRangeConfig + children []Config } // NewDstructConfig is a constructor for DstructConfig. @@ -51,6 +52,9 @@ func (c *DstructConfig) Number() NumberRangeConfig { // SetSliceLength implements Config.SetSliceLength. func (c *DstructConfig) SetSliceLength(min, max int) Config { c.SliceConfig.SetLengthRange(min, max) + for _, sub := range c.children { + sub.SetSliceLength(min, max) + } return c } diff --git a/generator/config/dstruct.number.range.config.go b/generator/config/dstruct.number.range.config.go index d7824ba..088e5b0 100644 --- a/generator/config/dstruct.number.range.config.go +++ b/generator/config/dstruct.number.range.config.go @@ -1,9 +1,5 @@ package config -import ( - "fmt" -) - // DstructNumberRangeConfig implements NumberRangeConfig. type DstructNumberRangeConfig struct { IntConfig NumberRange[int] @@ -109,8 +105,6 @@ func (nc *DstructNumberRangeConfig) UIntPtr() *NumberRange[uintptr] { // Copy implements NumberConfig.Copy. func (nc *DstructNumberRangeConfig) Copy() NumberRangeConfig { - fmt.Println("Copying DstructNumberRangeConfig ") - fmt.Println(nc.Int().RangeRef()) numberConfigCopy := new(DstructNumberRangeConfig) *numberConfigCopy = *nc return numberConfigCopy diff --git a/generator/core/function.holder.go b/generator/core/function.holder.go index aa882eb..3f1bc5f 100644 --- a/generator/core/function.holder.go +++ b/generator/core/function.holder.go @@ -16,10 +16,13 @@ type FunctionHolder interface { Kind() reflect.Kind } +type ResetFunction func(config config.Config) generator.GenerationFunction + type BaseFunctionHolder struct { config config.Config generationFunction generator.GenerationFunction fun any + resetFunction ResetFunction } func (c *BaseFunctionHolder) SetFunction(generationFunction generator.GenerationFunction) { @@ -35,8 +38,8 @@ func (c *BaseFunctionHolder) GetConfig() config.Config { } func (c *BaseFunctionHolder) SetConfig(cfg config.Config) { - c.generationFunction = nil // new config recreate generation function c.config = cfg + c.generationFunction = c.resetFunction(cfg) } func (c *BaseFunctionHolder) Copy() BaseFunctionHolder { @@ -49,7 +52,7 @@ func (c *BaseFunctionHolder) Copy() BaseFunctionHolder { return BaseFunctionHolder{ fun: c.fun, config: configCopy, - generationFunction: c.generationFunction, + generationFunction: c.resetFunction(configCopy), } } diff --git a/generator/core/generated.field.go b/generator/core/generated.field.go index 24d1cbf..1c02664 100644 --- a/generator/core/generated.field.go +++ b/generator/core/generated.field.go @@ -289,5 +289,9 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio // if we get no match, we default to the default generation function for the kind // kidds of type Slice will be handled here as their default generation function for a slice will be overwritten when the generated field is created. + fmt.Printf( + "Returning function ref: %p\n", + field.Config.GenerationFunctions[kind].GetConfig().Number(), + ) return field.Config.GenerationFunctions[kind].GetFunction() } diff --git a/generator/core/number.function.holder.go b/generator/core/number.function.holder.go index 211d92f..90b419e 100644 --- a/generator/core/number.function.holder.go +++ b/generator/core/number.function.holder.go @@ -5,7 +5,6 @@ import ( "github.com/MartinSimango/dstruct/generator/config" ) -// TODO: rename to NumberFunctionHolderFunc? type NumberFunctionHolderFunc func(config.NumberRangeConfig) generator.GenerationFunction type NumberFunctionHolder struct { @@ -18,27 +17,23 @@ func NewNumberFunctionHolder( f NumberFunctionHolderFunc, cfg config.NumberRangeConfig, ) *NumberFunctionHolder { - return &NumberFunctionHolder{ + nfh := &NumberFunctionHolder{ BaseFunctionHolder: BaseFunctionHolder{ - config: config.NewDstructConfigBuilder().WithNumberConfig(cfg).Build(), - fun: f, + config: config.NewDstructConfigBuilder().WithNumberConfig(cfg).Build(), + fun: f, + resetFunction: func(cfg config.Config) generator.GenerationFunction { + return f(cfg.Number()) + }, generationFunction: f(cfg), }, } -} - -// Override -func (c *NumberFunctionHolder) GetFunction() generator.GenerationFunction { - if c.generationFunction != nil { - return c.generationFunction - } - c.generationFunction = c.fun.(NumberFunctionHolderFunc)(c.config.Number()) - return c.generationFunction + return nfh } func (c *NumberFunctionHolder) Copy() FunctionHolder { - return &NumberFunctionHolder{ + nf := &NumberFunctionHolder{ BaseFunctionHolder: c.BaseFunctionHolder.Copy(), } + return nf } diff --git a/generator/core/number.go b/generator/core/number.go index a57bc33..673594d 100644 --- a/generator/core/number.go +++ b/generator/core/number.go @@ -18,8 +18,11 @@ func GenerateNumberFunc[n config.Number]( ) generator.GenerationFunction { // get reference to min and max so when the function is called, it will use the current value min, max := getNumberRange[n](cfg) + return &coreGenerationFunction{ _func: func(parameters ...any) any { + fmt.Println("Refernece to min and max: ", min, max) + fmt.Printf("Cfg: %p\n", cfg) return generateNum(*min, *max) }, kind: reflect.ValueOf(*new(n)).Kind(), From 5b0027347ebd1e20932425d5bad134f3ad0c3a43 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sun, 13 Oct 2024 23:27:53 +0200 Subject: [PATCH 15/31] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9718ab7..3b2867e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ > :warning: **Notice** > * Please do not install any of these versions: v1.1.1 v1.1.0 v1.0.0 v0.1.2 v0.1.1 as these were removed from the repo - (but are still available at pkg.go.dev). -> * When installing please explicitly install the actual latest version of dstruct which is currently v0.3.0-beta. +> * When installing please explicitly install the actual latest version of dstruct which is currently v0.4.0-beta. # dstruct @@ -39,7 +39,7 @@ extend or merge structs which have struct fields of type `any` their value must ## Install ```sh -go get github.com/MartinSimango/dstruct@v0.3.0-beta +go get github.com/MartinSimango/dstruct@v0.4.0-beta ``` ## How it works? From 73d8503e738ccbb414dba718eeb0db633c636fab Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Wed, 16 Oct 2024 02:37:46 +0200 Subject: [PATCH 16/31] fix config propagation bug --- dstruct.generated.struct.go | 8 ---- generator/config/config.go | 4 +- generator/config/dstruct.config.go | 46 +++++++++++-------- .../config/dstruct.number.range.config.go | 4 ++ generator/config/number.range.config.go | 10 ++-- generator/core/date.go | 17 ++++--- .../core/default.generation.functions.go | 20 +++++--- generator/core/fixed.function.holder.go | 6 +-- generator/core/function.holder.go | 23 ++++------ generator/core/generated.field.go | 26 ++++++----- generator/core/no.arg.function.holder.go | 8 ++-- generator/core/number.function.holder.go | 7 ++- generator/core/slice.function.holder.go | 11 +++-- go.mod | 2 +- 14 files changed, 107 insertions(+), 85 deletions(-) diff --git a/dstruct.generated.struct.go b/dstruct.generated.struct.go index 4bb29d3..fe6917a 100644 --- a/dstruct.generated.struct.go +++ b/dstruct.generated.struct.go @@ -345,14 +345,6 @@ func (gs *DStructGeneratedStruct[T]) createGeneratedField( gs.structConfig.Copy(kind), gs.customTypes, field.data.goType) - fmt.Printf( - "\nStruct Config address: %p\n", - gs.structConfig.GenerationFunctions[reflect.Int].GetConfig().Number(), - ) - fmt.Printf( - "\nConfig address: %p\n", - v.Config.GenerationFunctions[reflect.Int].GetConfig().Number(), - ) return v } diff --git a/generator/config/config.go b/generator/config/config.go index 0cdff7d..ada3e89 100644 --- a/generator/config/config.go +++ b/generator/config/config.go @@ -3,7 +3,6 @@ package config // Config represents the generation config for a dynamic struct. type Config interface { - // Number returns the number configuration. Number() NumberRangeConfig @@ -57,4 +56,7 @@ type Config interface { // Copy returns a copy of the configuration. Copy() Config + + // SetFrom sets the configuration from another configuration. + SetFrom(cfg Config) } diff --git a/generator/config/dstruct.config.go b/generator/config/dstruct.config.go index aacc632..e52e089 100644 --- a/generator/config/dstruct.config.go +++ b/generator/config/dstruct.config.go @@ -5,7 +5,6 @@ type DstructConfig struct { SliceConfig SliceConfig NumberConfig NumberRangeConfig DateConfig DateRangeConfig - children []Config } // NewDstructConfig is a constructor for DstructConfig. @@ -19,21 +18,6 @@ func NewDstructConfig() *DstructConfig { var _ Config = &DstructConfig{} -// Copy implements Config. -// TODO: implement this -func (c *DstructConfig) Copy() Config { - newConfig := &DstructConfig{} - if c.SliceConfig != nil { - newConfig.SliceConfig = c.SliceConfig.Copy() - } - - if c.NumberConfig != nil { - newConfig.NumberConfig = c.NumberConfig.Copy() - } - - return newConfig -} - // Slice implements Config.Slice. func (c *DstructConfig) Slice() SliceConfig { return c.SliceConfig @@ -52,9 +36,6 @@ func (c *DstructConfig) Number() NumberRangeConfig { // SetSliceLength implements Config.SetSliceLength. func (c *DstructConfig) SetSliceLength(min, max int) Config { c.SliceConfig.SetLengthRange(min, max) - for _, sub := range c.children { - sub.SetSliceLength(min, max) - } return c } @@ -135,3 +116,30 @@ func (c *DstructConfig) SetUIntPtr(min, max uintptr) Config { c.NumberConfig.UIntPtr().SetRange(min, max) return c } + +// Copy implements Config. +func (c *DstructConfig) Copy() Config { + if c == nil { + return nil + } + + newConfig := &DstructConfig{} + if c.SliceConfig != nil { + newConfig.SliceConfig = c.SliceConfig.Copy() + } + + if c.NumberConfig != nil { + newConfig.NumberConfig = c.NumberConfig.Copy() + } + + return newConfig +} + +// SetFrom implements Config. +func (c *DstructConfig) SetFrom(cfg Config) { + if cfg == nil { + return + } + + c.NumberConfig.SetFrom(cfg.Number()) +} diff --git a/generator/config/dstruct.number.range.config.go b/generator/config/dstruct.number.range.config.go index 088e5b0..f0cd45e 100644 --- a/generator/config/dstruct.number.range.config.go +++ b/generator/config/dstruct.number.range.config.go @@ -109,3 +109,7 @@ func (nc *DstructNumberRangeConfig) Copy() NumberRangeConfig { *numberConfigCopy = *nc return numberConfigCopy } + +func (nc *DstructNumberRangeConfig) SetFrom(c NumberRangeConfig) { + *nc = *c.(*DstructNumberRangeConfig) +} diff --git a/generator/config/number.range.config.go b/generator/config/number.range.config.go index 62c33e4..66b930d 100644 --- a/generator/config/number.range.config.go +++ b/generator/config/number.range.config.go @@ -7,13 +7,10 @@ type Number interface { } // numRange represents the range of a number. -type numRange[n Number] struct { +type NumberRange[n Number] struct { min n max n } -type NumberRange[n Number] struct { - numRange[n] -} func (nr *NumberRange[n]) Range() (n, n) { return nr.min, nr.max @@ -47,7 +44,7 @@ func (nr *NumberRange[n]) RangeRef() (*n, *n) { } func numberRange[N Number](min, max N) NumberRange[N] { - return NumberRange[N]{numRange[N]{min, max}} + return NumberRange[N]{min, max} } // NumberRangeConfig represents the configuration for a number range within a dynamic struct. @@ -93,4 +90,7 @@ type NumberRangeConfig interface { // Copy returns a copy of the configuration. Copy() NumberRangeConfig + + // SetFrom sets the configuration from another configuration. + SetFrom(cfg NumberRangeConfig) } diff --git a/generator/core/date.go b/generator/core/date.go index d57920f..a1c5f2c 100644 --- a/generator/core/date.go +++ b/generator/core/date.go @@ -10,7 +10,6 @@ import ( const ISO8601 string = "2018-03-20T09:12:28Z" func GenerateDateTimeFunc() generator.GenerationFunction { - // TODO have a proper implementation return &coreGenerationFunction{ _func: func(parameters ...any) any { @@ -18,21 +17,18 @@ func GenerateDateTimeFunc() generator.GenerationFunction { }, kind: NewKind(time.Time{}), } - } func GenerateDateTimeBetweenDatesFunc(dc config.DateRangeConfig) generator.GenerationFunction { - // TODO have a proper implementation return &coreGenerationFunction{ _func: func(parameters ...any) any { timeDiffInSeconds := dc.GetEndDate().Sub(dc.GetStartDate()).Seconds() - return dc.GetStartDate().Add(time.Second * time.Duration(generateNum(0, timeDiffInSeconds))) - + return dc.GetStartDate(). + Add(time.Second * time.Duration(generateNum(0, timeDiffInSeconds))) }, kind: NewKind(time.Time{}), } - } type DateFunctionHolderFunc func(config.DateRangeConfig) generator.GenerationFunction @@ -41,7 +37,10 @@ type DateFunctionHolder struct { BaseFunctionHolder } -func NewDateFunctionHolder(f DateFunctionHolderFunc, cfg config.DateRangeConfig) *DateFunctionHolder { +func NewDateFunctionHolder( + f DateFunctionHolderFunc, + cfg config.DateRangeConfig, +) *DateFunctionHolder { return &DateFunctionHolder{ BaseFunctionHolder: BaseFunctionHolder{ config: config.NewDstructConfigBuilder().WithDateRangeConfig(cfg).Build(), @@ -55,8 +54,8 @@ func DefaultDateFunctionHolder(cfg config.DateRangeConfig) *DateFunctionHolder { return NewDateFunctionHolder(GenerateDateTimeBetweenDatesFunc, cfg) } -func (c *DateFunctionHolder) Copy() FunctionHolder { +func (c *DateFunctionHolder) Copy(cfg config.Config) FunctionHolder { return &DateFunctionHolder{ - BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + BaseFunctionHolder: c.BaseFunctionHolder.Copy(cfg), } } diff --git a/generator/core/default.generation.functions.go b/generator/core/default.generation.functions.go index 1d21eff..d129644 100644 --- a/generator/core/default.generation.functions.go +++ b/generator/core/default.generation.functions.go @@ -1,6 +1,7 @@ package core import ( + "fmt" "reflect" "github.com/MartinSimango/dstruct/generator/config" @@ -8,18 +9,23 @@ import ( type DefaultGenerationFunctions map[reflect.Kind]FunctionHolder -func (d DefaultGenerationFunctions) Copy(kind reflect.Kind) (dgf DefaultGenerationFunctions) { +func (d DefaultGenerationFunctions) Copy( + cfg config.Config, + kind reflect.Kind, +) (dgf DefaultGenerationFunctions) { // look at the kind and only return what needs to be copied dgf = make(DefaultGenerationFunctions) - switch kind { - case reflect.Struct, reflect.Slice, reflect.Ptr: + if d[kind] == nil { + // Copy ll the functions if the kind is not found + // TODO: check if this can ever happen + fmt.Println("Kind not found", kind) for k, v := range d { - dgf[k] = v.Copy() + dgf[k] = v.Copy(cfg) } - default: - dgf[kind] = d[kind].Copy() + return } - return dgf + dgf[kind] = d[kind].Copy(cfg) + return } func NewDefaultGenerationFunctions(cfg config.Config) DefaultGenerationFunctions { diff --git a/generator/core/fixed.function.holder.go b/generator/core/fixed.function.holder.go index 12595fd..7edad14 100644 --- a/generator/core/fixed.function.holder.go +++ b/generator/core/fixed.function.holder.go @@ -2,6 +2,7 @@ package core import ( "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" ) type FixedFunctionHolderFunc[T any] func(value T) generator.GenerationFunction @@ -20,7 +21,6 @@ func NewFixedFunctionHolder[T any](f FixedFunctionHolderFunc[T], value T) *Fixed }, value: value, } - } // Override @@ -33,9 +33,9 @@ func (c *FixedFunctionHolder[T]) GetFunction() generator.GenerationFunction { return c.generationFunction } -func (c *FixedFunctionHolder[T]) Copy() FunctionHolder { +func (c *FixedFunctionHolder[T]) Copy(cfg config.Config) FunctionHolder { return &FixedFunctionHolder[T]{ - BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + BaseFunctionHolder: c.BaseFunctionHolder.Copy(cfg), value: c.value, } } diff --git a/generator/core/function.holder.go b/generator/core/function.holder.go index 3f1bc5f..c458b7b 100644 --- a/generator/core/function.holder.go +++ b/generator/core/function.holder.go @@ -12,7 +12,7 @@ type FunctionHolder interface { SetFunction(generationFunction generator.GenerationFunction) GetConfig() config.Config SetConfig(cfg config.Config) - Copy() FunctionHolder + Copy(cfg config.Config) FunctionHolder Kind() reflect.Kind } @@ -39,21 +39,18 @@ func (c *BaseFunctionHolder) GetConfig() config.Config { func (c *BaseFunctionHolder) SetConfig(cfg config.Config) { c.config = cfg - c.generationFunction = c.resetFunction(cfg) -} - -func (c *BaseFunctionHolder) Copy() BaseFunctionHolder { - // TODO: should this panic? - var configCopy config.Config - if c.config != nil { - configCopy = c.config.Copy() + if c.resetFunction != nil { + c.generationFunction = c.resetFunction(cfg) } +} - return BaseFunctionHolder{ - fun: c.fun, - config: configCopy, - generationFunction: c.resetFunction(configCopy), +func (c *BaseFunctionHolder) Copy(cfg config.Config) (bf BaseFunctionHolder) { + bf = BaseFunctionHolder{ + fun: c.fun, + resetFunction: c.resetFunction, } + bf.SetConfig(cfg) + return } func (c *BaseFunctionHolder) Kind() reflect.Kind { diff --git a/generator/core/generated.field.go b/generator/core/generated.field.go index 1c02664..b41cdd2 100644 --- a/generator/core/generated.field.go +++ b/generator/core/generated.field.go @@ -15,11 +15,12 @@ type GeneratedFieldConfig struct { GenerationConfig config.Config } -func (gf *GeneratedFieldConfig) Copy(kind reflect.Kind) (gfc GeneratedFieldConfig) { +func (gf *GeneratedFieldConfig) Copy(kind reflect.Kind) GeneratedFieldConfig { + cfg := gf.GenerationConfig.Copy() return GeneratedFieldConfig{ - GenerationFunctions: gf.GenerationFunctions.Copy(kind), + GenerationFunctions: gf.GenerationFunctions.Copy(cfg, kind), GenerationSettings: gf.GenerationSettings, - GenerationConfig: gf.GenerationConfig.Copy(), + GenerationConfig: cfg, } } @@ -94,14 +95,17 @@ func (field *GeneratedField) customTypeFunctionHolder() FunctionHolder { } func (field *GeneratedField) SetConfig(cfg config.Config) { - kind := field.Value.Kind() - if field.IsCustomType() { - field.customTypeFunctionHolder().SetConfig(cfg) - } else if field.Config.GenerationFunctions[kind] != nil { - field.Config.GenerationFunctions[kind].SetConfig(cfg) - } else { - field.Config.SetConfig(cfg) - } + field.Config.GenerationConfig.SetFrom(cfg) + + // kind := field.Value.Kind() + // if field.IsCustomType() { + // field.customTypeFunctionHolder().SetConfig(cfg) + // } else if field.Config.GenerationFunctions[kind] != nil { + // fmt.Println("Setting config for kind: ", kind) + // field.Config.GenerationFunctions[kind].SetConfig(cfg) + // } else { + // field.Config.SetConfig(cfg) + // } } func (field *GeneratedField) SetGenerationSettings(settings config.GenerationSettings) { diff --git a/generator/core/no.arg.function.holder.go b/generator/core/no.arg.function.holder.go index eb36987..699d2d2 100644 --- a/generator/core/no.arg.function.holder.go +++ b/generator/core/no.arg.function.holder.go @@ -13,7 +13,9 @@ type FunctionHolderWithNoArgs struct { var _ FunctionHolder = &FunctionHolderWithNoArgs{} -func NewFunctionHolderNoArgs(generationFunction generator.GenerationFunction) *FunctionHolderWithNoArgs { +func NewFunctionHolderNoArgs( + generationFunction generator.GenerationFunction, +) *FunctionHolderWithNoArgs { return &FunctionHolderWithNoArgs{ BaseFunctionHolder: BaseFunctionHolder{ generationFunction: generationFunction, @@ -27,8 +29,8 @@ func (c *FunctionHolderWithNoArgs) GetFunction() generator.GenerationFunction { func (c *FunctionHolderWithNoArgs) SetConfig(config config.Config) {} -func (c *FunctionHolderWithNoArgs) Copy() FunctionHolder { +func (c *FunctionHolderWithNoArgs) Copy(cfg config.Config) FunctionHolder { return &FunctionHolderWithNoArgs{ - BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + BaseFunctionHolder: c.BaseFunctionHolder.Copy(cfg), } } diff --git a/generator/core/number.function.holder.go b/generator/core/number.function.holder.go index 90b419e..e5f0a69 100644 --- a/generator/core/number.function.holder.go +++ b/generator/core/number.function.holder.go @@ -1,6 +1,8 @@ package core import ( + "fmt" + "github.com/MartinSimango/dstruct/generator" "github.com/MartinSimango/dstruct/generator/config" ) @@ -22,6 +24,7 @@ func NewNumberFunctionHolder( config: config.NewDstructConfigBuilder().WithNumberConfig(cfg).Build(), fun: f, resetFunction: func(cfg config.Config) generator.GenerationFunction { + fmt.Println("Resetting number function holder") return f(cfg.Number()) }, generationFunction: f(cfg), @@ -31,9 +34,9 @@ func NewNumberFunctionHolder( return nfh } -func (c *NumberFunctionHolder) Copy() FunctionHolder { +func (c *NumberFunctionHolder) Copy(cfg config.Config) FunctionHolder { nf := &NumberFunctionHolder{ - BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + BaseFunctionHolder: c.BaseFunctionHolder.Copy(cfg), } return nf } diff --git a/generator/core/slice.function.holder.go b/generator/core/slice.function.holder.go index 2f6fd2b..3b27059 100644 --- a/generator/core/slice.function.holder.go +++ b/generator/core/slice.function.holder.go @@ -15,7 +15,12 @@ type SliceFunctionHolder struct { var _ FunctionHolder = &SliceFunctionHolder{} -func NewSliceFunctionHolder(f SliceFunctionHolderFunc, field *GeneratedField, cfg config.Config, generationFunctions DefaultGenerationFunctions) *SliceFunctionHolder { +func NewSliceFunctionHolder( + f SliceFunctionHolderFunc, + field *GeneratedField, + cfg config.Config, + generationFunctions DefaultGenerationFunctions, +) *SliceFunctionHolder { return &SliceFunctionHolder{ BaseFunctionHolder: BaseFunctionHolder{ config: cfg, @@ -35,9 +40,9 @@ func (c *SliceFunctionHolder) GetFunction() generator.GenerationFunction { return c.generationFunction } -func (c *SliceFunctionHolder) Copy() FunctionHolder { +func (c *SliceFunctionHolder) Copy(cfg config.Config) FunctionHolder { return &SliceFunctionHolder{ - BaseFunctionHolder: c.BaseFunctionHolder.Copy(), + BaseFunctionHolder: c.BaseFunctionHolder.Copy(cfg), // TODO address this // generationFunctions: c.generationFunctions.Copy(reflect.Slice), } diff --git a/go.mod b/go.mod index 3689aa6..33961c4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/MartinSimango/dstruct -go 1.21.3 +go 1.23.2 retract ( v1.1.1 From ec007f927cddd4a0585c7f74e9f6b85c9645cbbc Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Wed, 16 Oct 2024 21:23:41 +0200 Subject: [PATCH 17/31] document drelect package --- dreflect/dreflect.go | 64 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/dreflect/dreflect.go b/dreflect/dreflect.go index 9b2b379..cfaa69a 100644 --- a/dreflect/dreflect.go +++ b/dreflect/dreflect.go @@ -1,3 +1,5 @@ +// package dreflect adds more advanced reflection functionality to the standard reflect package by providing additional functions to convert between different types. + package dreflect import ( @@ -34,7 +36,13 @@ func ConvertToType[T any](val any) T { func Convert(val reflect.Value, typ reflect.Type) reflect.Value { defer func() { if r := recover(); r != nil { - panic(fmt.Sprintf("dreflect.Convert: value of type %v cannot be converted to type %v", val.Type(), typ)) + panic( + fmt.Sprintf( + "dreflect.Convert: value of type %v cannot be converted to type %v", + val.Type(), + typ, + ), + ) } }() dst := reflect.New(typ).Elem() @@ -45,7 +53,6 @@ func Convert(val reflect.Value, typ reflect.Type) reflect.Value { } func convertibleTo(src, dst reflect.Type) bool { - return !src.ConvertibleTo(dst) && src.Kind() != reflect.Struct && src.Kind() != reflect.Slice && @@ -54,9 +61,14 @@ func convertibleTo(src, dst reflect.Type) bool { } func convert(src reflect.Value, dst reflect.Value) reflect.Value { - if convertibleTo(src.Type(), dst.Type()) { - panic(fmt.Sprintf("dreflect.Convert: value of type %s cannot be converted to type %s", src.Type(), dst.Type())) + panic( + fmt.Sprintf( + "dreflect.Convert: value of type %s cannot be converted to type %s", + src.Type(), + dst.Type(), + ), + ) } switch src.Kind() { @@ -67,13 +79,26 @@ func convert(src reflect.Value, dst reflect.Value) reflect.Value { sNum := srcStruct.NumField() if dNum != sNum { - panic(fmt.Sprintf("dreflect.Convert: Number of struct fields differ %s has %d subfield(s) and %s has %d subfield(s)", src.Type(), sNum, dst.Type(), dNum)) - + panic( + fmt.Sprintf( + "dreflect.Convert: Number of struct fields differ %s has %d subfield(s) and %s has %d subfield(s)", + src.Type(), + sNum, + dst.Type(), + dNum, + ), + ) } for i := 0; i < newStruct.NumField(); i++ { f := srcStruct.Field(i) if srcStruct.Type().Field(i).Name != newStruct.Type().Field(i).Name { - panic(fmt.Sprintf("dreflect.Convert: value of type %s cannot be converted to type %s", src.Type(), dst.Type())) + panic( + fmt.Sprintf( + "dreflect.Convert: value of type %s cannot be converted to type %s", + src.Type(), + dst.Type(), + ), + ) } fieldValue := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem() newField := convert(fieldValue, dst.Field(i)) @@ -100,7 +125,13 @@ func convert(src reflect.Value, dst reflect.Value) reflect.Value { return newSlice case reflect.Array: if src.Len() != dst.Len() { - panic(fmt.Sprintf("dreflect.Convert: value of type %s cannot be converted to type %s", src.Type(), dst.Type())) + panic( + fmt.Sprintf( + "dreflect.Convert: value of type %s cannot be converted to type %s", + src.Type(), + dst.Type(), + ), + ) } dstSliceType := GetSliceType(dst.Interface()) @@ -118,17 +149,26 @@ func convert(src reflect.Value, dst reflect.Value) reflect.Value { dstPointerValueType := dst.Type().Elem() srcPointerValue := reflect.ValueOf(GetUnderlyingPointerValue(src.Interface())) - if src.Type().Elem().Kind() != dst.Type().Elem().Kind() && src.Elem().Kind() >= 1 && src.Elem().Kind() <= 14 { - panic(fmt.Sprintf("dreflect.Convert: value of type %s cannot be converted to type %s", src.Type(), dst.Type())) + if src.Type().Elem().Kind() != dst.Type().Elem().Kind() && src.Elem().Kind() >= 1 && + src.Elem().Kind() <= 14 { + panic( + fmt.Sprintf( + "dreflect.Convert: value of type %s cannot be converted to type %s", + src.Type(), + dst.Type(), + ), + ) } retPointer := reflect.New(dst.Type()) - //ensure that new pointer uses same memory address as src pointer + // ensure that new pointer uses same memory address as src pointer reflect.NewAt(src.Type(), unsafe.Pointer(retPointer.Elem().UnsafeAddr())).Elem(). Set(src) // now copy over the values from the src - retPointer.Elem().Elem().Set(convert(srcPointerValue, reflect.New(dstPointerValueType).Elem())) + retPointer.Elem(). + Elem(). + Set(convert(srcPointerValue, reflect.New(dstPointerValueType).Elem())) return retPointer.Elem() From c04125b6500c473306a0af9e946d5b371d5af34c Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Thu, 17 Oct 2024 14:38:28 +0200 Subject: [PATCH 18/31] Document function --- tree.builder.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tree.builder.go b/tree.builder.go index 1055a35..a3ca735 100644 --- a/tree.builder.go +++ b/tree.builder.go @@ -108,6 +108,7 @@ func (dsb *treeBuilderImpl) RemoveField(name string) Builder { return dsb } +// GetField implements Builder.GetField func (dsb *treeBuilderImpl) GetField(field string) Builder { if node := dsb.getNode(field); node != nil { return newBuilderFromNode(node, false) From 8b5f20b0aa9a3a4bf863961804631ee965935824 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Thu, 17 Oct 2024 23:20:43 +0200 Subject: [PATCH 19/31] add builder examples and fix bugs within builder --- .gitignore | 1 - builder.go | 8 +--- examples/builder/add-field/main.go | 39 ++++++++++++++++++ examples/builder/get-field/main.go | 51 +++++++++++++++++++++++ examples/builder/remove-field/main.go | 25 ++++++++++++ examples/task/main.go | 9 +++++ field.go | 3 +- node.go | 4 ++ tree.builder.go | 58 +++++++++++++++++++-------- 9 files changed, 173 insertions(+), 25 deletions(-) create mode 100644 examples/builder/add-field/main.go create mode 100644 examples/builder/get-field/main.go create mode 100644 examples/builder/remove-field/main.go create mode 100644 examples/task/main.go diff --git a/.gitignore b/.gitignore index 1a3e026..cbeafd6 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,3 @@ main/ dist/ .DS_Store -examples/ diff --git a/builder.go b/builder.go index 1c85f97..4904cc3 100644 --- a/builder.go +++ b/builder.go @@ -10,14 +10,10 @@ type Builder interface { // Build returns a DynamicStructModifier instance. Build() DynamicStructModifier - // GetField returns a builder instance of the subfield of the struct that is currently being built. + // GetField returns a builder instance of the subfield of the struct that is currently being built. It will panic if the + // field is not a struct or does not fully dereference to a struct value. It returns nil if field does not exist. GetField(name string) Builder - // GetFieldCopy returns a copy of a builder instance of the subfield of the struct that is currently being built. - // - // Deprecated: this method will be removed use NewBuilderFromField instead. - GetFieldCopy(name string) Builder - // GetNewBuilderFromField returns a new builder instance where the subfield of the struct "field" is the root of the struct. NewBuilderFromField(field string) Builder diff --git a/examples/builder/add-field/main.go b/examples/builder/add-field/main.go new file mode 100644 index 0000000..e3812c3 --- /dev/null +++ b/examples/builder/add-field/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + + "github.com/MartinSimango/dstruct" +) + +type Address struct { + Street string `json:"street"` +} + +type Person struct { + Name string `json:"name"` + Age int `json:"age"` + Address Address `json:"address"` +} + +type Phone struct { + Number string `json:"number"` +} + +func main() { + builder := dstruct.NewBuilder(). + AddField("Person", Person{Name: "Martin", Age: 25, Address: Address{Street: "Jackson Street"}}, `json:"person"`). + AddField("Job", "Software Developer", "") + + fmt.Printf("Struct after adding fields:\n%+v\n\n", builder.Build()) + + // Add fields to the nested struct + + builder.GetField("Person"). + AddField("Height", 175, `json:"height"`). + AddEmbeddedField(Phone{Number: "123456789"}, "") // Add an embedded field + + builder.GetField("Person.Address").AddField("City", "New York", `json:"city"`) + + fmt.Printf("Struct after adding fields to the nested struct:\n%+v\n", builder.Build()) +} diff --git a/examples/builder/get-field/main.go b/examples/builder/get-field/main.go new file mode 100644 index 0000000..b452bb7 --- /dev/null +++ b/examples/builder/get-field/main.go @@ -0,0 +1,51 @@ +package main + +import ( + "fmt" + + "github.com/MartinSimango/dstruct" +) + +type Address struct { + Street string `json:"street"` +} +type Phone struct { + Number string `json:"number"` +} +type Person struct { + Name string `json:"name"` + Age int `json:"age"` + Address Address `json:"address"` +} + +func main() { + builder := dstruct.NewBuilder(). + AddField("Person", &Person{Name: "Martin", Age: 25, Address: Address{}}, `json:"person"`) + + builder.GetField("Person").AddField("Height", 175, `json:"height"`) + + addressBuilder := builder.GetField("Person.Address") + + fmt.Printf("Address field:\n%+v\n\n", addressBuilder.Build()) + + streetBuilder := addressBuilder.GetField( + "Street", + ) // this will be an empty struct because Street is a primitive type + + fmt.Printf("Street field:\n%+v\n\n", streetBuilder.Build()) + + fmt.Println(addressBuilder.AddField("Cool", 2, "").Build()) + + fmt.Println(builder.Build()) + + // fmt.Printf("Struct after adding fields:\n%+v\n\n", builder.Build()) + // + // // Add fields to the nested struct + // + // builder.GetField("Person"). + // AddField("Height", 175, `json:"height"`) + // + // builder.GetField("Person.Address").AddField("City", "New York", `json:"city"`) + // + // fmt.Printf("Struct after adding fields to the nested struct:\n%+v\n", builder.Build()) +} diff --git a/examples/builder/remove-field/main.go b/examples/builder/remove-field/main.go new file mode 100644 index 0000000..276aa0b --- /dev/null +++ b/examples/builder/remove-field/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + + "github.com/MartinSimango/dstruct" +) + +type Person struct { + Name string `json:"name"` + Age int `json:"age"` +} + +func main() { + var builder dstruct.Builder = dstruct.NewBuilder(). + AddField("Person", Person{Name: "Martin", Age: 25}, `json:"person"`). + AddField("Job", "Software Developer", "") + + fmt.Printf("Struct before removing fields:\n%+v\n", builder.Build().Instance()) + + builder.RemoveField("Person.Age") + builder.RemoveField("Job") + + fmt.Printf("Struct after removing fields:\n%+v\n", builder.Build().Instance()) +} diff --git a/examples/task/main.go b/examples/task/main.go new file mode 100644 index 0000000..fa0842c --- /dev/null +++ b/examples/task/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "fmt" +) + +func main() { + fmt.Println("Testing task") +} diff --git a/field.go b/field.go index b6d401d..e2b8c55 100644 --- a/field.go +++ b/field.go @@ -15,8 +15,9 @@ type structField struct { pkgPath string anonymous bool jsonName string - ptrDepth int fullyQualifiedName string + ptrDepth int + ptrKind reflect.Kind structIndex *int numberOfSubFields *int } diff --git a/node.go b/node.go index 5431723..0fa09d7 100644 --- a/node.go +++ b/node.go @@ -26,6 +26,10 @@ func (n *Node[T]) HasChildren() bool { return len(n.children) > 0 } +func (n *Node[T]) HasChild(name string) bool { + return n.children[name] != nil +} + func (n *Node[T]) Copy() *Node[T] { newNode := &Node[T]{ data: new(T), diff --git a/tree.builder.go b/tree.builder.go index a3ca735..aff7b8a 100644 --- a/tree.builder.go +++ b/tree.builder.go @@ -55,9 +55,9 @@ func ExtendStruct(val any) *treeBuilderImpl { switch value.Kind() { case reflect.Struct: - b.addStructFields(value, b.root, 0, false) + b.addStructFields(value, b.root, 0) case reflect.Ptr: - b.addPtrField(value, b.root, false) + b.addPtrField(value, b.root) } return b @@ -82,6 +82,9 @@ func resetNodeFieldsFQN(node *Node[structField]) *Node[structField] { } func (dsb *treeBuilderImpl) AddField(name string, value interface{}, tag string) Builder { + if dsb.root.HasChild(name) { + panic(fmt.Sprintf("Field '%s' already exists", name)) + } dsb.addFieldToTree(name, value, "", false, reflect.StructTag(tag), dsb.root) return dsb } @@ -111,6 +114,28 @@ func (dsb *treeBuilderImpl) RemoveField(name string) Builder { // GetField implements Builder.GetField func (dsb *treeBuilderImpl) GetField(field string) Builder { if node := dsb.getNode(field); node != nil { + fmt.Println( + "Node kind is", + field, + node.data.ptrDepth, + node.data.ptrKind, + node.data.typ.Kind(), + ) + if node.data.ptrDepth > 0 { + if node.data.ptrKind != reflect.Struct { + panic( + fmt.Sprintf( + "Cannot get field '%s' because it a points to a non-struct value.", + field, + ), + ) + } + } else if node.data.typ.Kind() != reflect.Struct { + panic(fmt.Sprintf("Cannot get field '%s' because it is not a struct or it points to a non-struct value", field)) + } + // if node.data.ptrKind != reflect.Struct { + + // node. / return newBuilderFromNode(node, false) } return nil @@ -205,9 +230,10 @@ func (dsb *treeBuilderImpl) addFieldToTree( // don't add struct fields if special kind switch value.Kind() { case reflect.Struct: - field.typ = dsb.addStructFields(value, root.children[name], 0, anonymous) + field.typ = dsb.addStructFields(value, root.children[name], 0) + case reflect.Ptr: - field.typ = dsb.addPtrField(value, root.children[name], anonymous) + field.typ = dsb.addPtrField(value, root.children[name]) } return field.typ @@ -294,16 +320,14 @@ func setPointerFieldValue(field reflect.Value, currentNode *Node[structField]) { } f := field - if currentNode.data.numberOfSubFields != nil { // node is a struct with subfields that needs to be dereferenced - for i := 0; i < currentNode.data.ptrDepth; i++ { - // We don't use f.Set(reflect.New(f.Type().Elem())) because it panics when the field is unexported - // se we need to access the memory address of the field and set the value which bypasses the panic - reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())). - Elem(). - Set(reflect.New(f.Type().Elem())) - // f.Set(reflect.New(f.Type().Elem())) - f = f.Elem() - } + for i := 0; i < currentNode.data.ptrDepth; i++ { + // We don't use f.Set(reflect.New(f.Type().Elem())) because it panics when the field is unexported + // se we need to access the memory address of the field and set the value which bypasses the panic + reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())). + Elem(). + Set(reflect.New(f.Type().Elem())) + // f.Set(reflect.New(f.Type().Elem())) + f = f.Elem() } switch f.Kind() { @@ -319,7 +343,6 @@ func (dsb *treeBuilderImpl) addStructFields( strct reflect.Value, root *Node[structField], ptrDepth int, - anon bool, ) reflect.Type { var structFields []reflect.StructField @@ -375,7 +398,6 @@ func getPtrValue(value reflect.Value, ptrDepth int) (reflect.Value, int) { func (dsb *treeBuilderImpl) addPtrField( value reflect.Value, node *Node[structField], - anonymous bool, ) reflect.Type { if value.IsNil() { return reflect.TypeOf(value.Interface()) @@ -384,9 +406,11 @@ func (dsb *treeBuilderImpl) addPtrField( ptrValue, ptrDepth := getPtrValue(value, 0) node.data.ptrDepth = ptrDepth + node.data.ptrKind = ptrValue.Kind() + switch ptrValue.Kind() { case reflect.Struct: - return dsb.addStructFields(ptrValue, node, ptrDepth, anonymous) + return dsb.addStructFields(ptrValue, node, ptrDepth) } return reflect.TypeOf(value.Interface()) } From afa438e15bb4e0b65712e862ff7be758605e8aa8 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Thu, 17 Oct 2024 23:54:45 +0200 Subject: [PATCH 20/31] rename folders and clean up some code --- .../{add-field => adding-a-field}/main.go | 0 examples/builder/get-field/main.go | 51 ------------------- examples/builder/getting-a-field/main.go | 40 +++++++++++++++ .../main.go | 3 +- tree.builder.go | 9 +--- 5 files changed, 43 insertions(+), 60 deletions(-) rename examples/builder/{add-field => adding-a-field}/main.go (100%) delete mode 100644 examples/builder/get-field/main.go create mode 100644 examples/builder/getting-a-field/main.go rename examples/builder/{remove-field => removing-a-field}/main.go (88%) diff --git a/examples/builder/add-field/main.go b/examples/builder/adding-a-field/main.go similarity index 100% rename from examples/builder/add-field/main.go rename to examples/builder/adding-a-field/main.go diff --git a/examples/builder/get-field/main.go b/examples/builder/get-field/main.go deleted file mode 100644 index b452bb7..0000000 --- a/examples/builder/get-field/main.go +++ /dev/null @@ -1,51 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/MartinSimango/dstruct" -) - -type Address struct { - Street string `json:"street"` -} -type Phone struct { - Number string `json:"number"` -} -type Person struct { - Name string `json:"name"` - Age int `json:"age"` - Address Address `json:"address"` -} - -func main() { - builder := dstruct.NewBuilder(). - AddField("Person", &Person{Name: "Martin", Age: 25, Address: Address{}}, `json:"person"`) - - builder.GetField("Person").AddField("Height", 175, `json:"height"`) - - addressBuilder := builder.GetField("Person.Address") - - fmt.Printf("Address field:\n%+v\n\n", addressBuilder.Build()) - - streetBuilder := addressBuilder.GetField( - "Street", - ) // this will be an empty struct because Street is a primitive type - - fmt.Printf("Street field:\n%+v\n\n", streetBuilder.Build()) - - fmt.Println(addressBuilder.AddField("Cool", 2, "").Build()) - - fmt.Println(builder.Build()) - - // fmt.Printf("Struct after adding fields:\n%+v\n\n", builder.Build()) - // - // // Add fields to the nested struct - // - // builder.GetField("Person"). - // AddField("Height", 175, `json:"height"`) - // - // builder.GetField("Person.Address").AddField("City", "New York", `json:"city"`) - // - // fmt.Printf("Struct after adding fields to the nested struct:\n%+v\n", builder.Build()) -} diff --git a/examples/builder/getting-a-field/main.go b/examples/builder/getting-a-field/main.go new file mode 100644 index 0000000..51d04f5 --- /dev/null +++ b/examples/builder/getting-a-field/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + + "github.com/MartinSimango/dstruct" +) + +type Address struct { + Street string `json:"street"` +} +type Phone struct { + Number string `json:"number"` +} +type Person struct { + Name string `json:"name"` + Age int `json:"age"` + Address Address `json:"address"` +} + +func main() { + builder := dstruct.NewBuilder(). + AddField("Person", &Person{Name: "Martin", Age: 25, Address: Address{Street: "street"}}, `json:"person"`) + + addressBuilder := builder.GetField("Person.Address") + addressBuilder.AddField("City", "New York", "") + + fmt.Printf("Address field after adding City:\n%+v\n\n", addressBuilder.Build()) + fmt.Printf("Struct after modifying address field:\n%+v\n\n", builder.Build()) + + // Get a copy of the address struct - this does not modify the original struct + addressBuilderCopy := builder.NewBuilderFromField("Person.Address") + addressBuilderCopy.AddField("PostalCode", "12345", "") + + fmt.Printf( + "Copy of Address field after adding PostalCode:\n%+v\n\n", + addressBuilderCopy.Build(), + ) + fmt.Printf("Struct after modifying address field (No PostalCode):\n%+v\n\n", builder.Build()) +} diff --git a/examples/builder/remove-field/main.go b/examples/builder/removing-a-field/main.go similarity index 88% rename from examples/builder/remove-field/main.go rename to examples/builder/removing-a-field/main.go index 276aa0b..738cda4 100644 --- a/examples/builder/remove-field/main.go +++ b/examples/builder/removing-a-field/main.go @@ -12,12 +12,13 @@ type Person struct { } func main() { - var builder dstruct.Builder = dstruct.NewBuilder(). + builder := dstruct.NewBuilder(). AddField("Person", Person{Name: "Martin", Age: 25}, `json:"person"`). AddField("Job", "Software Developer", "") fmt.Printf("Struct before removing fields:\n%+v\n", builder.Build().Instance()) + // Can remove nested fields builder.RemoveField("Person.Age") builder.RemoveField("Job") diff --git a/tree.builder.go b/tree.builder.go index aff7b8a..6005268 100644 --- a/tree.builder.go +++ b/tree.builder.go @@ -114,13 +114,6 @@ func (dsb *treeBuilderImpl) RemoveField(name string) Builder { // GetField implements Builder.GetField func (dsb *treeBuilderImpl) GetField(field string) Builder { if node := dsb.getNode(field); node != nil { - fmt.Println( - "Node kind is", - field, - node.data.ptrDepth, - node.data.ptrKind, - node.data.typ.Kind(), - ) if node.data.ptrDepth > 0 { if node.data.ptrKind != reflect.Struct { panic( @@ -131,7 +124,7 @@ func (dsb *treeBuilderImpl) GetField(field string) Builder { ) } } else if node.data.typ.Kind() != reflect.Struct { - panic(fmt.Sprintf("Cannot get field '%s' because it is not a struct or it points to a non-struct value", field)) + panic(fmt.Sprintf("Cannot get field '%s' because it is not a struct or does not fully derefence to a struct value", field)) } // if node.data.ptrKind != reflect.Struct { From 57a1a35b7b16009dd9320cb056b0b8eff3688ffb Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Fri, 18 Oct 2024 09:39:46 +0200 Subject: [PATCH 21/31] finish adding examples for builder --- .gitignore | 2 + dstruct.generated.struct.go | 34 +++++----- examples/builder/adding-a-field/main.go | 24 +++++++ examples/builder/getting-a-field/main.go | 23 +++++++ examples/builder/removing-a-field/main.go | 22 +++++-- examples/util.go | 11 ++++ field.go | 64 ------------------- merger.go | 37 ++++++++--- modifier.go | 24 +++---- struct.field.go | 71 +++++++++++++++++++++ tree.builder.go | 78 +++++++++++------------ 11 files changed, 242 insertions(+), 148 deletions(-) create mode 100644 examples/util.go delete mode 100644 field.go create mode 100644 struct.field.go diff --git a/.gitignore b/.gitignore index cbeafd6..d6f0d46 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ main/ dist/ .DS_Store +future_ideas.txt + diff --git a/dstruct.generated.struct.go b/dstruct.generated.struct.go index fe6917a..1425e1c 100644 --- a/dstruct.generated.struct.go +++ b/dstruct.generated.struct.go @@ -336,10 +336,10 @@ func (gs *DStructGeneratedStruct[T]) addCustomType(customType CustomType) { } func (gs *DStructGeneratedStruct[T]) createGeneratedField( - field *Node[structField], + field *Node[StructField], kind reflect.Kind, ) *core.GeneratedField { - v := core.NewGeneratedField(field.data.fullyQualifiedName, + v := core.NewGeneratedField(field.data.qualifiedName, field.data.value, field.data.tag, gs.structConfig.Copy(kind), @@ -348,16 +348,16 @@ func (gs *DStructGeneratedStruct[T]) createGeneratedField( return v } -func (gs *DStructGeneratedStruct[T]) populateGeneratedFields(node *Node[structField]) { +func (gs *DStructGeneratedStruct[T]) populateGeneratedFields(node *Node[StructField]) { for _, field := range node.children { if customType := gs.customTypes[field.data.goType]; customType != nil { - gs.fieldContexts[field.data.fullyQualifiedName] = core.NewGeneratedFieldContext( + gs.fieldContexts[field.data.qualifiedName] = core.NewGeneratedFieldContext( gs.createGeneratedField(field, customType.Kind()), ) } else if field.HasChildren() { gs.populateGeneratedFields(field) } else { - gs.fieldContexts[field.data.fullyQualifiedName] = core.NewGeneratedFieldContext( + gs.fieldContexts[field.data.qualifiedName] = core.NewGeneratedFieldContext( gs.createGeneratedField(field, field.data.value.Kind()), ) } @@ -365,58 +365,58 @@ func (gs *DStructGeneratedStruct[T]) populateGeneratedFields(node *Node[structFi } func (gs *DStructGeneratedStruct[T]) propagateConfig( - node *Node[structField], + node *Node[StructField], cfg config.Config, ) { for _, field := range node.children { // Don't propagate changes to children nodes if the field is a custom type if field.HasChildren() && - !gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.IsCustomType() { + !gs.fieldContexts[field.data.qualifiedName].GeneratedField.IsCustomType() { gs.propagateConfig(field, cfg) } else { - gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.SetConfig(cfg) + gs.fieldContexts[field.data.qualifiedName].GeneratedField.SetConfig(cfg) } } } func (gs *DStructGeneratedStruct[T]) propagateSettings( - node *Node[structField], + node *Node[StructField], settings config.GenerationSettings, ) { for _, field := range node.children { if field.HasChildren() && - !gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.IsCustomType() { + !gs.fieldContexts[field.data.qualifiedName].GeneratedField.IsCustomType() { gs.propagateSettings(field, settings) } else { - gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.SetGenerationSettings(settings) + gs.fieldContexts[field.data.qualifiedName].GeneratedField.SetGenerationSettings(settings) } } } func (gs *DStructGeneratedStruct[T]) propagateGenerationFunctions( - node *Node[structField], + node *Node[StructField], functions core.DefaultGenerationFunctions, ) { for _, field := range node.children { if field.HasChildren() && - !gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.IsCustomType() { + !gs.fieldContexts[field.data.qualifiedName].GeneratedField.IsCustomType() { gs.propagateGenerationFunctions(field, functions) } else { - gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.SetGenerationFunctions(functions) + gs.fieldContexts[field.data.qualifiedName].GeneratedField.SetGenerationFunctions(functions) } } } func (gs *DStructGeneratedStruct[T]) propagateGenerationFunction( - node *Node[structField], + node *Node[StructField], functionHolder core.FunctionHolder, ) { for _, field := range node.children { if field.HasChildren() && - !gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.IsCustomType() { + !gs.fieldContexts[field.data.qualifiedName].GeneratedField.IsCustomType() { gs.propagateGenerationFunction(field, functionHolder) } else { - gs.fieldContexts[field.data.fullyQualifiedName].GeneratedField.SetGenerationFunction(functionHolder) + gs.fieldContexts[field.data.qualifiedName].GeneratedField.SetGenerationFunction(functionHolder) } } } diff --git a/examples/builder/adding-a-field/main.go b/examples/builder/adding-a-field/main.go index e3812c3..a87c6ca 100644 --- a/examples/builder/adding-a-field/main.go +++ b/examples/builder/adding-a-field/main.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/MartinSimango/dstruct" + "github.com/MartinSimango/dstruct/examples" ) type Address struct { @@ -36,4 +37,27 @@ func main() { builder.GetField("Person.Address").AddField("City", "New York", `json:"city"`) fmt.Printf("Struct after adding fields to the nested struct:\n%+v\n", builder.Build()) + + // Uncomment the lines below to see the panic + errorExample_1() + errorExample_2() +} + +func errorExample_1() { + defer examples.RecoverFromPanic() + builder := dstruct.NewBuilder(). + AddField("Person", Person{Name: "Martin", Age: 25, Address: Address{Street: "Jackson Street"}}, `json:"person"`). + AddField("job", "Software Developer", "") + + // This will panic because the field "job" is unexported + builder.Build() +} + +func errorExample_2() { + defer examples.RecoverFromPanic() + builder := dstruct.NewBuilder(). + AddField("Person", Person{Name: "Martin", Age: 25, Address: Address{Street: "Jackson Street"}}, `json:"person"`). + AddField("Job Titile", "Software Developer", "") + + builder.Build() // This will panic because the field "Job Titile" is an invalid struct field name } diff --git a/examples/builder/getting-a-field/main.go b/examples/builder/getting-a-field/main.go index 51d04f5..3b063a0 100644 --- a/examples/builder/getting-a-field/main.go +++ b/examples/builder/getting-a-field/main.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/MartinSimango/dstruct" + "github.com/MartinSimango/dstruct/examples" ) type Address struct { @@ -37,4 +38,26 @@ func main() { addressBuilderCopy.Build(), ) fmt.Printf("Struct after modifying address field (No PostalCode):\n%+v\n\n", builder.Build()) + + // Error Examples: + errorExample_1() + errorExample_2() +} + +func errorExample_1() { + defer examples.RecoverFromPanic() + builder := dstruct.NewBuilder(). + AddField("Person", Person{Name: "Martin", Age: 25, Address: Address{Street: "Jackson Street"}}, `json:"person"`) + + // This will panic because the field "People" does not exist + builder.GetField("People") +} + +func errorExample_2() { + defer examples.RecoverFromPanic() + builder := dstruct.NewBuilder(). + AddField("Person", Person{Name: "Martin", Age: 25, Address: Address{Street: "Jackson Street"}}, `json:"person"`) + + // This will panic beecause teh field "Street" is not a struct or doesn't fully dereference to a struct value + builder.GetField("Person.Address.Street") } diff --git a/examples/builder/removing-a-field/main.go b/examples/builder/removing-a-field/main.go index 738cda4..693868a 100644 --- a/examples/builder/removing-a-field/main.go +++ b/examples/builder/removing-a-field/main.go @@ -7,20 +7,30 @@ import ( ) type Person struct { - Name string `json:"name"` - Age int `json:"age"` + Name string `json:"name"` + Age int `json:"age"` + Phone Phone `json:"phone"` +} + +type Phone struct { + number string + model string } func main() { builder := dstruct.NewBuilder(). - AddField("Person", Person{Name: "Martin", Age: 25}, `json:"person"`). + AddField("Person", Person{Name: "Martin", Age: 25, Phone: Phone{number: "123456789", model: "iPhone 16"}}, `json:"person"`). AddField("Job", "Software Developer", "") - fmt.Printf("Struct before removing fields:\n%+v\n", builder.Build().Instance()) + // Builder.Instance() will return the struct instance of type any + + fmt.Printf("Struct before removing fields:\n%+v\n\n", builder.Build().Instance()) // Can remove nested fields - builder.RemoveField("Person.Age") - builder.RemoveField("Job") + builder.RemoveField("Person.Age"). + RemoveField("Job"). + RemoveField("UnknownField"). // this will simply be ignored + RemoveField("Person.Phone.number") fmt.Printf("Struct after removing fields:\n%+v\n", builder.Build().Instance()) } diff --git a/examples/util.go b/examples/util.go new file mode 100644 index 0000000..ec50a67 --- /dev/null +++ b/examples/util.go @@ -0,0 +1,11 @@ +package examples + +import ( + "fmt" +) + +func RecoverFromPanic() { + if r := recover(); r != nil { + fmt.Println("Recovered from panic:", r) + } +} diff --git a/field.go b/field.go deleted file mode 100644 index e2b8c55..0000000 --- a/field.go +++ /dev/null @@ -1,64 +0,0 @@ -package dstruct - -import ( - "fmt" - "reflect" - "strconv" -) - -type structField struct { - name string - tag reflect.StructTag - value reflect.Value - typ reflect.Type - goType reflect.Type - pkgPath string - anonymous bool - jsonName string - fullyQualifiedName string - ptrDepth int - ptrKind reflect.Kind - structIndex *int - numberOfSubFields *int -} - -func (f structField) GetFieldName() string { - return f.name -} - -func (f structField) GetValue() any { - return f.value.Interface() -} - -func (f structField) GetType() reflect.Type { - return f.typ -} - -func (f structField) GetFieldFullyQualifiedName() string { - return f.fullyQualifiedName -} - -func (f structField) GetTag(t string) string { - return f.tag.Get(t) -} - -func (f structField) GetJsonName() string { - return f.jsonName -} - -func (f structField) GetEnumValues() (enumValues map[string]int) { - enum, ok := f.tag.Lookup("enum") - if ok { - numEnums, err := strconv.Atoi(enum) - if err != nil { - return - } - enumValues = make(map[string]int) - for i := 1; i <= numEnums; i++ { - if key := f.tag.Get(fmt.Sprintf("enum_%d", i)); key != "" { - enumValues[key] = i - } - } - } - return -} diff --git a/merger.go b/merger.go index f730819..1df6d76 100644 --- a/merger.go +++ b/merger.go @@ -15,7 +15,10 @@ func MergeStructs(strcts ...interface{}) (a any, err error) { for i := 0; i < len(strcts); i++ { if reflect.ValueOf(strcts[i]).Kind() != reflect.Struct { - return nil, fmt.Errorf("failed to merged structs: %d interface is not a struct ", (i + 1)) + return nil, fmt.Errorf( + "failed to merged structs: %d interface is not a struct ", + (i + 1), + ) } } @@ -36,7 +39,6 @@ func MergeStructs(strcts ...interface{}) (a any, err error) { // TODO clean this function up func mergeStructs(left, right Builder, parentKind reflect.Kind) (any, error) { - // struct to be returned newStruct := ExtendStruct(left.Build().Instance()) @@ -49,21 +51,20 @@ func mergeStructs(left, right Builder, parentKind reflect.Kind) (any, error) { newStruct.AddField(elementName, field.data.value.Interface(), string(field.data.tag)) continue } - if err := validateTypes(field.data.value, cV.data.value, field.data.fullyQualifiedName); err != nil { + if err := validateTypes(field.data.value, cV.data.value, field.data.qualifiedName); err != nil { return nil, err } if field.data.value.Kind() == reflect.Slice { vSliceType := dreflect.GetSliceType(field.data.value.Interface()) cVSliceType := dreflect.GetSliceType(cV.data.value.Interface()) - if err := validateSliceTypes(vSliceType, cVSliceType, field.data.value, cV.data.value, field.data.fullyQualifiedName); err != nil { + if err := validateSliceTypes(vSliceType, cVSliceType, field.data.value, cV.data.value, field.data.qualifiedName); err != nil { return nil, err } - newStruct.RemoveField(field.data.fullyQualifiedName) + newStruct.RemoveField(field.data.qualifiedName) if cVSliceType.Kind() == reflect.Struct { newSliceTypeStruct, err := mergeStructs(left.GetField(name), right.GetField(name), reflect.Slice) - if err != nil { return nil, err } @@ -84,7 +85,9 @@ func mergeStructs(left, right Builder, parentKind reflect.Kind) (any, error) { } if parentKind == reflect.Slice { - sliceOfElementType := reflect.SliceOf(reflect.ValueOf(newStruct.Build().Instance()).Elem().Type()) + sliceOfElementType := reflect.SliceOf( + reflect.ValueOf(newStruct.Build().Instance()).Elem().Type(), + ) return reflect.MakeSlice(sliceOfElementType, 0, 1024).Interface(), nil } return newStruct.Build().Instance(), nil @@ -102,7 +105,12 @@ func validateTypes(v, cV reflect.Value, fullFieldName string) error { newElementType := reflect.TypeOf(v.Interface()) if shouldTypeMatch(v.Kind()) || shouldTypeMatch(cV.Kind()) { if currentElementType != newElementType { - return fmt.Errorf("mismatching types for field '%s': %s and %s", fullFieldName, currentElementType, newElementType) + return fmt.Errorf( + "mismatching types for field '%s': %s and %s", + fullFieldName, + currentElementType, + newElementType, + ) } } else { if v.Kind() != cV.Kind() { @@ -112,13 +120,22 @@ func validateTypes(v, cV reflect.Value, fullFieldName string) error { return nil } -func validateSliceTypes(vSliceType, cVSliceType reflect.Type, v, cV reflect.Value, fullFieldName string) error { +func validateSliceTypes( + vSliceType, cVSliceType reflect.Type, + v, cV reflect.Value, + fullFieldName string, +) error { currentElementType := reflect.TypeOf(reflect.New(cVSliceType).Interface()) newElementType := reflect.TypeOf(reflect.New(vSliceType).Interface()) if shouldTypeMatch(vSliceType.Kind()) || shouldTypeMatch(cVSliceType.Kind()) { if currentElementType != newElementType { - return fmt.Errorf("mismatching types for field '%s': %s and %s", fullFieldName, reflect.TypeOf(v.Interface()), reflect.TypeOf(cV.Interface())) + return fmt.Errorf( + "mismatching types for field '%s': %s and %s", + fullFieldName, + reflect.TypeOf(v.Interface()), + reflect.TypeOf(cV.Interface()), + ) } } else { if v.Kind() != cV.Kind() { diff --git a/modifier.go b/modifier.go index 39facbf..104798e 100644 --- a/modifier.go +++ b/modifier.go @@ -9,9 +9,9 @@ import ( "github.com/MartinSimango/dstruct/dreflect" ) -type FieldNode map[string]*Node[structField] +type FieldNode map[string]*Node[StructField] -type FieldData map[string]structField +type FieldData map[string]StructField type DynamicStructModifier interface { // Instance returns a copy of the struct @@ -43,32 +43,32 @@ type DynamicStructModifier interface { Apply(field string, value any) error } -type FieldModifier func(*structField) +type FieldModifier func(*StructField) type DynamicStructModifierImpl struct { strct any fieldNodeMap FieldNode - fieldData map[string]structField - root *Node[structField] + fieldData map[string]StructField + root *Node[StructField] } var _ DynamicStructModifier = &DynamicStructModifierImpl{} -func newStruct(strct any, rootNode *Node[structField]) *DynamicStructModifierImpl { +func newStruct(strct any, rootNode *Node[StructField]) *DynamicStructModifierImpl { dsm := &DynamicStructModifierImpl{ strct: strct, fieldNodeMap: make(FieldNode), - fieldData: make(map[string]structField), + fieldData: make(map[string]StructField), root: rootNode, } dsm.createFieldToNodeMappings(rootNode) return dsm } -func (dm *DynamicStructModifierImpl) createFieldToNodeMappings(rootNode *Node[structField]) { +func (dm *DynamicStructModifierImpl) createFieldToNodeMappings(rootNode *Node[StructField]) { for _, field := range rootNode.children { - dm.fieldNodeMap[field.data.fullyQualifiedName] = field - dm.fieldData[field.data.fullyQualifiedName] = *field.data + dm.fieldNodeMap[field.data.qualifiedName] = field + dm.fieldData[field.data.qualifiedName] = *field.data dm.createFieldToNodeMappings(field) } } @@ -81,7 +81,7 @@ func (dm *DynamicStructModifierImpl) Instance() any { return dreflect.GetUnderlyingPointerValue(dm.strct) } -func (dm *DynamicStructModifierImpl) get(field string) (n *Node[structField]) { +func (dm *DynamicStructModifierImpl) get(field string) (n *Node[StructField]) { return dm.fieldNodeMap[field] } @@ -115,7 +115,7 @@ func isFieldExported(field string) bool { } func (dm *DynamicStructModifierImpl) Set(field string, value any) error { - var f *Node[structField] + var f *Node[StructField] if f = dm.get(field); f == nil { return fmt.Errorf("field %s does not exists in struct", field) } diff --git a/struct.field.go b/struct.field.go new file mode 100644 index 0000000..6d5cf70 --- /dev/null +++ b/struct.field.go @@ -0,0 +1,71 @@ +package dstruct + +import ( + "fmt" + "reflect" + "strconv" +) + +type StructField struct { + name string + tag reflect.StructTag + value reflect.Value + dstructType reflect.Type + goType reflect.Type + pkgPath string + anonymous bool + jsonName string + qualifiedName string + ptrDepth int + ptrKind reflect.Kind + structIndex *int + numberOfSubFields *int +} + +// GetFieldName returns the name of the field. +func (f StructField) GetFieldName() string { + return f.name +} + +// GetValue returns the value of the field. +func (f StructField) GetValue() any { + return f.value.Interface() +} + +// GetType returns the Dstruct type of the field which can be different from the Go type (reflect.TypeOf(val)) +func (f StructField) GetType() reflect.Type { + return f.dstructType +} + +// GetQualifiedName returns the fully qualified name of the field. +func (f StructField) GetQualifiedName() string { + return f.qualifiedName +} + +// GetTag returns the tag of the field. +func (f StructField) GetTag(t string) string { + return f.tag.Get(t) +} + +// GetJsonName returns the json name of the field. +func (f StructField) GetJsonName() string { + return f.jsonName +} + +// TODO: shoudl this even be here as it is not used ? +func (f StructField) GetEnumValues() (enumValues map[string]int) { + enum, ok := f.tag.Lookup("enum") + if ok { + numEnums, err := strconv.Atoi(enum) + if err != nil { + return + } + enumValues = make(map[string]int) + for i := 1; i <= numEnums; i++ { + if key := f.tag.Get(fmt.Sprintf("enum_%d", i)); key != "" { + enumValues[key] = i + } + } + } + return +} diff --git a/tree.builder.go b/tree.builder.go index 6005268..90f7d14 100644 --- a/tree.builder.go +++ b/tree.builder.go @@ -11,20 +11,20 @@ import ( ) type treeBuilderImpl struct { - root *Node[structField] + root *Node[StructField] setValues bool } var _ Builder = &treeBuilderImpl{} -func createRoot() *Node[structField] { - return &Node[structField]{ - data: &structField{ - name: "#root", - value: reflect.ValueOf(nil), - fullyQualifiedName: "#root", +func createRoot() *Node[StructField] { + return &Node[StructField]{ + data: &StructField{ + name: "#root", + value: reflect.ValueOf(nil), + qualifiedName: "#root", }, - children: make(map[string]*Node[structField]), + children: make(map[string]*Node[StructField]), } } @@ -63,7 +63,7 @@ func ExtendStruct(val any) *treeBuilderImpl { return b } -func newBuilderFromNode(node *Node[structField], resetFQN bool) *treeBuilderImpl { +func newBuilderFromNode(node *Node[StructField], resetFQN bool) *treeBuilderImpl { if resetFQN { resetNodeFieldsFQN(node) } @@ -73,9 +73,9 @@ func newBuilderFromNode(node *Node[structField], resetFQN bool) *treeBuilderImpl } } -func resetNodeFieldsFQN(node *Node[structField]) *Node[structField] { +func resetNodeFieldsFQN(node *Node[StructField]) *Node[StructField] { for _, v := range node.children { - v.data.fullyQualifiedName = getFQN(node.data.name, v.data.name) + v.data.qualifiedName = getQualifiedName(node.data.name, v.data.name) resetNodeFieldsFQN(v) } return node @@ -123,7 +123,7 @@ func (dsb *treeBuilderImpl) GetField(field string) Builder { ), ) } - } else if node.data.typ.Kind() != reflect.Struct { + } else if node.data.dstructType.Kind() != reflect.Struct { panic(fmt.Sprintf("Cannot get field '%s' because it is not a struct or does not fully derefence to a struct value", field)) } // if node.data.ptrKind != reflect.Struct { @@ -131,13 +131,13 @@ func (dsb *treeBuilderImpl) GetField(field string) Builder { // node. / return newBuilderFromNode(node, false) } - return nil + panic(fmt.Sprintf("Field '%s' does not exist", field)) } func (dsb *treeBuilderImpl) NewBuilderFromField(field string) Builder { copyNode := dsb.getNode(field).Copy() copyNode.data.name = "#root" - copyNode.data.fullyQualifiedName = "#root" + copyNode.data.qualifiedName = "#root" return newBuilderFromNode(copyNode, true) } @@ -146,7 +146,7 @@ func (dsb *treeBuilderImpl) GetFieldCopy(field string) Builder { return newBuilderFromNode(copyNode, false) } -func (dsb *treeBuilderImpl) getNode(field string) *Node[structField] { +func (dsb *treeBuilderImpl) getNode(field string) *Node[StructField] { fields := strings.Split(field, ".") node := dsb.root @@ -163,13 +163,13 @@ func (db *treeBuilderImpl) Build() DynamicStructModifier { // Ensure that the current node is treated is root when struct is built if db.root.parent != nil { rootCopy.data.name = "#root" - rootCopy.data.fullyQualifiedName = "#root" + rootCopy.data.qualifiedName = "#root" rootCopy = resetNodeFieldsFQN(rootCopy) } return newStruct(db.buildStruct(rootCopy), rootCopy) } -func (db *treeBuilderImpl) buildStruct(tree *Node[structField]) any { +func (db *treeBuilderImpl) buildStruct(tree *Node[StructField]) any { structValue := reflect.ValueOf(dreflect.GetPointerOfValueType(treeToStruct(tree))) tree.data.value = structValue // set the value of the struct fields. Currently the tree structure contains the values of the fields @@ -191,7 +191,7 @@ func (dsb *treeBuilderImpl) addFieldToTree( pkgPath string, anonymous bool, tag reflect.StructTag, - root *Node[structField], + root *Node[StructField], ) reflect.Type { value := reflect.ValueOf(typ) if !value.IsValid() { @@ -203,36 +203,36 @@ func (dsb *treeBuilderImpl) addFieldToTree( *root.data.numberOfSubFields++ } goType := reflect.TypeOf(value.Interface()) - field := &structField{ - name: name, - value: value, // this will initally be unaddressable until the struct is built - tag: tag, - typ: goType, - goType: goType, - pkgPath: pkgPath, - anonymous: anonymous, - jsonName: strings.Split(tag.Get("json"), ",")[0], + field := &StructField{ + name: name, + value: value, // this will initally be unaddressable until the struct is built + tag: tag, + dstructType: goType, + goType: goType, + pkgPath: pkgPath, + anonymous: anonymous, + jsonName: strings.Split(tag.Get("json"), ",")[0], } field.structIndex = new(int) *field.structIndex = *root.data.numberOfSubFields - field.fullyQualifiedName = getFQN(root.data.GetFieldFullyQualifiedName(), field.name) + field.qualifiedName = getQualifiedName(root.data.GetQualifiedName(), field.name) root.AddNode(name, field) // don't add struct fields if special kind switch value.Kind() { case reflect.Struct: - field.typ = dsb.addStructFields(value, root.children[name], 0) + field.dstructType = dsb.addStructFields(value, root.children[name], 0) case reflect.Ptr: - field.typ = dsb.addPtrField(value, root.children[name]) + field.dstructType = dsb.addPtrField(value, root.children[name]) } - return field.typ + return field.dstructType } -func sortKeys(root *Node[structField]) (keys []string) { +func sortKeys(root *Node[StructField]) (keys []string) { for key := range root.children { keys = append(keys, key) } @@ -245,11 +245,11 @@ func sortKeys(root *Node[structField]) (keys []string) { // this only allocates memory for the struct and its fields and does not set any values // so the returned value will be an uninitialized struct -func treeToStruct(root *Node[structField]) any { +func treeToStruct(root *Node[StructField]) any { var structFields []reflect.StructField // sort the keys to ensure type of struct produced is always the same - var keys []string = sortKeys(root) + keys := sortKeys(root) for _, fieldName := range keys { var typ reflect.Type @@ -279,7 +279,7 @@ func treeToStruct(root *Node[structField]) any { return strct.Interface() } -func setStructFieldValues(strct reflect.Value, root *Node[structField]) { +func setStructFieldValues(strct reflect.Value, root *Node[StructField]) { for i := 0; i < strct.NumField(); i++ { field := strct.Field(i) fieldName := strct.Type().Field(i).Name @@ -307,7 +307,7 @@ func setStructFieldValues(strct reflect.Value, root *Node[structField]) { } } -func setPointerFieldValue(field reflect.Value, currentNode *Node[structField]) { +func setPointerFieldValue(field reflect.Value, currentNode *Node[StructField]) { if currentNode.data.value.IsNil() { return } @@ -334,7 +334,7 @@ func setPointerFieldValue(field reflect.Value, currentNode *Node[structField]) { func (dsb *treeBuilderImpl) addStructFields( strct reflect.Value, - root *Node[structField], + root *Node[StructField], ptrDepth int, ) reflect.Type { var structFields []reflect.StructField @@ -390,7 +390,7 @@ func getPtrValue(value reflect.Value, ptrDepth int) (reflect.Value, int) { func (dsb *treeBuilderImpl) addPtrField( value reflect.Value, - node *Node[structField], + node *Node[StructField], ) reflect.Type { if value.IsNil() { return reflect.TypeOf(value.Interface()) @@ -408,7 +408,7 @@ func (dsb *treeBuilderImpl) addPtrField( return reflect.TypeOf(value.Interface()) } -func getFQN(root, name string) string { +func getQualifiedName(root, name string) string { if root != "#root" && root != "" { return fmt.Sprintf("%s.%s", root, name) } From a557af889c1071747f08320bcaef56bf906be24c Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sun, 27 Oct 2024 16:35:44 +0200 Subject: [PATCH 22/31] fix bugs when generating pointers and custom types --- dstruct.generated.struct.go | 56 ++++-- examples/task/main.go | 173 ++++++++++++++++++ .../core/default.generation.functions.go | 12 +- generator/core/function.holder.go | 1 + generator/core/generated.field.go | 22 ++- generator/core/number.function.holder.go | 4 +- generator/core/number.go | 2 - generator/core/pointer.function.holder.go | 43 +++++ generator/core/pointer.go | 28 ++- generator/core/slice.function.holder.go | 3 + generator/core/slice.go | 2 +- generator/task.go | 31 ++-- generator/task.properties.go | 7 + modifier.go | 33 +++- struct.field.go | 19 +- tree.builder.go | 60 ++++-- 16 files changed, 423 insertions(+), 73 deletions(-) create mode 100644 generator/core/pointer.function.holder.go create mode 100644 generator/task.properties.go diff --git a/dstruct.generated.struct.go b/dstruct.generated.struct.go index 1425e1c..e956679 100644 --- a/dstruct.generated.struct.go +++ b/dstruct.generated.struct.go @@ -25,7 +25,7 @@ type DStructGeneratedStruct[T any] struct { fieldContexts GeneratedFieldContexts structConfig core.GeneratedFieldConfig instance T - customTypes map[reflect.Type]core.FunctionHolder + customTypes map[string]core.FunctionHolder } var _ GeneratedStruct = &DStructGeneratedStruct[int]{} @@ -50,7 +50,7 @@ func NewGeneratedStructWithConfig[T any](val T, cfg, settings, ), - customTypes: make(map[reflect.Type]core.FunctionHolder), + customTypes: make(map[string]core.FunctionHolder), } for _, v := range customTypes { @@ -74,6 +74,11 @@ func (gs *DStructGeneratedStruct[T]) NewT() *T { // Generate implements GeneratedStruct.Generate. func (gs *DStructGeneratedStruct[T]) Generate() { + // TODO: should only be called if new struct fields are generated + // an idea is to have generatedFields return a bool to indicate if new fields were generated + // also make this method private + + // gs.populateGeneratedFields(gs.root) // in case new fields are added / gs.generateFields() switch any(*new(T)).(type) { @@ -83,10 +88,14 @@ func (gs *DStructGeneratedStruct[T]) Generate() { } gs.instance = toType[T](gs.DynamicStructModifierImpl) +} - // TODO: should only be called if new struct fields are generated - // an idea is to have generatedFields return a bool to indicate if new fields were generated - gs.Update() +func (gs *DStructGeneratedStruct[T]) Set(field string, value any) error { + if err := gs.DynamicStructModifierImpl.Set(field, value); err != nil { + return err + } + gs.populateGeneratedFields(gs.fieldNodeMap[field].parent) + return nil } // SetFieldGenerationSettings implements GeneratedStruct.SetFieldGenerationSettings @@ -148,7 +157,7 @@ func (gs *DStructGeneratedStruct[T]) SetFieldGenerationConfig( return err } - if gs.fieldContexts[field] == nil { + if gs.fieldContexts[field] == nil { // if field has children gs.propagateConfig(gs.fieldNodeMap[field], cfg) } else { // this will be fields that are either custom types or fields that have no children @@ -311,11 +320,17 @@ func toPointerType[T any](gs DynamicStructModifier) *T { } func (gs *DStructGeneratedStruct[T]) generateFields() { + // wg := sync.WaitGroup{} for k, genFunc := range gs.fieldContexts { + // wg.Add(1) + // go func(k string, genFunc *core.GeneratedFieldContext) { if err := gs.Set(k, genFunc.Generate()); err != nil { fmt.Println(err) } + // wg.Done() + // }(k, genFunc) } + // wg.Wait() } func (gs *DStructGeneratedStruct[T]) addCustomTypes() { @@ -329,7 +344,7 @@ func (gs *DStructGeneratedStruct[T]) addCustomTypes() { func (gs *DStructGeneratedStruct[T]) addCustomType(customType CustomType) { // TODO: restrict some types from being added such as nil, ints etc - gs.customTypes[reflect.TypeOf(customType.Value)] = customType.FunctionHolder + gs.customTypes[reflect.TypeOf(customType.Value).String()] = customType.FunctionHolder // the function holder kind is the find that the function retrusn which could either be an existing kind or a new kind i.ie time.Time would be a new kind // TODO:idea: GenerationsFunction key should be type of CustomKind and not reflect.Kind gs.structConfig.GenerationFunctions[customType.FunctionHolder.Kind()] = customType.FunctionHolder @@ -350,6 +365,27 @@ func (gs *DStructGeneratedStruct[T]) createGeneratedField( func (gs *DStructGeneratedStruct[T]) populateGeneratedFields(node *Node[StructField]) { for _, field := range node.children { + if gs.fieldContexts[field.data.qualifiedName] != nil { + continue + } + // fmt.Println( + // "Field: ", + // field.data.qualifiedName, + // field.data.value.Kind(), + // field.data.ptrDepth, + // field.data.ptrKind, + // field.data.IsFieldDereferencable(), + // ) + + // fmt.Println( + // "POP: ", + // field.data.qualifiedName, + // gs.customTypes, + // field.data.goType, + // field.data.dstructType, + + // ) + if customType := gs.customTypes[field.data.goType]; customType != nil { gs.fieldContexts[field.data.qualifiedName] = core.NewGeneratedFieldContext( gs.createGeneratedField(field, customType.Kind()), @@ -370,8 +406,7 @@ func (gs *DStructGeneratedStruct[T]) propagateConfig( ) { for _, field := range node.children { // Don't propagate changes to children nodes if the field is a custom type - if field.HasChildren() && - !gs.fieldContexts[field.data.qualifiedName].GeneratedField.IsCustomType() { + if field.HasChildren() && gs.customTypes[field.data.goType] == nil { gs.propagateConfig(field, cfg) } else { gs.fieldContexts[field.data.qualifiedName].GeneratedField.SetConfig(cfg) @@ -384,8 +419,7 @@ func (gs *DStructGeneratedStruct[T]) propagateSettings( settings config.GenerationSettings, ) { for _, field := range node.children { - if field.HasChildren() && - !gs.fieldContexts[field.data.qualifiedName].GeneratedField.IsCustomType() { + if field.HasChildren() && gs.customTypes[field.data.goType] == nil { gs.propagateSettings(field, settings) } else { gs.fieldContexts[field.data.qualifiedName].GeneratedField.SetGenerationSettings(settings) diff --git a/examples/task/main.go b/examples/task/main.go index fa0842c..31a7c25 100644 --- a/examples/task/main.go +++ b/examples/task/main.go @@ -2,8 +2,181 @@ package main import ( "fmt" + "strconv" + "time" + + "github.com/MartinSimango/dstruct" + "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" + "github.com/MartinSimango/dstruct/generator/core" +) + +const ( + GenInt32 generator.TaskName = "GenInt32" ) +type genInt32Params struct { + min int32 + max int32 +} + +type GenInt32Task struct{} + +// Ensure GenInt32Task implements Task. +var _ generator.Task = &GenInt32Task{} + +// Tags implements Task. +func (g *GenInt32Task) Name() string { + return string(GenInt32) +} + +// GenerationFunction implements Task. +func (g *GenInt32Task) GenerationFunction( + taskProperties generator.TaskProperties, +) generator.GenerationFunction { + generator.ValidateParamCount(g, taskProperties) + params := g.getInt32Params(taskProperties.FieldName, taskProperties.Parameters) + numberConfig := config.NewNumberRangeConfig() + numberConfig.Int().SetRange(int(params.min), int(params.max)) + return core.GenerateNumberFunc[int](numberConfig) +} + +func (g *GenInt32Task) ExpectedParameterCount() int { + return 2 +} + +func (g *GenInt32Task) FunctionHolder(taskProperties generator.TaskProperties) core.FunctionHolder { + return core.NewFunctionHolderNoArgs(g.GenerationFunction(taskProperties)) +} + +func (g *GenInt32Task) getInt32Params(fieldName string, params []string) genInt32Params { + param_1, err := strconv.Atoi(params[0]) + if err != nil { + panic(fmt.Sprintf("error with field %s: task %s error: %s", fieldName, GenInt32, err)) + } + + param_2, err := strconv.Atoi(params[1]) + if err != nil { + panic(fmt.Sprintf("error with field %s: task %s error: %s", fieldName, GenInt32, err)) + } + + if param_1 > param_2 { + err = fmt.Errorf( + "min must be less or equal to the max value min = %d max = %d", + param_1, + param_2, + ) + panic(fmt.Sprintf("error with field %s: task %s error: %s", fieldName, GenInt32, err)) + + } + + return genInt32Params{ + min: int32(param_1), + max: int32(param_2), + } +} + +type M struct { + Name string +} + +type Person struct { + // Love ABC + Age *int + Time time.Time + Other *M + + // Parray []Person + + // Person *Person +} + +type P struct { + P Person + Value int +} + +type Test struct { + // A *int + // S string + // C int + Person P `json:"person"` + + // Cpoint *int + T time.Time + + // Pa []Pa + // Parray []Person + + // L +} + func main() { + e := dstruct.ExtendStruct(Test{}).Build().Instance() + createTime := time.Now() + // for i := 0; i < 1; i++ { + // estruct.AddField(fmt.Sprintf("Test_%d", i), Test{}, "") + // } + fmt.Println(time.Since(createTime)) + // b := estruct.Build() + // + st := config.GenerationSettings{SetNonRequiredFields: true} + gestruct := dstruct.NewGeneratedStructWithConfig(e, + config.NewDstructConfig(), + st, + ) + + c := gestruct.GetGenerationConfig() + c.SetIntRange(20, 50) + st.SetNonRequiredFields = false + + // gestruct.SetFieldGenerationSettings("Person", st) + // gestruct.SetFieldGenerationConfig("Person", c) + + gTime := time.Now() + gestruct.Generate() + // gestruct.Update() + // gestruct.Set("Person.P", Person{Age: new(int), Time: time.Now()}) + err := gestruct.Set( + "Person.P", + Person{Age: new(int), Time: time.Now(), Other: &M{Name: "Martin"}}, + ) + if err != nil { + // panic(err) + } + + fmt.Println("Time to generate: ", time.Since(gTime)) + fmt.Printf("%+v\n", gestruct.Get_("Person.P.Other.Name")) + // for field := range gestruct.GetFields() { + // fmt.Println("Field: ", field) + // fmt.Printf( + // "'%s': dstruct: %+v goType: %+v\n", + // field, + // value.GetDstructType(), + // value.GetGoType(), + // ) + // } + + // generatedStuct := dstruct.NewGeneratedStructWithConfig( + // Test{Cpoint: new(int)}, + // config.NewDstructConfig().SetSliceLength(3, 3), + // config.DefaultGenerationSettings(), + // ) + // gt := &GenInt32Task{} + // generator.AddTask(gt) + // if err := generatedStuct.Set("Person.P", &Person{}); err != nil { + // panic(err) + // } + // generatedStuct.SetFieldGenerationConfig( + // "Person.Value", + // config.NewDstructConfig().SetIntRange(800, 1000), + // ) + // + // // generatedStuct.SetFieldFromTask("C", gt, 300, 400) + // // generatedStuct.Update() + // generatedStuct.Generate() + // + // fmt.Printf("%+v\n", generatedStuct.Get_("Person.P")) + // fmt.Println("Testing task") } diff --git a/generator/core/default.generation.functions.go b/generator/core/default.generation.functions.go index d129644..d835cc6 100644 --- a/generator/core/default.generation.functions.go +++ b/generator/core/default.generation.functions.go @@ -1,7 +1,6 @@ package core import ( - "fmt" "reflect" "github.com/MartinSimango/dstruct/generator/config" @@ -13,12 +12,13 @@ func (d DefaultGenerationFunctions) Copy( cfg config.Config, kind reflect.Kind, ) (dgf DefaultGenerationFunctions) { - // look at the kind and only return what needs to be copied dgf = make(DefaultGenerationFunctions) - if d[kind] == nil { - // Copy ll the functions if the kind is not found - // TODO: check if this can ever happen - fmt.Println("Kind not found", kind) + // if kind can have subfields, copy the generation functions + // For example if kind is ptr it could be pointing to a struct that has fields of different types + // and those different types will have different generation functions + if kind == reflect.Pointer || kind == reflect.Struct || kind == reflect.Slice || + kind == reflect.Array { + for k, v := range d { dgf[k] = v.Copy(cfg) } diff --git a/generator/core/function.holder.go b/generator/core/function.holder.go index c458b7b..becbb3b 100644 --- a/generator/core/function.holder.go +++ b/generator/core/function.holder.go @@ -53,6 +53,7 @@ func (c *BaseFunctionHolder) Copy(cfg config.Config) (bf BaseFunctionHolder) { return } +// TODO: this is not used reoved it? func (c *BaseFunctionHolder) Kind() reflect.Kind { return c.generationFunction.Kind() } diff --git a/generator/core/generated.field.go b/generator/core/generated.field.go index b41cdd2..016a22e 100644 --- a/generator/core/generated.field.go +++ b/generator/core/generated.field.go @@ -49,8 +49,8 @@ type GeneratedField struct { Config GeneratedFieldConfig Parent *GeneratedField PointerValue *reflect.Value - customTypes map[reflect.Type]FunctionHolder - goType reflect.Type + customTypes map[string]FunctionHolder + goType string currentGenerationFunction generator.GenerationFunction } @@ -58,8 +58,8 @@ func NewGeneratedField(fqn string, value reflect.Value, tag reflect.StructTag, config GeneratedFieldConfig, - customTypes map[reflect.Type]FunctionHolder, - goType reflect.Type, + customTypes map[string]FunctionHolder, + goType string, ) *GeneratedField { generateField := &GeneratedField{ Name: fqn, @@ -78,6 +78,12 @@ func NewGeneratedField(fqn string, generateField.Config.GenerationFunctions, ) } + if value.Kind() == reflect.Ptr { + config.GenerationFunctions[reflect.Ptr] = NewPointerFunctionHolder( + GeneratePointerValueFunc, + generateField, + ) + } // if field.IsCustomType() { // config.GenerationFunctions[field.customTypeFunctionHolder().GetFunction().Kind()] = field.customTypeFunctionHolder() @@ -173,7 +179,7 @@ func (field *GeneratedField) SetValue() bool { } GenerateStructFunc(field).Generate() case reflect.Pointer: - GeneratePointerValueFunc(field).Generate() + field.Value.Set(reflect.ValueOf(GeneratePointerValueFunc(field).Generate())) case reflect.Slice: // Don't allow recursion within slices if field.checkForRecursiveDefinition(true) { @@ -199,7 +205,7 @@ func (field *GeneratedField) setStructValues() { Config: field.Config.Copy(field.Value.Field(j).Kind()), Parent: field, customTypes: field.customTypes, - goType: field.Value.Field(j).Type(), + goType: field.Value.Field(j).Type().Name(), } structField.SetValue() } @@ -293,9 +299,5 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio // if we get no match, we default to the default generation function for the kind // kidds of type Slice will be handled here as their default generation function for a slice will be overwritten when the generated field is created. - fmt.Printf( - "Returning function ref: %p\n", - field.Config.GenerationFunctions[kind].GetConfig().Number(), - ) return field.Config.GenerationFunctions[kind].GetFunction() } diff --git a/generator/core/number.function.holder.go b/generator/core/number.function.holder.go index e5f0a69..94dfafa 100644 --- a/generator/core/number.function.holder.go +++ b/generator/core/number.function.holder.go @@ -1,8 +1,6 @@ package core import ( - "fmt" - "github.com/MartinSimango/dstruct/generator" "github.com/MartinSimango/dstruct/generator/config" ) @@ -24,7 +22,7 @@ func NewNumberFunctionHolder( config: config.NewDstructConfigBuilder().WithNumberConfig(cfg).Build(), fun: f, resetFunction: func(cfg config.Config) generator.GenerationFunction { - fmt.Println("Resetting number function holder") + // fmt.Println("Resetting number function holder") return f(cfg.Number()) }, generationFunction: f(cfg), diff --git a/generator/core/number.go b/generator/core/number.go index 673594d..9854a18 100644 --- a/generator/core/number.go +++ b/generator/core/number.go @@ -21,8 +21,6 @@ func GenerateNumberFunc[n config.Number]( return &coreGenerationFunction{ _func: func(parameters ...any) any { - fmt.Println("Refernece to min and max: ", min, max) - fmt.Printf("Cfg: %p\n", cfg) return generateNum(*min, *max) }, kind: reflect.ValueOf(*new(n)).Kind(), diff --git a/generator/core/pointer.function.holder.go b/generator/core/pointer.function.holder.go new file mode 100644 index 0000000..1db9368 --- /dev/null +++ b/generator/core/pointer.function.holder.go @@ -0,0 +1,43 @@ +package core + +import ( + "github.com/MartinSimango/dstruct/generator" + "github.com/MartinSimango/dstruct/generator/config" +) + +type PointerFunctionHolderFunc func(*GeneratedField) generator.GenerationFunction + +type PointerFunctionHolder struct { + BaseFunctionHolder + field *GeneratedField +} + +var _ FunctionHolder = &PointerFunctionHolder{} + +func NewPointerFunctionHolder( + f PointerFunctionHolderFunc, + field *GeneratedField, +) FunctionHolder { + return &PointerFunctionHolder{ + BaseFunctionHolder: BaseFunctionHolder{ + config: nil, + fun: f, + resetFunction: nil, + }, + field: field, + } +} + +func (c *PointerFunctionHolder) GetFunction() generator.GenerationFunction { + if c.generationFunction != nil { + return c.generationFunction + } + c.generationFunction = c.fun.(PointerFunctionHolderFunc)(c.field) + return c.generationFunction +} + +func (c *PointerFunctionHolder) Copy(cfg config.Config) FunctionHolder { + return &SliceFunctionHolder{ + BaseFunctionHolder: c.BaseFunctionHolder.Copy(cfg), + } +} diff --git a/generator/core/pointer.go b/generator/core/pointer.go index c0027a4..0e0b322 100644 --- a/generator/core/pointer.go +++ b/generator/core/pointer.go @@ -13,18 +13,32 @@ func GeneratePointerValueFunc(field *GeneratedField) generator.GenerationFunctio if !field.Config.GenerationSettings.SetNonRequiredFields { return nil } - - field.Value.Set(reflect.New(field.Value.Type().Elem())) + // t := field.Value.Interface() + // field.Value.Set(reflect.New(field.Value.Type().Elem())) + fieldValueCopy := reflect.New(field.Value.Type()).Elem() + fieldValueCopy.Set(reflect.New(field.Value.Type().Elem())) fieldPointerValue := *field - fieldPointerValue.Value = field.Value.Elem() - fieldPointerValue.PointerValue = &field.Value + fieldPointerValue.Value = fieldValueCopy.Elem() + fieldPointerValue.PointerValue = &fieldValueCopy fieldPointerValue.SetValue() - if field.Value.Elem().CanSet() { - field.Value.Elem().Set(fieldPointerValue.Value) + if fieldValueCopy.Elem().CanSet() { + fieldValueCopy.Elem().Set(fieldPointerValue.Value) } + return fieldValueCopy.Interface() + + // field.Value.Set(reflect.New(field.Value.Type().Elem())) + // fieldPointerValue := *field + // fieldPointerValue.Value = field.Value.Elem() + // fieldPointerValue.PointerValue = &field.Value + // fieldPointerValue.SetValue() - return field.Value.Interface() + // if field.Value.Elem().CanSet() { + // field.Value.Elem().Set(fieldPointerValue.Value) + // } + // + // + // return field.Value.Interface() }, kind: reflect.Ptr, args: []any{field}, diff --git a/generator/core/slice.function.holder.go b/generator/core/slice.function.holder.go index 3b27059..17bf581 100644 --- a/generator/core/slice.function.holder.go +++ b/generator/core/slice.function.holder.go @@ -25,6 +25,9 @@ func NewSliceFunctionHolder( BaseFunctionHolder: BaseFunctionHolder{ config: cfg, fun: f, + resetFunction: func(cfg config.Config) generator.GenerationFunction { + return f(field, cfg, generationFunctions) + }, }, field: field, generationFunctions: generationFunctions, diff --git a/generator/core/slice.go b/generator/core/slice.go index a96d943..3222f3b 100644 --- a/generator/core/slice.go +++ b/generator/core/slice.go @@ -40,7 +40,7 @@ func GenerateSliceFunc( Config: fieldConfig, Parent: field, customTypes: field.customTypes, - goType: elemValue.Type(), + goType: elemValue.Type().Name(), } newField.SetValue() diff --git a/generator/task.go b/generator/task.go index 3bfd392..d11b18f 100644 --- a/generator/task.go +++ b/generator/task.go @@ -21,12 +21,6 @@ func init() { tasks = make(map[string]Task) } -type TaskProperties struct { - TaskName string - Parameters []string - FieldName string -} - func GetTask(task string) Task { return tasks[task] } @@ -51,11 +45,19 @@ func CreateTaskProperties(fieldName string, tags reflect.StructTag) (*TaskProper gen_task_tag := strings.TrimSpace(tags.Get("gen_task")) leftBraceIndex := strings.Index(gen_task_tag, "(") if leftBraceIndex == -1 { - return nil, fmt.Errorf("error with field %s: task %s error: no ( found", fieldName, gen_task_tag) + return nil, fmt.Errorf( + "error with field %s: task %s error: no ( found", + fieldName, + gen_task_tag, + ) } if gen_task_tag[len(gen_task_tag)-1:] != ")" { - return nil, fmt.Errorf("error with field %s: task %s error: last character of task must be )", fieldName, gen_task_tag) + return nil, fmt.Errorf( + "error with field %s: task %s error: last character of task must be )", + fieldName, + gen_task_tag, + ) } taskName := gen_task_tag[:leftBraceIndex] parameterCount, err := strconv.Atoi(gen_task_tag[leftBraceIndex+1 : len(gen_task_tag)-1]) @@ -71,7 +73,6 @@ func CreateTaskProperties(fieldName string, tags reflect.StructTag) (*TaskProper } func GetTagForTask(name TaskName, params ...any) reflect.StructTag { - if tasks[string(name)] == nil { panic(fmt.Sprintf("Task '%s' is not registered", name)) } @@ -82,12 +83,18 @@ func GetTagForTask(name TaskName, params ...any) reflect.StructTag { } return reflect.StructTag(tags) - } func ValidateParamCount(task Task, taskProperties TaskProperties) { if len(taskProperties.Parameters) != task.ExpectedParameterCount() { - panic(fmt.Sprintf("error with field %s. task '%s': task requires %d parameters but has %d", taskProperties.FieldName, task.Name(), task.ExpectedParameterCount(), len(taskProperties.Parameters))) + panic( + fmt.Sprintf( + "error with field %s. task '%s': task requires %d parameters but has %d", + taskProperties.FieldName, + task.Name(), + task.ExpectedParameterCount(), + len(taskProperties.Parameters), + ), + ) } - } diff --git a/generator/task.properties.go b/generator/task.properties.go new file mode 100644 index 0000000..260bdd2 --- /dev/null +++ b/generator/task.properties.go @@ -0,0 +1,7 @@ +package generator + +type TaskProperties struct { + TaskName string + Parameters []string + FieldName string +} diff --git a/modifier.go b/modifier.go index 104798e..65ceaee 100644 --- a/modifier.go +++ b/modifier.go @@ -32,7 +32,7 @@ type DynamicStructModifier interface { Set(field string, value any) error // GetFields returns a map containing all fields within a struct - GetFields() FieldData + GetFields() map[string]StructField // Update updates the struct's underlying tree to represent that of the strct's value. // The structs underlying tree can change if new fields are added due to fields within the struct changing from @@ -123,9 +123,8 @@ func (dm *DynamicStructModifierImpl) Set(field string, value any) error { if !isFieldExported(field) { return fmt.Errorf("field %s is not exported", field) } - fieldValue := f.data.value - if !canExtend(value) { + if !canExtend(value) { // if value is not a struct if value == nil { if !fieldValue.IsZero() { fieldValue.Set(reflect.Zero(fieldValue.Type())) @@ -133,17 +132,39 @@ func (dm *DynamicStructModifierImpl) Set(field string, value any) error { return nil } fieldValue.Set(dreflect.Convert(reflect.ValueOf(value), fieldValue.Type())) + return nil } + // createTree := fieldValue.Kind() == reflect.Pointer && value != nil + + // fmt.Println( + // "fieldValue: ", + // f.data.qualifiedName, + // fieldValue.Kind(), + // fieldValue.IsZero(), + // fieldValue.Interface(), + // createTree, + // value, + // ) fieldValue.Set(dreflect.Convert(reflect.ValueOf(value), fieldValue.Type())) - - // TODO: should we update the struct here? and then remove the Update and Apply methods + dm.update(f) return nil } -func (dm *DynamicStructModifierImpl) GetFields() FieldData { +// update updates the struct's underlying node to represent that of the strct's value. +func (dm *DynamicStructModifierImpl) update(node *Node[StructField]) { + fmt.Println("updating node", node.data.qualifiedName, node.data.value) + nodeValue := node.data.value + if canExtend(nodeValue.Interface()) { + node.children = ExtendStruct(nodeValue.Interface()).root.children + setValues(nodeValue, resetNodeFieldsFQN(node)) + dm.createFieldToNodeMappings(node) + } +} + +func (dm *DynamicStructModifierImpl) GetFields() map[string]StructField { return dm.fieldData } diff --git a/struct.field.go b/struct.field.go index 6d5cf70..59e9d71 100644 --- a/struct.field.go +++ b/struct.field.go @@ -11,7 +11,7 @@ type StructField struct { tag reflect.StructTag value reflect.Value dstructType reflect.Type - goType reflect.Type + goType string pkgPath string anonymous bool jsonName string @@ -22,6 +22,14 @@ type StructField struct { numberOfSubFields *int } +func (f StructField) IsFieldDereferencable() bool { + if f.value.Kind() == reflect.Pointer { + return f.ptrKind != reflect.Invalid + } + + return true +} + // GetFieldName returns the name of the field. func (f StructField) GetFieldName() string { return f.name @@ -32,11 +40,16 @@ func (f StructField) GetValue() any { return f.value.Interface() } -// GetType returns the Dstruct type of the field which can be different from the Go type (reflect.TypeOf(val)) -func (f StructField) GetType() reflect.Type { +// GetDstructType returns the Dstruct type of the field which can be different from the Go type (reflect.TypeOf(val)) +func (f StructField) GetDstructType() reflect.Type { return f.dstructType } +// GetGoType returns the Go type of the field. +func (f StructField) GetGoType() string { + return f.goType +} + // GetQualifiedName returns the fully qualified name of the field. func (f StructField) GetQualifiedName() string { return f.qualifiedName diff --git a/tree.builder.go b/tree.builder.go index 90f7d14..a732f61 100644 --- a/tree.builder.go +++ b/tree.builder.go @@ -55,9 +55,9 @@ func ExtendStruct(val any) *treeBuilderImpl { switch value.Kind() { case reflect.Struct: - b.addStructFields(value, b.root, 0) + b.root.data.dstructType = b.addStructFields(value, b.root, 0) case reflect.Ptr: - b.addPtrField(value, b.root) + b.root.data.dstructType = b.addPtrField(value, b.root) } return b @@ -75,7 +75,7 @@ func newBuilderFromNode(node *Node[StructField], resetFQN bool) *treeBuilderImpl func resetNodeFieldsFQN(node *Node[StructField]) *Node[StructField] { for _, v := range node.children { - v.data.qualifiedName = getQualifiedName(node.data.name, v.data.name) + v.data.qualifiedName = getQualifiedName(node.data.qualifiedName, v.data.name) resetNodeFieldsFQN(v) } return node @@ -85,7 +85,14 @@ func (dsb *treeBuilderImpl) AddField(name string, value interface{}, tag string) if dsb.root.HasChild(name) { panic(fmt.Sprintf("Field '%s' already exists", name)) } - dsb.addFieldToTree(name, value, "", false, reflect.StructTag(tag), dsb.root) + dsb.root.data.dstructType = dsb.addFieldToTree( + name, + value, + "", + false, + reflect.StructTag(tag), + dsb.root, + ) return dsb } @@ -160,7 +167,7 @@ func (dsb *treeBuilderImpl) getNode(field string) *Node[StructField] { func (db *treeBuilderImpl) Build() DynamicStructModifier { rootCopy := db.root.Copy() - // Ensure that the current node is treated is root when struct is built + // Ensure that the current node is treated as root when struct is built if db.root.parent != nil { rootCopy.data.name = "#root" rootCopy.data.qualifiedName = "#root" @@ -175,16 +182,20 @@ func (db *treeBuilderImpl) buildStruct(tree *Node[StructField]) any { // set the value of the struct fields. Currently the tree structure contains the values of the fields // so we need to copy the values to the struct fields if db.setValues { - if structValue.Elem().Kind() == reflect.Ptr { - setPointerFieldValue(structValue.Elem(), tree) - } else { - setStructFieldValues(structValue.Elem(), tree) - } + setValues(structValue.Elem(), tree) } return structValue.Interface() } +func setValues(value reflect.Value, tree *Node[StructField]) { + if value.Kind() == reflect.Ptr { + setPointerFieldValue(value, tree) + } else { + setStructFieldValues(value, tree) + } +} + func (dsb *treeBuilderImpl) addFieldToTree( name string, typ interface{}, @@ -202,12 +213,17 @@ func (dsb *treeBuilderImpl) addFieldToTree( } else { *root.data.numberOfSubFields++ } - goType := reflect.TypeOf(value.Interface()) + + var goType string + if goType = tag.Get("goType"); goType == "" { + goType = value.Type().Name() + } + field := &StructField{ name: name, value: value, // this will initally be unaddressable until the struct is built tag: tag, - dstructType: goType, + dstructType: value.Type(), goType: goType, pkgPath: pkgPath, anonymous: anonymous, @@ -298,6 +314,7 @@ func setStructFieldValues(strct reflect.Value, root *Node[StructField]) { } // make the tree node value point to the struct field - ensuring that when the node value changes the struct field value changes + // also update the node value to actually have a memory address currentNode.data.value = field // if currentNode.data.anonymous { @@ -347,6 +364,15 @@ func (dsb *treeBuilderImpl) addStructFields( for i := 0; i < strct.NumField(); i++ { fieldName := strct.Type().Field(i).Name fieldTag := strct.Type().Field(i).Tag + + if fieldTag.Get("goType") == "" { + fieldTag = (reflect.StructTag)(strings.TrimSpace(fmt.Sprintf( + "%s goType:\"%s\"", + fieldTag, + strct.Field(i).Type(), + ))) + } + var fieldValue any if strct.Type().Field(i).IsExported() { fieldValue = strct.Field(i).Interface() @@ -358,6 +384,16 @@ func (dsb *treeBuilderImpl) addStructFields( pkgPath := strct.Type().Field(i).PkgPath anonymous := strct.Type().Field(i).Anonymous fieldType := dsb.addFieldToTree(fieldName, fieldValue, pkgPath, anonymous, fieldTag, root) + // fmt.Printf( + // "\nfieldType: '%s' %+v %+v %+v PO: %+v S: %+v \n", + // fieldName, + // fieldType, + // reflect.TypeOf(fieldValue), + // strct.Field(i).Type(), + // reflect.TypeOf(reflect.ValueOf(fieldValue).Interface()), + // reflect.TypeOf(strct.Interface()), + // ) + // // // fmt.Println() if anonymous { pkgPath = "" From 7a949a0adcc5755948a0b0d27856edc46761ef2a Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sun, 27 Oct 2024 17:26:31 +0200 Subject: [PATCH 23/31] OA --- examples/task/main.go | 114 +++++++++++++++++++++--------------------- modifier.go | 1 - 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/examples/task/main.go b/examples/task/main.go index 31a7c25..2125655 100644 --- a/examples/task/main.go +++ b/examples/task/main.go @@ -99,11 +99,11 @@ type P struct { type Test struct { // A *int // S string - // C int + C int Person P `json:"person"` - // Cpoint *int - T time.Time + Cpoint *int + T time.Time // Pa []Pa // Parray []Person @@ -112,41 +112,41 @@ type Test struct { } func main() { - e := dstruct.ExtendStruct(Test{}).Build().Instance() - createTime := time.Now() - // for i := 0; i < 1; i++ { - // estruct.AddField(fmt.Sprintf("Test_%d", i), Test{}, "") + // e := dstruct.ExtendStruct(Test{}).Build().Instance() + // createTime := time.Now() + // // for i := 0; i < 1; i++ { + // // estruct.AddField(fmt.Sprintf("Test_%d", i), Test{}, "") + // // } + // fmt.Println(time.Since(createTime)) + // // b := estruct.Build() + // // + // st := config.GenerationSettings{SetNonRequiredFields: true} + // gestruct := dstruct.NewGeneratedStructWithConfig(e, + // config.NewDstructConfig(), + // st, + // ) + // + // c := gestruct.GetGenerationConfig() + // c.SetIntRange(20, 50) + // st.SetNonRequiredFields = false + // + // // gestruct.SetFieldGenerationSettings("Person", st) + // // gestruct.SetFieldGenerationConfig("Person", c) + // + // gTime := time.Now() + // gestruct.Generate() + // // gestruct.Update() + // // gestruct.Set("Person.P", Person{Age: new(int), Time: time.Now()}) + // err := gestruct.Set( + // "Person.P", + // Person{Age: new(int), Time: time.Now(), Other: &M{Name: "Martin"}}, + // ) + // if err != nil { + // // panic(err) // } - fmt.Println(time.Since(createTime)) - // b := estruct.Build() // - st := config.GenerationSettings{SetNonRequiredFields: true} - gestruct := dstruct.NewGeneratedStructWithConfig(e, - config.NewDstructConfig(), - st, - ) - - c := gestruct.GetGenerationConfig() - c.SetIntRange(20, 50) - st.SetNonRequiredFields = false - - // gestruct.SetFieldGenerationSettings("Person", st) - // gestruct.SetFieldGenerationConfig("Person", c) - - gTime := time.Now() - gestruct.Generate() - // gestruct.Update() - // gestruct.Set("Person.P", Person{Age: new(int), Time: time.Now()}) - err := gestruct.Set( - "Person.P", - Person{Age: new(int), Time: time.Now(), Other: &M{Name: "Martin"}}, - ) - if err != nil { - // panic(err) - } - - fmt.Println("Time to generate: ", time.Since(gTime)) - fmt.Printf("%+v\n", gestruct.Get_("Person.P.Other.Name")) + // fmt.Println("Time to generate: ", time.Since(gTime)) + // fmt.Printf("%+v\n", gestruct.Get_("Person.P.Other.Name")) // for field := range gestruct.GetFields() { // fmt.Println("Field: ", field) // fmt.Printf( @@ -157,26 +157,26 @@ func main() { // ) // } - // generatedStuct := dstruct.NewGeneratedStructWithConfig( - // Test{Cpoint: new(int)}, - // config.NewDstructConfig().SetSliceLength(3, 3), - // config.DefaultGenerationSettings(), - // ) - // gt := &GenInt32Task{} - // generator.AddTask(gt) - // if err := generatedStuct.Set("Person.P", &Person{}); err != nil { - // panic(err) - // } - // generatedStuct.SetFieldGenerationConfig( - // "Person.Value", - // config.NewDstructConfig().SetIntRange(800, 1000), - // ) - // - // // generatedStuct.SetFieldFromTask("C", gt, 300, 400) - // // generatedStuct.Update() - // generatedStuct.Generate() - // - // fmt.Printf("%+v\n", generatedStuct.Get_("Person.P")) - // + generatedStuct := dstruct.NewGeneratedStructWithConfig( + Test{Cpoint: new(int)}, + config.NewDstructConfig().SetSliceLength(3, 3), + config.DefaultGenerationSettings(), + ) + gt := &GenInt32Task{} + generator.AddTask(gt) + if err := generatedStuct.Set("Person.P", Person{}); err != nil { + panic(err) + } + generatedStuct.SetFieldGenerationConfig( + "Person.Value", + config.NewDstructConfig().SetIntRange(800, 1000), + ) + + generatedStuct.SetFieldFromTask("C", gt, 300, 400) + // generatedStuct.Update() + generatedStuct.Generate() + + fmt.Printf("%+v\n", generatedStuct) + fmt.Println("Testing task") } diff --git a/modifier.go b/modifier.go index 65ceaee..65dd7d7 100644 --- a/modifier.go +++ b/modifier.go @@ -155,7 +155,6 @@ func (dm *DynamicStructModifierImpl) Set(field string, value any) error { // update updates the struct's underlying node to represent that of the strct's value. func (dm *DynamicStructModifierImpl) update(node *Node[StructField]) { - fmt.Println("updating node", node.data.qualifiedName, node.data.value) nodeValue := node.data.value if canExtend(nodeValue.Interface()) { node.children = ExtendStruct(nodeValue.Interface()).root.children From 373c9050ca9e41cbf40e45b80500300904674d4b Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Tue, 5 Nov 2024 15:38:06 +0200 Subject: [PATCH 24/31] fix bugs - improve task code and add task example code --- dstruct.generated.struct.go | 43 ++----- examples/task/main.go | 154 ++++++++--------------- generated.struct.go | 10 +- generator/base.task.go | 48 +++++++ generator/core/generated.field.go | 8 +- generator/core/no.arg.function.holder.go | 3 + generator/task.go | 74 +---------- generator/task.instance.go | 6 + generator/task.properties.go | 67 +++++++++- modifier.go | 11 -- tree.builder.go | 10 -- 11 files changed, 197 insertions(+), 237 deletions(-) create mode 100644 generator/base.task.go create mode 100644 generator/task.instance.go diff --git a/dstruct.generated.struct.go b/dstruct.generated.struct.go index e956679..ea2099b 100644 --- a/dstruct.generated.struct.go +++ b/dstruct.generated.struct.go @@ -54,7 +54,7 @@ func NewGeneratedStructWithConfig[T any](val T, } for _, v := range customTypes { - generatedStruct.addCustomType(v) + generatedStruct.AddCustomType(v) } generatedStruct.addCustomTypes() generatedStruct.populateGeneratedFields(generatedStruct.root) @@ -266,24 +266,18 @@ func (gs *DStructGeneratedStruct[T]) SetGenerationFunctions( gs.propagateGenerationFunctions(gs.root, functions) } -// SetFieldFromTask implements GeneratedStruct.SetFieldFromTask -func (gs *DStructGeneratedStruct[T]) SetFieldFromTask( +// SetFieldFromTaskInstance implements GeneratedStruct.SetFieldFromTaskInstanc +func (gs *DStructGeneratedStruct[T]) SetFieldFromTaskInstance( field string, - task generator.Task, - params ...any, + taskInstance generator.TaskInstance, ) error { - taskProperties, err := generator.CreateTaskProperties( - field, - generator.GetTagForTask(generator.TaskName(task.Name()), params...), - ) - if err != nil { - return err - } - gs.SetFieldGenerationFunction( field, - core.NewFunctionHolderNoArgs(task.GenerationFunction(*taskProperties)), + core.NewFunctionHolderNoArgs( + taskInstance.GenerationFunction(), + ), ) + return nil } @@ -334,7 +328,7 @@ func (gs *DStructGeneratedStruct[T]) generateFields() { } func (gs *DStructGeneratedStruct[T]) addCustomTypes() { - gs.addCustomType( + gs.AddCustomType( CustomType{ time.Time{}, core.DefaultDateFunctionHolder(gs.structConfig.GenerationConfig.Date()), @@ -342,7 +336,7 @@ func (gs *DStructGeneratedStruct[T]) addCustomTypes() { ) } -func (gs *DStructGeneratedStruct[T]) addCustomType(customType CustomType) { +func (gs *DStructGeneratedStruct[T]) AddCustomType(customType CustomType) { // TODO: restrict some types from being added such as nil, ints etc gs.customTypes[reflect.TypeOf(customType.Value).String()] = customType.FunctionHolder // the function holder kind is the find that the function retrusn which could either be an existing kind or a new kind i.ie time.Time would be a new kind @@ -368,23 +362,6 @@ func (gs *DStructGeneratedStruct[T]) populateGeneratedFields(node *Node[StructFi if gs.fieldContexts[field.data.qualifiedName] != nil { continue } - // fmt.Println( - // "Field: ", - // field.data.qualifiedName, - // field.data.value.Kind(), - // field.data.ptrDepth, - // field.data.ptrKind, - // field.data.IsFieldDereferencable(), - // ) - - // fmt.Println( - // "POP: ", - // field.data.qualifiedName, - // gs.customTypes, - // field.data.goType, - // field.data.dstructType, - - // ) if customType := gs.customTypes[field.data.goType]; customType != nil { gs.fieldContexts[field.data.qualifiedName] = core.NewGeneratedFieldContext( diff --git a/examples/task/main.go b/examples/task/main.go index 2125655..fc08d6f 100644 --- a/examples/task/main.go +++ b/examples/task/main.go @@ -12,7 +12,7 @@ import ( ) const ( - GenInt32 generator.TaskName = "GenInt32" + GenInt32 string = "GenInt32" ) type genInt32Params struct { @@ -20,60 +20,59 @@ type genInt32Params struct { max int32 } -type GenInt32Task struct{} - -// Ensure GenInt32Task implements Task. -var _ generator.Task = &GenInt32Task{} +type GenInt32Task struct { + generator.BaseTask + numberConfig config.NumberRangeConfig +} -// Tags implements Task. -func (g *GenInt32Task) Name() string { - return string(GenInt32) +type GenInt32TaskInstance struct { + GenInt32Task + genFunc generator.GenerationFunction } -// GenerationFunction implements Task. -func (g *GenInt32Task) GenerationFunction( - taskProperties generator.TaskProperties, -) generator.GenerationFunction { - generator.ValidateParamCount(g, taskProperties) - params := g.getInt32Params(taskProperties.FieldName, taskProperties.Parameters) - numberConfig := config.NewNumberRangeConfig() - numberConfig.Int().SetRange(int(params.min), int(params.max)) - return core.GenerateNumberFunc[int](numberConfig) +var ( + _ generator.TaskInstance = &GenInt32TaskInstance{} + // Ensure GenInt32Task implements Task. + _ generator.Task = &GenInt32Task{} +) + +func NewGenInt32Task() *GenInt32Task { + gt := &GenInt32Task{ + numberConfig: config.NewNumberRangeConfig(), + BaseTask: *generator.NewBaseTask(string(GenInt32), 2), + } + return gt } -func (g *GenInt32Task) ExpectedParameterCount() int { - return 2 +func (g GenInt32Task) Instance(params ...string) generator.TaskInstance { + gt := &GenInt32TaskInstance{ + GenInt32Task: g, + } + gt.numberConfig = g.numberConfig.Copy() + gt.SetParameters(params...) + gt.genFunc = core.GenerateNumberFunc[int32](gt.numberConfig) + return gt } -func (g *GenInt32Task) FunctionHolder(taskProperties generator.TaskProperties) core.FunctionHolder { - return core.NewFunctionHolderNoArgs(g.GenerationFunction(taskProperties)) +func (g *GenInt32TaskInstance) GenerationFunction() generator.GenerationFunction { + return g.genFunc } -func (g *GenInt32Task) getInt32Params(fieldName string, params []string) genInt32Params { - param_1, err := strconv.Atoi(params[0]) +func (g *GenInt32TaskInstance) SetParameters(params ...string) { + g.ValidateParamCount(params...) + min, err := strconv.Atoi(params[0]) if err != nil { - panic(fmt.Sprintf("error with field %s: task %s error: %s", fieldName, GenInt32, err)) + panic(fmt.Sprintf("Error parsing min value: %s", err)) } - - param_2, err := strconv.Atoi(params[1]) + max, err := strconv.Atoi(params[1]) if err != nil { - panic(fmt.Sprintf("error with field %s: task %s error: %s", fieldName, GenInt32, err)) - } - - if param_1 > param_2 { - err = fmt.Errorf( - "min must be less or equal to the max value min = %d max = %d", - param_1, - param_2, - ) - panic(fmt.Sprintf("error with field %s: task %s error: %s", fieldName, GenInt32, err)) - + panic(fmt.Sprintf("Error parsing max value: %s", err)) } - return genInt32Params{ - min: int32(param_1), - max: int32(param_2), + if min > max { + panic(fmt.Sprintf("min value %d is greater than max value %d", min, max)) } + g.numberConfig.Int32().SetRange(int32(min), int32(max)) } type M struct { @@ -81,14 +80,9 @@ type M struct { } type Person struct { - // Love ABC Age *int Time time.Time Other *M - - // Parray []Person - - // Person *Person } type P struct { @@ -97,72 +91,20 @@ type P struct { } type Test struct { - // A *int - // S string - C int - Person P `json:"person"` - + B int32 `gen_task:"GenInt32(2)" gen_task_1:"10" gen_task_2:"20"` + C int32 + Person P Cpoint *int T time.Time - - // Pa []Pa - // Parray []Person - - // L } func main() { - // e := dstruct.ExtendStruct(Test{}).Build().Instance() - // createTime := time.Now() - // // for i := 0; i < 1; i++ { - // // estruct.AddField(fmt.Sprintf("Test_%d", i), Test{}, "") - // // } - // fmt.Println(time.Since(createTime)) - // // b := estruct.Build() - // // - // st := config.GenerationSettings{SetNonRequiredFields: true} - // gestruct := dstruct.NewGeneratedStructWithConfig(e, - // config.NewDstructConfig(), - // st, - // ) - // - // c := gestruct.GetGenerationConfig() - // c.SetIntRange(20, 50) - // st.SetNonRequiredFields = false - // - // // gestruct.SetFieldGenerationSettings("Person", st) - // // gestruct.SetFieldGenerationConfig("Person", c) - // - // gTime := time.Now() - // gestruct.Generate() - // // gestruct.Update() - // // gestruct.Set("Person.P", Person{Age: new(int), Time: time.Now()}) - // err := gestruct.Set( - // "Person.P", - // Person{Age: new(int), Time: time.Now(), Other: &M{Name: "Martin"}}, - // ) - // if err != nil { - // // panic(err) - // } - // - // fmt.Println("Time to generate: ", time.Since(gTime)) - // fmt.Printf("%+v\n", gestruct.Get_("Person.P.Other.Name")) - // for field := range gestruct.GetFields() { - // fmt.Println("Field: ", field) - // fmt.Printf( - // "'%s': dstruct: %+v goType: %+v\n", - // field, - // value.GetDstructType(), - // value.GetGoType(), - // ) - // } - generatedStuct := dstruct.NewGeneratedStructWithConfig( Test{Cpoint: new(int)}, config.NewDstructConfig().SetSliceLength(3, 3), config.DefaultGenerationSettings(), ) - gt := &GenInt32Task{} + gt := NewGenInt32Task() generator.AddTask(gt) if err := generatedStuct.Set("Person.P", Person{}); err != nil { panic(err) @@ -171,12 +113,14 @@ func main() { "Person.Value", config.NewDstructConfig().SetIntRange(800, 1000), ) - - generatedStuct.SetFieldFromTask("C", gt, 300, 400) - // generatedStuct.Update() + gti := gt.Instance("20", "30").(*GenInt32TaskInstance) + generatedStuct.SetFieldFromTaskInstance("C", gti) generatedStuct.Generate() fmt.Printf("%+v\n", generatedStuct) + gti.SetParameters("100", "200") - fmt.Println("Testing task") + generatedStuct.Generate() + + fmt.Printf("%+v\n", generatedStuct) } diff --git a/generated.struct.go b/generated.struct.go index be409f6..3516907 100644 --- a/generated.struct.go +++ b/generated.struct.go @@ -72,6 +72,12 @@ type GeneratedStruct interface { // SetGenerationFunctions sets the generation functions for the struct and propagates the settings to all fields SetGenerationFunctions(functions core.DefaultGenerationFunctions) - // SetFieldFromTask sets the field value from the task. The task is used to generate the value for the field. - SetFieldFromTask(field string, task generator.Task, params ...any) error + // SetFieldFromTaskInstance sets the field value from the task instance. The task is used to generate the value for the field. + SetFieldFromTaskInstance( + field string, + taskInstance generator.TaskInstance, + ) error + + // AddCustomType adds a custom type to the struct. The custom type is used to generate values for fields of the custom type. + AddCustomType(customType CustomType) } diff --git a/generator/base.task.go b/generator/base.task.go new file mode 100644 index 0000000..c8e26fa --- /dev/null +++ b/generator/base.task.go @@ -0,0 +1,48 @@ +package generator + +import ( + "fmt" + "reflect" +) + +type BaseTask struct { + taskName string + parameterCount int +} + +func NewBaseTask(taskName string, parameterCount int) *BaseTask { + return &BaseTask{ + taskName: taskName, + parameterCount: parameterCount, + } +} + +func (b *BaseTask) ParameterCount() int { + return b.parameterCount +} + +func (b *BaseTask) GetTags(params ...string) reflect.StructTag { + b.ValidateParamCount(params...) + tags := fmt.Sprintf(`gen_task:"%s(%d)"`, b.taskName, len(params)) + for i, p := range params { + tags += fmt.Sprintf(` gen_task_%d:"%v"`, (i + 1), p) + } + return reflect.StructTag(tags) +} + +func (b *BaseTask) Name() string { + return b.taskName +} + +func (b *BaseTask) ValidateParamCount(params ...string) { + if len(params) != b.parameterCount && b.parameterCount != -1 { + panic( + fmt.Sprintf( + "error with task '%s': task requires %d parameters but has %d", + b.taskName, + b.parameterCount, + len(params), + ), + ) + } +} diff --git a/generator/core/generated.field.go b/generator/core/generated.field.go index 016a22e..f362793 100644 --- a/generator/core/generated.field.go +++ b/generator/core/generated.field.go @@ -286,15 +286,17 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio _, ok = tags.Lookup("gen_task") if ok { - taskProperties, err := generator.CreateTaskProperties(field.Name, tags) + + taskProperties, err := generator.CreateTaskPropertiesFromTag(tags) if err != nil { - panic(fmt.Sprintf("Error decoding gen_task: %s", err.Error())) + panic(fmt.Sprintf("Error decoding gen_task for '%s': %s", field.Name, err.Error())) } task := generator.GetTask(taskProperties.TaskName) if task == nil { panic(fmt.Sprintf("Unregistered task %s", taskProperties.TaskName)) } - return task.GenerationFunction(*taskProperties) + return task.Instance(taskProperties.Parameters()...).GenerationFunction() + } // if we get no match, we default to the default generation function for the kind diff --git a/generator/core/no.arg.function.holder.go b/generator/core/no.arg.function.holder.go index 699d2d2..591eae3 100644 --- a/generator/core/no.arg.function.holder.go +++ b/generator/core/no.arg.function.holder.go @@ -19,6 +19,9 @@ func NewFunctionHolderNoArgs( return &FunctionHolderWithNoArgs{ BaseFunctionHolder: BaseFunctionHolder{ generationFunction: generationFunction, + resetFunction: func(cfg config.Config) generator.GenerationFunction { + return generationFunction + }, }, } } diff --git a/generator/task.go b/generator/task.go index d11b18f..f782112 100644 --- a/generator/task.go +++ b/generator/task.go @@ -3,16 +3,13 @@ package generator import ( "fmt" "reflect" - "strconv" - "strings" ) -type TaskName string - type Task interface { - GenerationFunction(taskProperties TaskProperties) GenerationFunction - ExpectedParameterCount() int + GetTags(params ...string) reflect.StructTag + ParameterCount() int Name() string + Instance(params ...string) TaskInstance } var tasks map[string]Task @@ -33,68 +30,3 @@ func AddTask(task Task) error { tasks[name] = task return nil } - -func getParameters(parameterCount int, tags reflect.StructTag) (parameters []string) { - for i := 1; i <= parameterCount; i++ { - parameters = append(parameters, tags.Get(fmt.Sprintf("gen_task_%d", i))) - } - return parameters -} - -func CreateTaskProperties(fieldName string, tags reflect.StructTag) (*TaskProperties, error) { - gen_task_tag := strings.TrimSpace(tags.Get("gen_task")) - leftBraceIndex := strings.Index(gen_task_tag, "(") - if leftBraceIndex == -1 { - return nil, fmt.Errorf( - "error with field %s: task %s error: no ( found", - fieldName, - gen_task_tag, - ) - } - - if gen_task_tag[len(gen_task_tag)-1:] != ")" { - return nil, fmt.Errorf( - "error with field %s: task %s error: last character of task must be )", - fieldName, - gen_task_tag, - ) - } - taskName := gen_task_tag[:leftBraceIndex] - parameterCount, err := strconv.Atoi(gen_task_tag[leftBraceIndex+1 : len(gen_task_tag)-1]) - if err != nil { - return nil, fmt.Errorf("error getting task parameter count: %w", err) - } - - return &TaskProperties{ - TaskName: taskName, - Parameters: getParameters(parameterCount, tags), - FieldName: fieldName, - }, nil -} - -func GetTagForTask(name TaskName, params ...any) reflect.StructTag { - if tasks[string(name)] == nil { - panic(fmt.Sprintf("Task '%s' is not registered", name)) - } - - tags := fmt.Sprintf(`gen_task:"%s(%d)"`, name, len(params)) - for i, p := range params { - tags += fmt.Sprintf(` gen_task_%d:"%v"`, (i + 1), p) - } - - return reflect.StructTag(tags) -} - -func ValidateParamCount(task Task, taskProperties TaskProperties) { - if len(taskProperties.Parameters) != task.ExpectedParameterCount() { - panic( - fmt.Sprintf( - "error with field %s. task '%s': task requires %d parameters but has %d", - taskProperties.FieldName, - task.Name(), - task.ExpectedParameterCount(), - len(taskProperties.Parameters), - ), - ) - } -} diff --git a/generator/task.instance.go b/generator/task.instance.go new file mode 100644 index 0000000..5793f83 --- /dev/null +++ b/generator/task.instance.go @@ -0,0 +1,6 @@ +package generator + +type TaskInstance interface { + GenerationFunction() GenerationFunction + SetParameters(params ...string) +} diff --git a/generator/task.properties.go b/generator/task.properties.go index 260bdd2..09af723 100644 --- a/generator/task.properties.go +++ b/generator/task.properties.go @@ -1,7 +1,70 @@ package generator +import ( + "fmt" + "reflect" + "strconv" + "strings" +) + type TaskProperties struct { TaskName string - Parameters []string - FieldName string + parameters []string +} + +// CreateTaskPropertiesFromTag creates a TaskProperties struct from a reflect.StructTag +// structure of gen task tag is as follows: gen_task:"task_name(parameter_count)" gen_task_1:"parameter_1" gen_task_2:"parameter_2" +func CreateTaskPropertiesFromTag(tag reflect.StructTag) (*TaskProperties, error) { + gen_task_tag := strings.TrimSpace(tag.Get("gen_task")) + leftBraceIndex := strings.Index(gen_task_tag, "(") + if leftBraceIndex == -1 { + return nil, fmt.Errorf( + "error creating task properties for task %s: no ( found", + gen_task_tag, + ) + } + + if gen_task_tag[len(gen_task_tag)-1:] != ")" { + return nil, fmt.Errorf( + "error creating task properties for task %s: last character of task must be )", + gen_task_tag, + ) + } + taskName := gen_task_tag[:leftBraceIndex] + parameterCount, err := strconv.Atoi(gen_task_tag[leftBraceIndex+1 : len(gen_task_tag)-1]) + if err != nil { + return nil, fmt.Errorf("error getting task parameter count: %w", err) + } + + return &TaskProperties{ + TaskName: taskName, + parameters: getParameters(parameterCount, tag), + }, nil +} + +func CreateTaskPropertiesFromParams(taskName string, params ...any) *TaskProperties { + tp := &TaskProperties{ + TaskName: taskName, + } + tp.SetParameters(params...) + return tp +} + +func (tp *TaskProperties) SetParameters(params ...any) { + p := make([]string, len(params)) + for i, param := range params { + p[i] = fmt.Sprintf("%v", param) + } + tp.parameters = p +} + +func (tp *TaskProperties) Parameters() []string { + return tp.parameters +} + +func getParameters(parameterCount int, tags reflect.StructTag) (parameters []string) { + for i := 1; i <= parameterCount; i++ { + parameters = append(parameters, tags.Get(fmt.Sprintf("gen_task_%d", i))) + } + return parameters } diff --git a/modifier.go b/modifier.go index 65dd7d7..7f14c31 100644 --- a/modifier.go +++ b/modifier.go @@ -136,17 +136,6 @@ func (dm *DynamicStructModifierImpl) Set(field string, value any) error { return nil } - // createTree := fieldValue.Kind() == reflect.Pointer && value != nil - - // fmt.Println( - // "fieldValue: ", - // f.data.qualifiedName, - // fieldValue.Kind(), - // fieldValue.IsZero(), - // fieldValue.Interface(), - // createTree, - // value, - // ) fieldValue.Set(dreflect.Convert(reflect.ValueOf(value), fieldValue.Type())) dm.update(f) diff --git a/tree.builder.go b/tree.builder.go index a732f61..3188ec2 100644 --- a/tree.builder.go +++ b/tree.builder.go @@ -384,16 +384,6 @@ func (dsb *treeBuilderImpl) addStructFields( pkgPath := strct.Type().Field(i).PkgPath anonymous := strct.Type().Field(i).Anonymous fieldType := dsb.addFieldToTree(fieldName, fieldValue, pkgPath, anonymous, fieldTag, root) - // fmt.Printf( - // "\nfieldType: '%s' %+v %+v %+v PO: %+v S: %+v \n", - // fieldName, - // fieldType, - // reflect.TypeOf(fieldValue), - // strct.Field(i).Type(), - // reflect.TypeOf(reflect.ValueOf(fieldValue).Interface()), - // reflect.TypeOf(strct.Interface()), - // ) - // // // fmt.Println() if anonymous { pkgPath = "" From 7c78b495921017fdaea25015711316d9531fc881 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Tue, 5 Nov 2024 19:44:35 +0200 Subject: [PATCH 25/31] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b2867e..39ecc16 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ > :warning: **Notice** > * Please do not install any of these versions: v1.1.1 v1.1.0 v1.0.0 v0.1.2 v0.1.1 as these were removed from the repo - (but are still available at pkg.go.dev). -> * When installing please explicitly install the actual latest version of dstruct which is currently v0.4.0-beta. +> * When installing please explicitly install the actual latest version of dstruct which is currently v0.5.0-beta. # dstruct From d59277f15a14da80df48170b75d2259b5cf7a56c Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Tue, 5 Nov 2024 19:47:34 +0200 Subject: [PATCH 26/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 39ecc16..b35a5d4 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ extend or merge structs which have struct fields of type `any` their value must ## Install ```sh -go get github.com/MartinSimango/dstruct@v0.4.0-beta +go get github.com/MartinSimango/dstruct@v0.5.0-beta ``` ## How it works? From 46df1e6eb2e6fb1750913b85b1d7403ddbd64e12 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sat, 9 Nov 2024 20:15:29 +0200 Subject: [PATCH 27/31] feat: update generation settings --- dagger.json | 11 +++++++++++ generator/config/generation.settings.go | 22 ++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 dagger.json diff --git a/dagger.json b/dagger.json new file mode 100644 index 0000000..702dcb9 --- /dev/null +++ b/dagger.json @@ -0,0 +1,11 @@ +{ + "name": "dstruct", + "engineVersion": "v0.13.7", + "dependencies": [ + { + "name": "nsv", + "source": "github.com/purpleclay/daggerverse/nsv@nsv/v0.10.0", + "pin": "dcf4cc7865ada8433f601f728e4a019b68d1fe4e" + } + ] +} diff --git a/generator/config/generation.settings.go b/generator/config/generation.settings.go index 13a6a53..8180fb2 100644 --- a/generator/config/generation.settings.go +++ b/generator/config/generation.settings.go @@ -5,8 +5,12 @@ type ConfigType uint type ValueGenerationType uint8 const ( - Generate ValueGenerationType = iota // will generate all field - GenerateOnce // will generate the fields once + // Generate will generated all fields. + Generate ValueGenerationType = iota + // GenerateOnce will generate the fields once. + GenerateOnce + // UseDefaults will use the default value of the field based on the default tag. If the default tag is not set, the + // field will use the field type's default generation function to generate a value for the field. UseDefaults ) @@ -30,6 +34,20 @@ func (gs *GenerationSettings) WithNonRequiredFields(required bool) GenerationSet return *gs } +func (gs *GenerationSettings) WithValueGenerationType( + valueGenerationType ValueGenerationType, +) GenerationSettings { + gs.ValueGenerationType = valueGenerationType + return *gs +} + +func (gs *GenerationSettings) WithRecursiveDefinition( + recursiveDefinition RecursiveDefinition, +) GenerationSettings { + gs.RecursiveDefinition = recursiveDefinition + return *gs +} + // DefaultGenerationSettings returns a default configuration for generation values. func DefaultGenerationSettings() GenerationSettings { return GenerationSettings{ From 74c0699ca3d04aa68f274aed29faad141e8c3315 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sat, 23 Nov 2024 21:40:11 +0200 Subject: [PATCH 28/31] chore: update dagger engine version and nsv version --- dagger.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dagger.json b/dagger.json index 702dcb9..98b8a4c 100644 --- a/dagger.json +++ b/dagger.json @@ -1,10 +1,10 @@ { "name": "dstruct", - "engineVersion": "v0.13.7", + "engineVersion": "v0.14.0", "dependencies": [ { "name": "nsv", - "source": "github.com/purpleclay/daggerverse/nsv@nsv/v0.10.0", + "source": "github.com/purpleclay/daggerverse/nsv@nsv/v0.10.1", "pin": "dcf4cc7865ada8433f601f728e4a019b68d1fe4e" } ] From 7938f5f16d6a35553db54788b8993c29060c24cb Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Thu, 12 Dec 2024 09:07:20 +0200 Subject: [PATCH 29/31] chore: start adding changes for automating releases with dagger and semantic release --- .github/workflows/release.yaml | 24 ++++++++++ .releaserc.yaml | 15 ++++++ Makefile | 6 +++ dagger.json | 18 ++++++-- dagger/.gitattributes | 4 ++ dagger/.gitignore | 4 ++ dagger/go.mod | 50 ++++++++++++++++++++ dagger/go.sum | 83 ++++++++++++++++++++++++++++++++++ dagger/main.go | 55 ++++++++++++++++++++++ 9 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release.yaml create mode 100644 .releaserc.yaml create mode 100644 dagger/.gitattributes create mode 100644 dagger/.gitignore create mode 100644 dagger/go.mod create mode 100644 dagger/go.sum create mode 100644 dagger/main.go diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..c7afe1a --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,24 @@ +name: Release + +on: + push: + branches: + - '**' + + +jobs: + dagger-go-flow: + name: dagger-go-flow + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/Checkout@v3 + - name: Dagger Go Flow + uses: dagger/dagger-for-github@v6 + with: + version: "latest" + verb: call + module: github.com/felipecruz91/daggerverse/go + args: test --source=. --go-version=1.23 stdout + #args: --help + diff --git a/.releaserc.yaml b/.releaserc.yaml new file mode 100644 index 0000000..625af82 --- /dev/null +++ b/.releaserc.yaml @@ -0,0 +1,15 @@ +branches: + - "feat/update-generation-settings" +plugins: + - - "@semantic-release/commit-analyzer" + - preset: conventionalcommits + - - "@semantic-release/release-notes-generator" + - preset: conventionalcommits + - "@semantic-release/github" + - - "@semantic-release/changelog" + - assets: + - CHANGELOG.md + - - "@semantic-release/git" + - assets: + - CHANGELOG.md +tagFormat: ${version} diff --git a/Makefile b/Makefile index ddec718..bafbb14 100644 --- a/Makefile +++ b/Makefile @@ -46,3 +46,9 @@ bench: task-example: go run examples/task/main.go + + + +### ACT ### +act: + act -P ubuntu-latest=catthehacker/ubuntu:act-latest diff --git a/dagger.json b/dagger.json index 98b8a4c..357d3db 100644 --- a/dagger.json +++ b/dagger.json @@ -1,11 +1,23 @@ { - "name": "dstruct", + "name": "dagger", "engineVersion": "v0.14.0", + "sdk": "go", "dependencies": [ + { + "name": "node", + "source": "github.com/dagger/dagger/sdk/typescript/dev/node", + "pin": "180573cf2a1359a7ce371f92a6b6d5fdde62c5a4" + }, + { + "name": "goreleaser", + "source": "github.com/goreleaser/goreleaser@v2.4.8", + "pin": "377981ebd76e1bbb0dbe07d5428239ec8c5381a8" + }, { "name": "nsv", "source": "github.com/purpleclay/daggerverse/nsv@nsv/v0.10.1", - "pin": "dcf4cc7865ada8433f601f728e4a019b68d1fe4e" + "pin": "424af485b7e79437e0a0f799d083edd3e843128e" } - ] + ], + "source": "dagger" } diff --git a/dagger/.gitattributes b/dagger/.gitattributes new file mode 100644 index 0000000..3a45493 --- /dev/null +++ b/dagger/.gitattributes @@ -0,0 +1,4 @@ +/dagger.gen.go linguist-generated +/internal/dagger/** linguist-generated +/internal/querybuilder/** linguist-generated +/internal/telemetry/** linguist-generated diff --git a/dagger/.gitignore b/dagger/.gitignore new file mode 100644 index 0000000..7ebabcc --- /dev/null +++ b/dagger/.gitignore @@ -0,0 +1,4 @@ +/dagger.gen.go +/internal/dagger +/internal/querybuilder +/internal/telemetry diff --git a/dagger/go.mod b/dagger/go.mod new file mode 100644 index 0000000..b97da30 --- /dev/null +++ b/dagger/go.mod @@ -0,0 +1,50 @@ +module dagger/dagger + +go 1.23.2 + +require ( + github.com/99designs/gqlgen v0.17.55 + github.com/Khan/genqlient v0.7.0 + github.com/vektah/gqlparser/v2 v2.5.17 + go.opentelemetry.io/otel v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 + go.opentelemetry.io/otel/log v0.3.0 + go.opentelemetry.io/otel/metric v1.27.0 + go.opentelemetry.io/otel/sdk v1.27.0 + go.opentelemetry.io/otel/sdk/log v0.3.0 + go.opentelemetry.io/otel/sdk/metric v1.27.0 + go.opentelemetry.io/otel/trace v1.27.0 + go.opentelemetry.io/proto/otlp v1.3.1 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa + golang.org/x/sync v0.8.0 + google.golang.org/grpc v1.65.0 +) + +require ( + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect + github.com/sosodev/duration v1.3.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/protobuf v1.34.2 // indirect +) + +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 + +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 + +replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.3.0 + +replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.3.0 diff --git a/dagger/go.sum b/dagger/go.sum new file mode 100644 index 0000000..c52dd24 --- /dev/null +++ b/dagger/go.sum @@ -0,0 +1,83 @@ +github.com/99designs/gqlgen v0.17.55 h1:3vzrNWYyzSZjGDFo68e5j9sSauLxfKvLp+6ioRokVtM= +github.com/99designs/gqlgen v0.17.55/go.mod h1:3Bq768f8hgVPGZxL8aY9MaYmbxa6llPM/qu1IGH1EJo= +github.com/Khan/genqlient v0.7.0 h1:GZ1meyRnzcDTK48EjqB8t3bcfYvHArCUUvgOwpz1D4w= +github.com/Khan/genqlient v0.7.0/go.mod h1:HNyy3wZvuYwmW3Y7mkoQLZsa/R5n5yIRajS1kPBvSFM= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= +github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vektah/gqlparser/v2 v2.5.17 h1:9At7WblLV7/36nulgekUgIaqHZWn5hxqluxrxGUhOmI= +github.com/vektah/gqlparser/v2 v2.5.17/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 h1:oM0GTNKGlc5qHctWeIGTVyda4iFFalOzMZ3Ehj5rwB4= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88/go.mod h1:JGG8ebaMO5nXOPnvKEl+DiA4MGwFjCbjsxT1WHIEBPY= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 h1:ccBrA8nCY5mM0y5uO7FT0ze4S0TuFcWdDB2FxGMTjkI= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0/go.mod h1:/9pb6634zi2Lk8LYg9Q0X8Ar6jka4dkFOylBLbVQPCE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= +go.opentelemetry.io/otel/log v0.3.0 h1:kJRFkpUFYtny37NQzL386WbznUByZx186DpEMKhEGZs= +go.opentelemetry.io/otel/log v0.3.0/go.mod h1:ziCwqZr9soYDwGNbIL+6kAvQC+ANvjgG367HVcyR/ys= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk/log v0.3.0 h1:GEjJ8iftz2l+XO1GF2856r7yYVh74URiF9JMcAacr5U= +go.opentelemetry.io/otel/sdk/log v0.3.0/go.mod h1:BwCxtmux6ACLuys1wlbc0+vGBd+xytjmjajwqqIul2g= +go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= +go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/dagger/main.go b/dagger/main.go new file mode 100644 index 0000000..cecc182 --- /dev/null +++ b/dagger/main.go @@ -0,0 +1,55 @@ +// A generated module for Dagger functions +// +// This module has been generated via dagger init and serves as a reference to +// basic module structure as you get started with Dagger. +// +// Two functions have been pre-created. You can modify, delete, or add to them, +// as needed. They demonstrate usage of arguments and return types using simple +// echo and grep commands. The functions can be called from the dagger CLI or +// from one of the SDKs. +// +// The first line in this comment block is a short description line and the +// rest is a long description with more detail on the module's purpose or usage, +// if appropriate. All modules should have a short description. + +package main + +import ( + "context" + "dagger/dagger/internal/dagger" +) + +type Dagger struct{} + +// Returns a container that echoes whatever string argument is provided +func (m *Dagger) Release(ctx context.Context, source *dagger.Directory) (string, error){ + + + return dag.Node(dagger.NodeOpts{ + Version: "21"}). + WithNpm(). + WithSource(source). + Container(). + WithExec([]string{"npm", "install","-g","@semantic-release/git"}). + WithExec([]string{"npx","semantic-release", "--help"}). + Stdout(ctx) + + // Commands(). + // Run([]string{"npx", "semantic-release", "--version"}). + // Stdout(ctx) + + + + // version, err := dag.Nsv(source).Next(ctx, dagger.NsvNextOpts{Show: true}) + // if err != nil { + // return "", err + // } + // + // fmt.Println("Version: ", version) + // + // return dag.Goreleaser(dagger.GoreleaserOpts{Source: source}).Test().Output(ctx) + + + + +} From 3c0064389f59af0508e77bcd226ad42ac9bb20ab Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sun, 12 Jan 2025 20:15:51 +0200 Subject: [PATCH 30/31] feat: modify dagger release code --- dagger.json | 2 +- dagger/main.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dagger.json b/dagger.json index 357d3db..14d6017 100644 --- a/dagger.json +++ b/dagger.json @@ -1,6 +1,6 @@ { "name": "dagger", - "engineVersion": "v0.14.0", + "engineVersion": "v0.15.1", "sdk": "go", "dependencies": [ { diff --git a/dagger/main.go b/dagger/main.go index cecc182..6dbdf4d 100644 --- a/dagger/main.go +++ b/dagger/main.go @@ -30,8 +30,9 @@ func (m *Dagger) Release(ctx context.Context, source *dagger.Directory) (string, WithNpm(). WithSource(source). Container(). - WithExec([]string{"npm", "install","-g","@semantic-release/git"}). - WithExec([]string{"npx","semantic-release", "--help"}). + WithExec([]string{"npm", "install","--save-dev","@semantic-release/git"}). + WithExec([]string{"npm","install","--save-dev","@semantic-release/changelog"}). + WithExec([]string{"npx","semantic-release"}). Stdout(ctx) // Commands(). From 42e7477af8aa64c9c60dfb8340d0a69c061a9414 Mon Sep 17 00:00:00 2001 From: Martin Simango Date: Sun, 23 Feb 2025 11:06:32 +0200 Subject: [PATCH 31/31] feat: use type hashes for custom types --- dreflect/dreflect.go | 36 ++++++++++++++ dstruct.generated.struct.go | 10 ++-- examples/builder/adding-a-field/main.go | 4 +- examples/task/main.go | 16 +++---- generator/config/generation.settings.go | 31 +++++++------ generator/core/generated.field.go | 20 ++++---- generator/core/slice.go | 3 +- struct.field.go | 8 ++-- tests/dstruct_test/tree.builder_test.go | 62 ++++++++++++++++++------- tree.builder.go | 56 +++++++++++----------- 10 files changed, 160 insertions(+), 86 deletions(-) diff --git a/dreflect/dreflect.go b/dreflect/dreflect.go index cfaa69a..3cc3e3b 100644 --- a/dreflect/dreflect.go +++ b/dreflect/dreflect.go @@ -3,11 +3,47 @@ package dreflect import ( + "crypto/sha256" "fmt" "reflect" "unsafe" ) +// GetTypeHash recursively generates a hash based on struct type +func GetTypeHash(v interface{}) string { + t := reflect.TypeOf(v) // Get type + + // If it's a pointer, dereference + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + + // Base case: If it's not a struct, just return type name + if t.Kind() != reflect.Struct { + return t.String() + } + + // Construct type signature + typeSignature := t.String() + "{" + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + fieldType := field.Type + + // If field is a struct, recurse + if fieldType.Kind() == reflect.Struct { + typeSignature += field.Name + ":" + GetTypeHash( + reflect.New(fieldType).Elem().Interface(), + ) + ";" + } else { + typeSignature += field.Name + ":" + fieldType.String() + ";" + } + } + typeSignature += "}" + // Compute SHA-256 hash + hash := sha256.Sum256([]byte(typeSignature)) + return fmt.Sprintf("%x", hash) // Return as hex string +} + // GetPointerOfValueType returns a pointer to a new value of the same type as the input value `val`. // The new value is initialized to the input value. func GetPointerOfValueType(val any) any { diff --git a/dstruct.generated.struct.go b/dstruct.generated.struct.go index ea2099b..381090d 100644 --- a/dstruct.generated.struct.go +++ b/dstruct.generated.struct.go @@ -338,7 +338,7 @@ func (gs *DStructGeneratedStruct[T]) addCustomTypes() { func (gs *DStructGeneratedStruct[T]) AddCustomType(customType CustomType) { // TODO: restrict some types from being added such as nil, ints etc - gs.customTypes[reflect.TypeOf(customType.Value).String()] = customType.FunctionHolder + gs.customTypes[dreflect.GetTypeHash(customType.Value)] = customType.FunctionHolder // the function holder kind is the find that the function retrusn which could either be an existing kind or a new kind i.ie time.Time would be a new kind // TODO:idea: GenerationsFunction key should be type of CustomKind and not reflect.Kind gs.structConfig.GenerationFunctions[customType.FunctionHolder.Kind()] = customType.FunctionHolder @@ -353,7 +353,7 @@ func (gs *DStructGeneratedStruct[T]) createGeneratedField( field.data.tag, gs.structConfig.Copy(kind), gs.customTypes, - field.data.goType) + field.data.typeHash) return v } @@ -363,7 +363,7 @@ func (gs *DStructGeneratedStruct[T]) populateGeneratedFields(node *Node[StructFi continue } - if customType := gs.customTypes[field.data.goType]; customType != nil { + if customType := gs.customTypes[field.data.typeHash]; customType != nil { gs.fieldContexts[field.data.qualifiedName] = core.NewGeneratedFieldContext( gs.createGeneratedField(field, customType.Kind()), ) @@ -383,7 +383,7 @@ func (gs *DStructGeneratedStruct[T]) propagateConfig( ) { for _, field := range node.children { // Don't propagate changes to children nodes if the field is a custom type - if field.HasChildren() && gs.customTypes[field.data.goType] == nil { + if field.HasChildren() && gs.customTypes[field.data.typeHash] == nil { gs.propagateConfig(field, cfg) } else { gs.fieldContexts[field.data.qualifiedName].GeneratedField.SetConfig(cfg) @@ -396,7 +396,7 @@ func (gs *DStructGeneratedStruct[T]) propagateSettings( settings config.GenerationSettings, ) { for _, field := range node.children { - if field.HasChildren() && gs.customTypes[field.data.goType] == nil { + if field.HasChildren() && gs.customTypes[field.data.typeHash] == nil { gs.propagateSettings(field, settings) } else { gs.fieldContexts[field.data.qualifiedName].GeneratedField.SetGenerationSettings(settings) diff --git a/examples/builder/adding-a-field/main.go b/examples/builder/adding-a-field/main.go index a87c6ca..25eff42 100644 --- a/examples/builder/adding-a-field/main.go +++ b/examples/builder/adding-a-field/main.go @@ -57,7 +57,7 @@ func errorExample_2() { defer examples.RecoverFromPanic() builder := dstruct.NewBuilder(). AddField("Person", Person{Name: "Martin", Age: 25, Address: Address{Street: "Jackson Street"}}, `json:"person"`). - AddField("Job Titile", "Software Developer", "") + AddField("Job Title", "Software Developer", "") - builder.Build() // This will panic because the field "Job Titile" is an invalid struct field name + builder.Build() // This will panic because the field "Job Title" is an invalid struct field name } diff --git a/examples/task/main.go b/examples/task/main.go index fc08d6f..62f6103 100644 --- a/examples/task/main.go +++ b/examples/task/main.go @@ -99,28 +99,28 @@ type Test struct { } func main() { - generatedStuct := dstruct.NewGeneratedStructWithConfig( + generatedStruct := dstruct.NewGeneratedStructWithConfig( Test{Cpoint: new(int)}, config.NewDstructConfig().SetSliceLength(3, 3), config.DefaultGenerationSettings(), ) gt := NewGenInt32Task() generator.AddTask(gt) - if err := generatedStuct.Set("Person.P", Person{}); err != nil { + if err := generatedStruct.Set("Person.P", Person{}); err != nil { panic(err) } - generatedStuct.SetFieldGenerationConfig( + generatedStruct.SetFieldGenerationConfig( "Person.Value", config.NewDstructConfig().SetIntRange(800, 1000), ) gti := gt.Instance("20", "30").(*GenInt32TaskInstance) - generatedStuct.SetFieldFromTaskInstance("C", gti) - generatedStuct.Generate() + generatedStruct.SetFieldFromTaskInstance("C", gti) + generatedStruct.Generate() - fmt.Printf("%+v\n", generatedStuct) + fmt.Printf("%+v\n", generatedStruct) gti.SetParameters("100", "200") - generatedStuct.Generate() + generatedStruct.Generate() - fmt.Printf("%+v\n", generatedStuct) + fmt.Printf("%v\n", generatedStruct) } diff --git a/generator/config/generation.settings.go b/generator/config/generation.settings.go index 6422f81..1298fa0 100644 --- a/generator/config/generation.settings.go +++ b/generator/config/generation.settings.go @@ -29,12 +29,29 @@ type GenerationSettings struct { RecursiveDefinition RecursiveDefinition } +// DefaultGenerationSettings returns a default configuration for generation values. +func DefaultGenerationSettings() GenerationSettings { + return GenerationSettings{ + ValueGenerationType: UseDefaults, + SetNonRequiredFields: false, + RecursiveDefinition: RecursiveDefinition{ + Allow: false, + Depth: 1, + }, + } +} + +// NewGenerationSettings returns a new instance of GenerationSettings. +func NewGenerationSettings() *GenerationSettings { + gs := DefaultGenerationSettings() + return &gs +} + func (gs *GenerationSettings) WithNonRequiredFields(required bool) GenerationSettings { gs.SetNonRequiredFields = required return *gs } - func (gs *GenerationSettings) WithValueGenerationType( valueGenerationType ValueGenerationType, ) GenerationSettings { @@ -48,15 +65,3 @@ func (gs *GenerationSettings) WithRecursiveDefinition( gs.RecursiveDefinition = recursiveDefinition return *gs } - -// DefaultGenerationSettings returns a default configuration for generation values. -func DefaultGenerationSettings() GenerationSettings { - return GenerationSettings{ - ValueGenerationType: UseDefaults, - SetNonRequiredFields: false, - RecursiveDefinition: RecursiveDefinition{ - Allow: false, - Depth: 1, - }, - } -} diff --git a/generator/core/generated.field.go b/generator/core/generated.field.go index f362793..09f2adb 100644 --- a/generator/core/generated.field.go +++ b/generator/core/generated.field.go @@ -50,7 +50,7 @@ type GeneratedField struct { Parent *GeneratedField PointerValue *reflect.Value customTypes map[string]FunctionHolder - goType string + typeHash string currentGenerationFunction generator.GenerationFunction } @@ -59,7 +59,7 @@ func NewGeneratedField(fqn string, tag reflect.StructTag, config GeneratedFieldConfig, customTypes map[string]FunctionHolder, - goType string, + typeHash string, ) *GeneratedField { generateField := &GeneratedField{ Name: fqn, @@ -67,7 +67,7 @@ func NewGeneratedField(fqn string, Tag: tag, Config: config, customTypes: customTypes, - goType: goType, + typeHash: typeHash, } // TODO: add custom type to GenerationFunctions if value.Kind() == reflect.Slice { @@ -93,11 +93,11 @@ func NewGeneratedField(fqn string, } func (field *GeneratedField) IsCustomType() bool { - return field.customTypes[field.goType] != nil + return field.customTypes[field.typeHash] != nil } func (field *GeneratedField) customTypeFunctionHolder() FunctionHolder { - return field.customTypes[field.goType] + return field.customTypes[field.typeHash] } func (field *GeneratedField) SetConfig(cfg config.Config) { @@ -122,7 +122,7 @@ func (field *GeneratedField) SetGenerationFunction( functionHolder FunctionHolder, ) { if field.IsCustomType() { - field.customTypes[field.goType] = functionHolder + field.customTypes[field.typeHash] = functionHolder } else if field.Config.GenerationFunctions[field.Value.Kind()] != nil { field.Tag = reflect.StructTag("") // remove tags to ensure the field is generated with the new function field.Config.GenerationFunctions[field.Value.Kind()] = functionHolder @@ -167,7 +167,7 @@ func (field *GeneratedField) checkForRecursiveDefinition(fail bool) bool { func (field *GeneratedField) SetValue() bool { // check if the current field is a custom type with it's own generation function - if customType := field.customTypes[field.goType]; customType != nil { + if customType := field.customTypes[field.typeHash]; customType != nil { field.Value.Set(reflect.ValueOf(customType.GetFunction().Generate())) return false } @@ -205,7 +205,7 @@ func (field *GeneratedField) setStructValues() { Config: field.Config.Copy(field.Value.Field(j).Kind()), Parent: field, customTypes: field.customTypes, - goType: field.Value.Field(j).Type().Name(), + typeHash: field.Value.Field(j).Type().Name(), } structField.SetValue() } @@ -219,8 +219,8 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio } // check if field is a custom type with it's own generation function - if field.customTypes[field.goType] != nil { - return field.customTypes[field.goType].GetFunction() + if field.customTypes[field.typeHash] != nil { + return field.customTypes[field.typeHash].GetFunction() } kind := field.Value.Kind() diff --git a/generator/core/slice.go b/generator/core/slice.go index 3222f3b..03ba20b 100644 --- a/generator/core/slice.go +++ b/generator/core/slice.go @@ -4,6 +4,7 @@ import ( "fmt" "reflect" + "github.com/MartinSimango/dstruct/dreflect" "github.com/MartinSimango/dstruct/generator" "github.com/MartinSimango/dstruct/generator/config" ) @@ -40,7 +41,7 @@ func GenerateSliceFunc( Config: fieldConfig, Parent: field, customTypes: field.customTypes, - goType: elemValue.Type().Name(), + typeHash: dreflect.GetTypeHash(elemValue.Interface()), } newField.SetValue() diff --git a/struct.field.go b/struct.field.go index 59e9d71..03dee39 100644 --- a/struct.field.go +++ b/struct.field.go @@ -11,7 +11,7 @@ type StructField struct { tag reflect.StructTag value reflect.Value dstructType reflect.Type - goType string + typeHash string pkgPath string anonymous bool jsonName string @@ -45,9 +45,9 @@ func (f StructField) GetDstructType() reflect.Type { return f.dstructType } -// GetGoType returns the Go type of the field. -func (f StructField) GetGoType() string { - return f.goType +// GetTypeHash returns the hash of the type of the field. +func (f StructField) GetTypeHash() string { + return f.typeHash } // GetQualifiedName returns the fully qualified name of the field. diff --git a/tests/dstruct_test/tree.builder_test.go b/tests/dstruct_test/tree.builder_test.go index c1b60b6..17b929e 100644 --- a/tests/dstruct_test/tree.builder_test.go +++ b/tests/dstruct_test/tree.builder_test.go @@ -1,12 +1,14 @@ package dstruct_test import ( + "fmt" "reflect" "testing" + "github.com/stretchr/testify/assert" + "github.com/MartinSimango/dstruct" "github.com/MartinSimango/dstruct/dreflect" - "github.com/stretchr/testify/assert" ) type TestExtendData struct { @@ -17,7 +19,6 @@ type TestExtendData struct { } func TestExtend(t *testing.T) { - type Embedded struct { Name string } @@ -43,7 +44,7 @@ func TestExtend(t *testing.T) { Name string unexportedStruct } - var tests = []TestExtendData{ + tests := []TestExtendData{ {"ExtendInt", 2, true, nil}, {"ExtendString", "hello", true, nil}, {"ExtendReflectValue", reflect.ValueOf(2), true, reflect.ValueOf(2)}, @@ -53,7 +54,12 @@ func TestExtend(t *testing.T) { {"ExtendStruct", testStruct1{Age: 20}, false, testStruct1{Age: 20}}, {"ExtendPointerToStruct", &testStruct1{Age: 20}, false, &testStruct1{Age: 20}}, {"ExtendStructWithEmbeddedField", testStructEmbedded{}, false, testStructEmbedded{}}, - {"ExtendStructWithUnexportedEmbeddedField", testStructUnexportedEmbedded{}, true, testStructUnexportedEmbedded{}}, + { + "ExtendStructWithUnexportedEmbeddedField", + testStructUnexportedEmbedded{}, + true, + testStructUnexportedEmbedded{}, + }, } assert := assert.New(t) @@ -65,11 +71,11 @@ func TestExtend(t *testing.T) { }() dynamicStruct := dstruct.ExtendStruct(test.input).Build().Instance() - dynamicStructConverted := dreflect.Convert(reflect.ValueOf(dynamicStruct), reflect.TypeOf(test.expected)).Interface() + dynamicStructConverted := dreflect.Convert(reflect.ValueOf(dynamicStruct), reflect.TypeOf(test.expected)). + Interface() assert.EqualValues(test.expected, dynamicStructConverted) }) } - } type TestAddFieldData struct { @@ -88,7 +94,10 @@ func TestAddField(t *testing.T) { instance := b.Build().Instance() - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface()) + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface(), + ) } func TestAddEmbeddedField(t *testing.T) { @@ -108,8 +117,10 @@ func TestAddEmbeddedField(t *testing.T) { instance := b.Build().Instance() - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface()) - + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface(), + ) } func TestGetField(t *testing.T) { @@ -131,10 +142,19 @@ func TestGetField(t *testing.T) { instance := b.GetField("Person").AddField("Cool", 2, "").Build() - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(instance.Instance()), reflect.TypeOf(expected)).Interface()) - // Original builder must also be altered and have new field - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(b.GetField("Person").Build().Instance()), reflect.TypeOf(expected)).Interface()) + fmt.Println(b.Build().GetFields()) + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(instance.Instance()), reflect.TypeOf(expected)). + Interface(), + ) + // Original builder must also be altered and have new field + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(b.GetField("Person").Build().Instance()), reflect.TypeOf(expected)). + Interface(), + ) } // GetNewBuilderFromField returns a new builder instance where the subfield of the struct "field" is the root of the struct. @@ -163,10 +183,17 @@ func TestNewBuilderFromField(t *testing.T) { instance := b.NewBuilderFromField("Person").AddField("Cool", 2, "").Build() - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(instance.Instance()), reflect.TypeOf(expected)).Interface()) + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(instance.Instance()), reflect.TypeOf(expected)). + Interface(), + ) // Original builder must NOT be altered and have new field - assert.EqualValues(expectedOld, dreflect.Convert(reflect.ValueOf(b.GetField("Person").Build().Instance()), reflect.TypeOf(expectedOld)).Interface()) - + assert.EqualValues( + expectedOld, + dreflect.Convert(reflect.ValueOf(b.GetField("Person").Build().Instance()), reflect.TypeOf(expectedOld)). + Interface(), + ) } func TestRemoveField(t *testing.T) { @@ -189,5 +216,8 @@ func TestRemoveField(t *testing.T) { instance := b.Build().Instance() - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface()) + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface(), + ) } diff --git a/tree.builder.go b/tree.builder.go index 3188ec2..e5a7f86 100644 --- a/tree.builder.go +++ b/tree.builder.go @@ -82,25 +82,38 @@ func resetNodeFieldsFQN(node *Node[StructField]) *Node[StructField] { } func (dsb *treeBuilderImpl) AddField(name string, value interface{}, tag string) Builder { - if dsb.root.HasChild(name) { + dsb.addField(false, name, value, tag, dsb.root) + return dsb +} + +func (dsb *treeBuilderImpl) AddEmbeddedField(value interface{}, tag string) Builder { + ptrValue, _ := getPtrValue(reflect.ValueOf(value), 0) + name := reflect.TypeOf(ptrValue.Interface()).Name() + + dsb.addField(true, name, value, tag, dsb.root) + + return dsb +} + +func (dsb *treeBuilderImpl) addField( + anonymous bool, + name string, + value interface{}, + tag string, + root *Node[StructField], +) { + if root.HasChild(name) { panic(fmt.Sprintf("Field '%s' already exists", name)) } + dsb.root.data.dstructType = dsb.addFieldToTree( name, value, "", - false, + anonymous, reflect.StructTag(tag), dsb.root, ) - return dsb -} - -func (dsb *treeBuilderImpl) AddEmbeddedField(value interface{}, tag string) Builder { - ptrValue, _ := getPtrValue(reflect.ValueOf(value), 0) - name := reflect.TypeOf(ptrValue.Interface()).Name() - dsb.addFieldToTree(name, value, "", true, reflect.StructTag(tag), dsb.root) - return dsb } func (dsb *treeBuilderImpl) RemoveField(name string) Builder { @@ -133,9 +146,7 @@ func (dsb *treeBuilderImpl) GetField(field string) Builder { } else if node.data.dstructType.Kind() != reflect.Struct { panic(fmt.Sprintf("Cannot get field '%s' because it is not a struct or does not fully derefence to a struct value", field)) } - // if node.data.ptrKind != reflect.Struct { - // node. / return newBuilderFromNode(node, false) } panic(fmt.Sprintf("Field '%s' does not exist", field)) @@ -214,17 +225,12 @@ func (dsb *treeBuilderImpl) addFieldToTree( *root.data.numberOfSubFields++ } - var goType string - if goType = tag.Get("goType"); goType == "" { - goType = value.Type().Name() - } - field := &StructField{ name: name, value: value, // this will initally be unaddressable until the struct is built tag: tag, dstructType: value.Type(), - goType: goType, + typeHash: dreflect.GetTypeHash(typ), pkgPath: pkgPath, anonymous: anonymous, jsonName: strings.Split(tag.Get("json"), ",")[0], @@ -245,7 +251,9 @@ func (dsb *treeBuilderImpl) addFieldToTree( field.dstructType = dsb.addPtrField(value, root.children[name]) } - return field.dstructType + // return field.dstructType + // We need to rebuild the stryct to get it's new type as a result of adding a new field + return reflect.TypeOf(dsb.Build().Instance()) } func sortKeys(root *Node[StructField]) (keys []string) { @@ -365,14 +373,6 @@ func (dsb *treeBuilderImpl) addStructFields( fieldName := strct.Type().Field(i).Name fieldTag := strct.Type().Field(i).Tag - if fieldTag.Get("goType") == "" { - fieldTag = (reflect.StructTag)(strings.TrimSpace(fmt.Sprintf( - "%s goType:\"%s\"", - fieldTag, - strct.Field(i).Type(), - ))) - } - var fieldValue any if strct.Type().Field(i).IsExported() { fieldValue = strct.Field(i).Interface() @@ -399,6 +399,8 @@ func (dsb *treeBuilderImpl) addStructFields( } + // If the struct is a pointer, we need to create a pointer to the struct + retStruct := reflect.New(reflect.StructOf(structFields)).Elem() for i := 0; i < ptrDepth; i++ { retStruct = reflect.New(retStruct.Type())