-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodifier.go
More file actions
174 lines (139 loc) · 4.65 KB
/
modifier.go
File metadata and controls
174 lines (139 loc) · 4.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package dstruct
import (
"encoding/json"
"fmt"
"reflect"
"strings"
"github.com/MartinSimango/dstruct/dreflect"
)
type FieldNode map[string]*Node[StructField]
type FieldData map[string]StructField
type DynamicStructModifier interface {
// Instance returns a copy of the struct
Instance() any
// New returns a pointer to the struct
New() any
// 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`.
Set(field string, value any) error
// GetFields returns a map containing all fields within a struct
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
// 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.
Apply(field string, value any) error
}
type FieldModifier func(*StructField)
type DynamicStructModifierImpl struct {
strct any
fieldNodeMap FieldNode
fieldData map[string]StructField
root *Node[StructField]
}
var _ DynamicStructModifier = &DynamicStructModifierImpl{}
func newStruct(strct any, rootNode *Node[StructField]) *DynamicStructModifierImpl {
dsm := &DynamicStructModifierImpl{
strct: strct,
fieldNodeMap: make(FieldNode),
fieldData: make(map[string]StructField),
root: rootNode,
}
dsm.createFieldToNodeMappings(rootNode)
return dsm
}
func (dm *DynamicStructModifierImpl) createFieldToNodeMappings(rootNode *Node[StructField]) {
for _, field := range rootNode.children {
dm.fieldNodeMap[field.data.qualifiedName] = field
dm.fieldData[field.data.qualifiedName] = *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) {
if f := dm.get(field); f == nil {
return nil, fmt.Errorf("field %s does not exists in struct", field)
} else {
return f.data.value.Interface(), nil
}
}
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, ".")
for _, f := range fields {
c := f[0]
if 'a' <= c && c <= 'z' || c == '_' {
return false
}
}
return true
}
func (dm *DynamicStructModifierImpl) Set(field string, value any) error {
var f *Node[StructField]
if f = dm.get(field); f == nil {
return fmt.Errorf("field %s does not exists in struct", field)
}
if !isFieldExported(field) {
return fmt.Errorf("field %s is not exported", field)
}
fieldValue := f.data.value
if !canExtend(value) { // if value is not a struct
if value == nil {
if !fieldValue.IsZero() {
fieldValue.Set(reflect.Zero(fieldValue.Type()))
}
return nil
}
fieldValue.Set(dreflect.Convert(reflect.ValueOf(value), fieldValue.Type()))
return nil
}
fieldValue.Set(dreflect.Convert(reflect.ValueOf(value), fieldValue.Type()))
dm.update(f)
return nil
}
// update updates the struct's underlying node to represent that of the strct's value.
func (dm *DynamicStructModifierImpl) update(node *Node[StructField]) {
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
}
func (dm *DynamicStructModifierImpl) String() string {
val, _ := json.MarshalIndent(dm.strct, "", "\t")
return string(val)
}
func (dm *DynamicStructModifierImpl) Update() {
*dm = *ExtendStruct(dm.Instance()).Build().(*DynamicStructModifierImpl)
}
func (dm *DynamicStructModifierImpl) Apply(field string, value any) error {
if err := dm.Set(field, value); err != nil {
return err
}
dm.Update()
return nil
}