@@ -2,18 +2,26 @@ package patch
2
2
3
3
import (
4
4
"fmt"
5
+
6
+ "gopkg.in/yaml.v2"
5
7
)
6
8
7
9
type ReplaceOp struct {
8
10
Path Pointer
9
- Value interface {}
11
+ Value interface {} // will be cloned using yaml library
10
12
}
11
13
12
14
func (op ReplaceOp ) Apply (doc interface {}) (interface {}, error ) {
15
+ // Ensure that value is not modified by future operations
16
+ clonedValue , err := op .cloneValue (op .Value )
17
+ if err != nil {
18
+ return nil , fmt .Errorf ("ReplaceOp cloning value: %s" , err )
19
+ }
20
+
13
21
tokens := op .Path .Tokens ()
14
22
15
23
if len (tokens ) == 1 {
16
- return op . Value , nil
24
+ return clonedValue , nil
17
25
}
18
26
19
27
obj := doc
@@ -36,7 +44,7 @@ func (op ReplaceOp) Apply(doc interface{}) (interface{}, error) {
36
44
}
37
45
38
46
if isLast {
39
- typedObj [idx ] = op . Value
47
+ typedObj [idx ] = clonedValue
40
48
} else {
41
49
obj = typedObj [idx ]
42
50
prevUpdate = func (newObj interface {}) { typedObj [idx ] = newObj }
@@ -49,7 +57,7 @@ func (op ReplaceOp) Apply(doc interface{}) (interface{}, error) {
49
57
}
50
58
51
59
if isLast {
52
- prevUpdate (append (typedObj , op . Value ))
60
+ prevUpdate (append (typedObj , clonedValue ))
53
61
} else {
54
62
return nil , fmt .Errorf ("Expected after last index token to be last in path '%s'" , op .Path )
55
63
}
@@ -73,7 +81,7 @@ func (op ReplaceOp) Apply(doc interface{}) (interface{}, error) {
73
81
74
82
if typedToken .Optional && len (idxs ) == 0 {
75
83
if isLast {
76
- prevUpdate (append (typedObj , op . Value ))
84
+ prevUpdate (append (typedObj , clonedValue ))
77
85
} else {
78
86
obj = map [interface {}]interface {}{typedToken .Key : typedToken .Value }
79
87
prevUpdate (append (typedObj , obj ))
@@ -87,7 +95,7 @@ func (op ReplaceOp) Apply(doc interface{}) (interface{}, error) {
87
95
idx := idxs [0 ]
88
96
89
97
if isLast {
90
- typedObj [idx ] = op . Value
98
+ typedObj [idx ] = clonedValue
91
99
} else {
92
100
obj = typedObj [idx ]
93
101
// no need to change prevUpdate since matching item can only be a map
@@ -108,7 +116,7 @@ func (op ReplaceOp) Apply(doc interface{}) (interface{}, error) {
108
116
}
109
117
110
118
if isLast {
111
- typedObj [typedToken .Key ] = op . Value
119
+ typedObj [typedToken .Key ] = clonedValue
112
120
} else {
113
121
prevUpdate = func (newObj interface {}) { typedObj [typedToken .Key ] = newObj }
114
122
@@ -137,3 +145,23 @@ func (op ReplaceOp) Apply(doc interface{}) (interface{}, error) {
137
145
138
146
return doc , nil
139
147
}
148
+
149
+ func (ReplaceOp ) cloneValue (in interface {}) (out interface {}, err error ) {
150
+ defer func () {
151
+ if recoverVal := recover (); recoverVal != nil {
152
+ err = fmt .Errorf ("Recovered: %s" , recoverVal )
153
+ }
154
+ }()
155
+
156
+ bytes , err := yaml .Marshal (in )
157
+ if err != nil {
158
+ return nil , err
159
+ }
160
+
161
+ err = yaml .Unmarshal (bytes , & out )
162
+ if err != nil {
163
+ return nil , err
164
+ }
165
+
166
+ return out , nil
167
+ }
0 commit comments