@@ -2,18 +2,26 @@ package patch
22
33import (
44 "fmt"
5+
6+ "gopkg.in/yaml.v2"
57)
68
79type ReplaceOp struct {
810 Path Pointer
9- Value interface {}
11+ Value interface {} // will be cloned using yaml library
1012}
1113
1214func (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+
1321 tokens := op .Path .Tokens ()
1422
1523 if len (tokens ) == 1 {
16- return op . Value , nil
24+ return clonedValue , nil
1725 }
1826
1927 obj := doc
@@ -36,7 +44,7 @@ func (op ReplaceOp) Apply(doc interface{}) (interface{}, error) {
3644 }
3745
3846 if isLast {
39- typedObj [idx ] = op . Value
47+ typedObj [idx ] = clonedValue
4048 } else {
4149 obj = typedObj [idx ]
4250 prevUpdate = func (newObj interface {}) { typedObj [idx ] = newObj }
@@ -49,7 +57,7 @@ func (op ReplaceOp) Apply(doc interface{}) (interface{}, error) {
4957 }
5058
5159 if isLast {
52- prevUpdate (append (typedObj , op . Value ))
60+ prevUpdate (append (typedObj , clonedValue ))
5361 } else {
5462 return nil , fmt .Errorf ("Expected after last index token to be last in path '%s'" , op .Path )
5563 }
@@ -73,7 +81,7 @@ func (op ReplaceOp) Apply(doc interface{}) (interface{}, error) {
7381
7482 if typedToken .Optional && len (idxs ) == 0 {
7583 if isLast {
76- prevUpdate (append (typedObj , op . Value ))
84+ prevUpdate (append (typedObj , clonedValue ))
7785 } else {
7886 obj = map [interface {}]interface {}{typedToken .Key : typedToken .Value }
7987 prevUpdate (append (typedObj , obj ))
@@ -87,7 +95,7 @@ func (op ReplaceOp) Apply(doc interface{}) (interface{}, error) {
8795 idx := idxs [0 ]
8896
8997 if isLast {
90- typedObj [idx ] = op . Value
98+ typedObj [idx ] = clonedValue
9199 } else {
92100 obj = typedObj [idx ]
93101 // 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) {
108116 }
109117
110118 if isLast {
111- typedObj [typedToken .Key ] = op . Value
119+ typedObj [typedToken .Key ] = clonedValue
112120 } else {
113121 prevUpdate = func (newObj interface {}) { typedObj [typedToken .Key ] = newObj }
114122
@@ -137,3 +145,23 @@ func (op ReplaceOp) Apply(doc interface{}) (interface{}, error) {
137145
138146 return doc , nil
139147}
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