Skip to content

Commit 638168c

Browse files
author
Martin Simango
committed
Allow for easy creating of custom tasks to be created allowing fields to have custom functions as generators
1 parent 58c2a92 commit 638168c

10 files changed

Lines changed: 114 additions & 59 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ func main() {
151151
AddField("Person", Person{Name: "Martin", Age: 25}, `json:"person"`).
152152
AddField("Job", "Software Developer", "")
153153

154+
155+
154156
strct := structBuilder.Build().Instance()
155157

156158
generatedStruct := dstruct.NewGeneratedStruct(strct)

dreflect/dreflect.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func convert(src reflect.Value, dst reflect.Value) reflect.Value {
112112
}
113113

114114
retPointer := reflect.New(dst.Type())
115-
//enure that new pointer uses same memory address as src pointer
115+
//ensure that new pointer uses same memory address as src pointer
116116
reflect.NewAt(src.Type(), unsafe.Pointer(retPointer.Elem().UnsafeAddr())).Elem().
117117
Set(src)
118118

field.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package dstruct
22

3-
import "reflect"
3+
import (
4+
"fmt"
5+
"reflect"
6+
"strconv"
7+
)
48

59
type structField struct {
610
name string
@@ -35,3 +39,24 @@ func (f structField) GetFieldFQName() string {
3539
func (f structField) GetTag(t string) string {
3640
return f.tag.Get(t)
3741
}
42+
43+
func (f structField) GetJsonName() string {
44+
return f.jsonName
45+
}
46+
47+
func (f structField) GetEnumValues() (enumValues map[string]int) {
48+
enum, ok := f.tag.Lookup("enum")
49+
if ok {
50+
numEnums, err := strconv.Atoi(enum)
51+
if err != nil {
52+
return
53+
}
54+
enumValues = make(map[string]int)
55+
for i := 1; i <= numEnums; i++ {
56+
if key := f.tag.Get(fmt.Sprintf("enum_%d", i)); key != "" {
57+
enumValues[key] = i
58+
}
59+
}
60+
}
61+
return
62+
}

generator/basic.functions.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import (
1111

1212
type basicGenerationFunction struct {
1313
_func func(...any) any
14-
args []any
14+
// TODO consider making args exportable for more customizable generation functions
15+
args []any
1516
}
1617

1718
const ISO8601 string = "2018-03-20T09:12:28Z"

generator/functions.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ func GenerateNumberFunc[n number](min, max n) GenerationFunction {
5555
}
5656

5757
f.args = []any{&min, &max}
58+
5859
return f
5960
}
6061

generator/generated.field.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import (
66
"strconv"
77
)
88

9-
type ParentType map[string]int
10-
119
type GeneratedField struct {
1210
Name string
1311
Value reflect.Value
@@ -155,9 +153,17 @@ func (field *GeneratedField) getGenerationFunction() GenerationFunction {
155153
return GenerateFixedValueFunc(tags.Get(fmt.Sprintf("enum_%d", generateNum(0, numEnums-1)+1)))
156154
}
157155

158-
gen_task, ok := tags.Lookup("gen_task")
156+
_, ok = tags.Lookup("gen_task")
159157
if ok {
160-
return getTask(gen_task, field.Name).getFunction()
158+
taskProperties, err := CreateTaskProperties(field.Name, tags)
159+
if err != nil {
160+
panic(fmt.Sprintf("Error decoding gen_task: %s", err.Error()))
161+
}
162+
task := GetTask(taskProperties.TaskName)
163+
if task == nil {
164+
panic(fmt.Sprintf("Unregistered task %s", taskProperties.TaskName))
165+
}
166+
return task.GenerationFunction(*taskProperties)
161167
}
162168
return field.Generator.DefaultGenerationFunctions[kind]
163169
}

generator/generator.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66

77
type GenerationFunction interface {
88
Generate() any
9+
// Copy copies the generation config and returns a copy of it with the same config
10+
// as the origin generation config
911
Copy(*GenerationConfig) GenerationFunction
1012
GetGenerationConfig() *GenerationConfig
1113
SetGenerationConfig(*GenerationConfig) GenerationFunction

generator/task.go

Lines changed: 59 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,83 +2,92 @@ package generator
22

33
import (
44
"fmt"
5+
"reflect"
56
"strconv"
67
"strings"
78
)
89

910
type TaskName string
1011

11-
const (
12-
GenInt32 TaskName = "GenInt32"
13-
)
12+
type Task interface {
13+
GenerationFunction(taskProperties TaskProperties) GenerationFunction
14+
ExpectedParameterCount() int
15+
Name() string
16+
}
17+
18+
var tasks map[string]Task
1419

15-
type Task struct {
16-
Name TaskName
17-
Parameters string
20+
func init() {
21+
tasks = make(map[string]Task)
22+
}
23+
24+
type TaskProperties struct {
25+
TaskName string
26+
Parameters []string
1827
FieldName string
1928
}
2029

21-
type GenInt32Params struct {
22-
min int32
23-
max int32
30+
func GetTask(task string) Task {
31+
return tasks[task]
2432
}
2533

26-
func (t *Task) GenInt32Params() GenInt32Params {
27-
params := strings.Split(t.Parameters, ",")
34+
func AddTask(task Task) error {
35+
name := task.Name()
36+
if tasks[name] != nil {
37+
return fmt.Errorf("Task with name %s already exists", name)
38+
}
39+
tasks[name] = task
40+
return nil
41+
}
2842

29-
if len(params) != 2 {
30-
panic(fmt.Sprintf("error with field %s: task %s: task requires 2 parameters but has %d", t.FieldName, t.Name, len(params)))
43+
func getParameters(parameterCount int, tags reflect.StructTag) (parameters []string) {
44+
for i := 1; i <= parameterCount; i++ {
45+
parameters = append(parameters, tags.Get(fmt.Sprintf("gen_task_%d", i)))
3146
}
32-
param_1, err := strconv.Atoi(params[0])
33-
if err != nil {
34-
panic(fmt.Sprintf("error with field %s: task %s error: %s", t.FieldName, t.Name, err))
47+
return parameters
48+
}
49+
50+
func CreateTaskProperties(fieldName string, tags reflect.StructTag) (*TaskProperties, error) {
51+
gen_task_tag := strings.TrimSpace(tags.Get("gen_task"))
52+
leftBraceIndex := strings.Index(gen_task_tag, "(")
53+
if leftBraceIndex == -1 {
54+
return nil, fmt.Errorf("error with field %s: task %s error: no ( found", fieldName, gen_task_tag)
3555
}
3656

37-
param_2, err := strconv.Atoi(params[1])
57+
if gen_task_tag[len(gen_task_tag)-1:] != ")" {
58+
return nil, fmt.Errorf("error with field %s: task %s error: last character of task must be )", fieldName, gen_task_tag)
59+
}
60+
taskName := gen_task_tag[:leftBraceIndex]
61+
parameterCount, err := strconv.Atoi(gen_task_tag[leftBraceIndex+1 : len(gen_task_tag)-1])
3862
if err != nil {
39-
panic(fmt.Sprintf("error with field %s: task %s error: %s", t.FieldName, t.Name, err))
63+
return nil, fmt.Errorf("error getting task parameter count: %w", err)
4064
}
4165

42-
if param_1 > param_2 {
43-
err = fmt.Errorf("min must be less or equal to the max value min = %d max = %d", param_1, param_2)
44-
panic(fmt.Sprintf("error with field %s: task %s error: %s", t.FieldName, t.Name, err))
66+
return &TaskProperties{
67+
TaskName: taskName,
68+
Parameters: getParameters(parameterCount, tags),
69+
FieldName: fieldName,
70+
}, nil
71+
}
4572

46-
}
73+
func GetTagForTask(name TaskName, params ...any) reflect.StructTag {
4774

48-
return GenInt32Params{
49-
min: int32(param_1),
50-
max: int32(param_2),
75+
if tasks[string(name)] == nil {
76+
panic(fmt.Sprintf("Task '%s' is not registered", name))
5177
}
52-
}
5378

54-
func getTask(task string, fieldName string) Task {
55-
task = strings.TrimSpace(task)
56-
leftBraceIndex := strings.Index(task, "(")
57-
if leftBraceIndex == -1 {
58-
panic(fmt.Sprintf("error with field %s: task %s error: no ( found", fieldName, task))
79+
tags := fmt.Sprintf(`gen_task:"%s(%d)"`, name, len(params))
80+
for i, p := range params {
81+
tags += fmt.Sprintf(` gen_task_%d:"%v"`, (i + 1), p)
5982
}
6083

61-
if task[len(task)-1:] != ")" {
62-
panic(fmt.Sprintf("error with field %s: task %s error: last character of task must be )", fieldName, task))
63-
}
64-
taskName := task[:leftBraceIndex]
65-
parameters := task[leftBraceIndex+1 : len(task)-1]
66-
return Task{
67-
Name: TaskName(taskName),
68-
Parameters: parameters,
69-
FieldName: fieldName,
70-
}
84+
return reflect.StructTag(tags)
85+
7186
}
7287

73-
func (t Task) getFunction() GenerationFunction {
74-
switch t.Name {
75-
case GenInt32:
76-
params := t.GenInt32Params()
77-
return GenerateNumberFunc(params.min, params.max)
88+
func validateParamCount(task Task, taskProperties TaskProperties) {
89+
if len(taskProperties.Parameters) != task.ExpectedParameterCount() {
90+
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)))
7891
}
79-
panic(fmt.Sprintf("Invalid task name '%s' for field %s ", t.Name, t.FieldName))
80-
}
8192

82-
func GetTagsForGenInt32Task(min int32, max int32) string {
83-
return fmt.Sprintf(`gen_task:"%s(%d,%d)"`, GenInt32, min, max)
8493
}

go.mod

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
module github.com/MartinSimango/dstruct
22

3-
go 1.20
3+
go 1.21.3
4+
5+
6+
retract v1.1.1
7+
retract v1.1.0
8+
retract v1.0.0
9+
retract v0.1.2
10+
retract v0.1.1
411

512
require (
613
github.com/stretchr/testify v1.8.1

modifier.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ type DynamicStructModifier interface {
3131
// GetFields returns a map containing all fields within a struct
3232
GetFields() FieldData
3333

34-
// Update updates the struct's underlying tree to represent that of the strct's value
34+
// Update updates the struct's underlying tree to represent that of the strct's value.
35+
// The structs underlying tree can change if new fields are added due to fields within the struct changing from
36+
// nil to become not nil. This can lead to new additional fields being introduced within the struct
3537
Update()
3638

3739
// Apply is a combination of Set and Update. Update is not called if Apply fails.

0 commit comments

Comments
 (0)