forked from r3labs/diff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
change_value.go
209 lines (185 loc) · 4.9 KB
/
change_value.go
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package diff
import (
"fmt"
"reflect"
)
//ChangeValue is a specialized struct for monitoring patching
type ChangeValue struct {
parent *reflect.Value
target *reflect.Value
flags PatchFlags
change *Change
err error
pos int
index int
key reflect.Value
}
//swap swaps out the target as we move down the path. Note that a nil
// check is foregone here due to the fact we control usage.
func (c *ChangeValue) swap(newTarget *reflect.Value) {
if newTarget.IsValid() {
c.ClearFlag(FlagInvalidTarget)
c.parent = c.target
c.target = newTarget
c.pos++
}
}
// Sets a flag on the node and saves the change
func (c *ChangeValue) SetFlag(flag PatchFlags) {
if c != nil {
c.flags = c.flags | flag
}
}
//ClearFlag removes just a single flag
func (c *ChangeValue) ClearFlag(flag PatchFlags) {
if c != nil {
c.flags = c.flags &^ flag
}
}
//HasFlag indicates if a flag is set on the node. returns false if node is bad
func (c *ChangeValue) HasFlag(flag PatchFlags) bool {
return (c.flags & flag) != 0
}
//IsValid echo for is valid
func (c *ChangeValue) IsValid() bool {
if c != nil {
return c.target.IsValid() || !c.HasFlag(FlagInvalidTarget)
}
return false
}
//ParentKind - helps keep us nil safe
func (c ChangeValue) ParentKind() reflect.Kind {
if c.parent != nil {
return c.parent.Kind()
}
return reflect.Invalid
}
//ParentLen is a nil safe parent length check
func (c ChangeValue) ParentLen() (ret int) {
if c.parent != nil &&
(c.parent.Kind() == reflect.Slice ||
c.parent.Kind() == reflect.Map) {
ret = c.parent.Len()
}
return
}
//ParentSet - nil safe parent set
func (c *ChangeValue) ParentSet(value reflect.Value, convertCompatibleTypes bool) {
if c != nil && c.parent != nil {
defer func() {
if r := recover(); r != nil {
c.SetFlag(FlagParentSetFailed)
}
}()
if convertCompatibleTypes {
if !value.Type().ConvertibleTo(c.parent.Type()) {
c.AddError(fmt.Errorf("Value of type %s is not convertible to %s", value.Type().String(), c.parent.Type().String()))
c.SetFlag(FlagParentSetFailed)
return
}
c.parent.Set(value.Convert(c.parent.Type()))
} else {
c.parent.Set(value)
}
c.SetFlag(FlagParentSetApplied)
}
}
//Len echo for len
func (c ChangeValue) Len() int {
return c.target.Len()
}
//Set echos reflect set
func (c *ChangeValue) Set(value reflect.Value, convertCompatibleTypes bool) {
if c == nil {
return
}
defer func() {
if r := recover(); r != nil {
switch e := r.(type) {
case string:
c.AddError(NewError(e))
case *reflect.ValueError:
c.AddError(NewError(e.Error()))
}
c.SetFlag(FlagFailed)
}
}()
if c.HasFlag(OptionImmutable) {
c.SetFlag(FlagIgnored)
return
}
if convertCompatibleTypes {
if c.target.Kind() == reflect.Ptr && value.Kind() != reflect.Ptr {
if !value.IsValid() {
c.target.Set(reflect.Zero(c.target.Type()))
c.SetFlag(FlagApplied)
return
} else if !value.Type().ConvertibleTo(c.target.Elem().Type()) {
c.AddError(fmt.Errorf("Value of type %s is not convertible to %s", value.Type().String(), c.target.Type().String()))
c.SetFlag(FlagFailed)
return
}
tv := reflect.New(c.target.Elem().Type())
tv.Elem().Set(value.Convert(c.target.Elem().Type()))
c.target.Set(tv)
} else {
if !value.Type().ConvertibleTo(c.target.Type()) {
c.AddError(fmt.Errorf("Value of type %s is not convertible to %s", value.Type().String(), c.target.Type().String()))
c.SetFlag(FlagFailed)
return
}
c.target.Set(value.Convert(c.target.Type()))
}
} else {
if value.IsValid() {
if c.target.Kind() == reflect.Ptr && value.Kind() != reflect.Ptr {
tv := reflect.New(value.Type())
tv.Elem().Set(value)
c.target.Set(tv)
} else {
c.target.Set(value)
}
} else if c.target.Kind() == reflect.Ptr {
c.target.Set(reflect.Zero(c.target.Type()))
} else if !c.target.IsZero() {
t := c.target.Elem()
t.Set(reflect.Zero(t.Type()))
}
}
c.SetFlag(FlagApplied)
}
//Index echo for index
func (c ChangeValue) Index(i int) reflect.Value {
return c.target.Index(i)
}
//ParentIndex - get us the parent version, nil safe
func (c ChangeValue) ParentIndex(i int) (ret reflect.Value) {
if c.parent != nil {
ret = c.parent.Index(i)
}
return
}
//Instance a new element of type for target. Taking the
//copy of the complex origin avoids the 'lack of data' issue
//present when allocating complex structs with slices and
//arrays
func (c ChangeValue) NewElement() reflect.Value {
ret := c.change.Parent
if ret != nil {
return reflect.ValueOf(ret)
}
return reflect.New(c.target.Type().Elem()).Elem()
}
//NewArrayElement gives us a dynamically typed new element
func (c ChangeValue) NewArrayElement() reflect.Value {
c.target.Set(reflect.Append(*c.target, c.NewElement()))
c.SetFlag(FlagCreated)
return c.Index(c.Len() - 1)
}
//AddError appends errors to this change value
func (c *ChangeValue) AddError(err error) *ChangeValue {
if c != nil {
c.err = err
}
return c
}