-
Notifications
You must be signed in to change notification settings - Fork 85
/
patch_map.go
106 lines (88 loc) · 2.66 KB
/
patch_map.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
package diff
import (
"errors"
"reflect"
"github.com/vmihailenco/msgpack/v5"
)
// renderMap - handle map rendering for patch
func (d *Differ) renderMap(c *ChangeValue) (m, k, v *reflect.Value) {
//we must tease out the type of the key, we use the msgpack from diff to recreate the key
kt := c.target.Type().Key()
field := reflect.New(kt)
if d.StructMapKeys {
if err := msgpack.Unmarshal([]byte(c.change.Path[c.pos]), field.Interface()); err != nil {
c.SetFlag(FlagIgnored)
c.AddError(NewError("Unable to unmarshal path element to target type for key in map", err))
return
}
c.key = field.Elem()
} else {
c.key = reflect.ValueOf(c.change.Path[c.pos])
}
if c.target.IsNil() && c.target.IsValid() {
c.target.Set(reflect.MakeMap(c.target.Type()))
}
// we need to check that MapIndex does not panic here
// when the key type is not a string
defer func() {
if err := recover(); err != nil {
switch x := err.(type) {
case error:
c.AddError(NewError("Unable to unmarshal path element to target type for key in map", x))
case string:
c.AddError(NewError("Unable to unmarshal path element to target type for key in map", errors.New(x)))
}
c.SetFlag(FlagIgnored)
}
}()
x := c.target.MapIndex(c.key)
if !x.IsValid() && c.change.Type != DELETE && !c.HasFlag(OptionNoCreate) {
x = c.NewElement()
}
if x.IsValid() { //Map elements come out as read only so we must convert
nv := reflect.New(x.Type()).Elem()
nv.Set(x)
x = nv
}
if x.IsValid() && !reflect.DeepEqual(c.change.From, x.Interface()) &&
c.HasFlag(OptionOmitUnequal) {
c.SetFlag(FlagIgnored)
c.AddError(NewError("target change doesn't match original"))
return
}
mp := *c.target //these may change out from underneath us as we recurse
key := c.key //so we make copies and pass back pointers to them
c.swap(&x)
return &mp, &key, &x
}
// updateMapEntry - deletes are special, they are handled differently based on options
//
// container type etc. We have to have special handling for each
// type. Set values are more generic even if they must be instanced
func (d *Differ) updateMapEntry(c *ChangeValue, m, k, v *reflect.Value) {
if k == nil || m == nil {
return
}
switch c.change.Type {
case DELETE:
if c.HasFlag(FlagDeleted) {
return
}
if !m.CanSet() && v.IsValid() && v.Kind() == reflect.Struct {
for x := 0; x < v.NumField(); x++ {
if !v.Field(x).IsZero() {
m.SetMapIndex(*k, *v)
return
}
} //if all the fields are zero, remove from map
}
m.SetMapIndex(*k, reflect.Value{})
c.SetFlag(FlagDeleted)
case CREATE:
m.SetMapIndex(*k, *v)
c.SetFlag(FlagCreated)
case UPDATE:
m.SetMapIndex(*k, *v)
c.SetFlag(FlagUpdated)
}
}