1 package jsonpatch
2
3 import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "strconv"
8 "strings"
9
10 "github.com/pkg/errors"
11 )
12
13 const (
14 eRaw = iota
15 eDoc
16 eAry
17 )
18
19 var (
20
21
22
23 SupportNegativeIndices bool = true
24
25
26 AccumulatedCopySizeLimit int64 = 0
27 )
28
29 var (
30 ErrTestFailed = errors.New("test failed")
31 ErrMissing = errors.New("missing value")
32 ErrUnknownType = errors.New("unknown object type")
33 ErrInvalid = errors.New("invalid state detected")
34 ErrInvalidIndex = errors.New("invalid index referenced")
35 )
36
37 type lazyNode struct {
38 raw *json.RawMessage
39 doc partialDoc
40 ary partialArray
41 which int
42 }
43
44
45 type Operation map[string]*json.RawMessage
46
47
48 type Patch []Operation
49
50 type partialDoc map[string]*lazyNode
51 type partialArray []*lazyNode
52
53 type container interface {
54 get(key string) (*lazyNode, error)
55 set(key string, val *lazyNode) error
56 add(key string, val *lazyNode) error
57 remove(key string) error
58 }
59
60 func newLazyNode(raw *json.RawMessage) *lazyNode {
61 return &lazyNode{raw: raw, doc: nil, ary: nil, which: eRaw}
62 }
63
64 func (n *lazyNode) MarshalJSON() ([]byte, error) {
65 switch n.which {
66 case eRaw:
67 return json.Marshal(n.raw)
68 case eDoc:
69 return json.Marshal(n.doc)
70 case eAry:
71 return json.Marshal(n.ary)
72 default:
73 return nil, ErrUnknownType
74 }
75 }
76
77 func (n *lazyNode) UnmarshalJSON(data []byte) error {
78 dest := make(json.RawMessage, len(data))
79 copy(dest, data)
80 n.raw = &dest
81 n.which = eRaw
82 return nil
83 }
84
85 func deepCopy(src *lazyNode) (*lazyNode, int, error) {
86 if src == nil {
87 return nil, 0, nil
88 }
89 a, err := src.MarshalJSON()
90 if err != nil {
91 return nil, 0, err
92 }
93 sz := len(a)
94 ra := make(json.RawMessage, sz)
95 copy(ra, a)
96 return newLazyNode(&ra), sz, nil
97 }
98
99 func (n *lazyNode) intoDoc() (*partialDoc, error) {
100 if n.which == eDoc {
101 return &n.doc, nil
102 }
103
104 if n.raw == nil {
105 return nil, ErrInvalid
106 }
107
108 err := json.Unmarshal(*n.raw, &n.doc)
109
110 if err != nil {
111 return nil, err
112 }
113
114 n.which = eDoc
115 return &n.doc, nil
116 }
117
118 func (n *lazyNode) intoAry() (*partialArray, error) {
119 if n.which == eAry {
120 return &n.ary, nil
121 }
122
123 if n.raw == nil {
124 return nil, ErrInvalid
125 }
126
127 err := json.Unmarshal(*n.raw, &n.ary)
128
129 if err != nil {
130 return nil, err
131 }
132
133 n.which = eAry
134 return &n.ary, nil
135 }
136
137 func (n *lazyNode) compact() []byte {
138 buf := &bytes.Buffer{}
139
140 if n.raw == nil {
141 return nil
142 }
143
144 err := json.Compact(buf, *n.raw)
145
146 if err != nil {
147 return *n.raw
148 }
149
150 return buf.Bytes()
151 }
152
153 func (n *lazyNode) tryDoc() bool {
154 if n.raw == nil {
155 return false
156 }
157
158 err := json.Unmarshal(*n.raw, &n.doc)
159
160 if err != nil {
161 return false
162 }
163
164 n.which = eDoc
165 return true
166 }
167
168 func (n *lazyNode) tryAry() bool {
169 if n.raw == nil {
170 return false
171 }
172
173 err := json.Unmarshal(*n.raw, &n.ary)
174
175 if err != nil {
176 return false
177 }
178
179 n.which = eAry
180 return true
181 }
182
183 func (n *lazyNode) equal(o *lazyNode) bool {
184 if n.which == eRaw {
185 if !n.tryDoc() && !n.tryAry() {
186 if o.which != eRaw {
187 return false
188 }
189
190 return bytes.Equal(n.compact(), o.compact())
191 }
192 }
193
194 if n.which == eDoc {
195 if o.which == eRaw {
196 if !o.tryDoc() {
197 return false
198 }
199 }
200
201 if o.which != eDoc {
202 return false
203 }
204
205 if len(n.doc) != len(o.doc) {
206 return false
207 }
208
209 for k, v := range n.doc {
210 ov, ok := o.doc[k]
211
212 if !ok {
213 return false
214 }
215
216 if (v == nil) != (ov == nil) {
217 return false
218 }
219
220 if v == nil && ov == nil {
221 continue
222 }
223
224 if !v.equal(ov) {
225 return false
226 }
227 }
228
229 return true
230 }
231
232 if o.which != eAry && !o.tryAry() {
233 return false
234 }
235
236 if len(n.ary) != len(o.ary) {
237 return false
238 }
239
240 for idx, val := range n.ary {
241 if !val.equal(o.ary[idx]) {
242 return false
243 }
244 }
245
246 return true
247 }
248
249
250 func (o Operation) Kind() string {
251 if obj, ok := o["op"]; ok && obj != nil {
252 var op string
253
254 err := json.Unmarshal(*obj, &op)
255
256 if err != nil {
257 return "unknown"
258 }
259
260 return op
261 }
262
263 return "unknown"
264 }
265
266
267 func (o Operation) Path() (string, error) {
268 if obj, ok := o["path"]; ok && obj != nil {
269 var op string
270
271 err := json.Unmarshal(*obj, &op)
272
273 if err != nil {
274 return "unknown", err
275 }
276
277 return op, nil
278 }
279
280 return "unknown", errors.Wrapf(ErrMissing, "operation missing path field")
281 }
282
283
284 func (o Operation) From() (string, error) {
285 if obj, ok := o["from"]; ok && obj != nil {
286 var op string
287
288 err := json.Unmarshal(*obj, &op)
289
290 if err != nil {
291 return "unknown", err
292 }
293
294 return op, nil
295 }
296
297 return "unknown", errors.Wrapf(ErrMissing, "operation, missing from field")
298 }
299
300 func (o Operation) value() *lazyNode {
301 if obj, ok := o["value"]; ok {
302 return newLazyNode(obj)
303 }
304
305 return nil
306 }
307
308
309 func (o Operation) ValueInterface() (interface{}, error) {
310 if obj, ok := o["value"]; ok && obj != nil {
311 var v interface{}
312
313 err := json.Unmarshal(*obj, &v)
314
315 if err != nil {
316 return nil, err
317 }
318
319 return v, nil
320 }
321
322 return nil, errors.Wrapf(ErrMissing, "operation, missing value field")
323 }
324
325 func isArray(buf []byte) bool {
326 Loop:
327 for _, c := range buf {
328 switch c {
329 case ' ':
330 case '\n':
331 case '\t':
332 continue
333 case '[':
334 return true
335 default:
336 break Loop
337 }
338 }
339
340 return false
341 }
342
343 func findObject(pd *container, path string) (container, string) {
344 doc := *pd
345
346 split := strings.Split(path, "/")
347
348 if len(split) < 2 {
349 return nil, ""
350 }
351
352 parts := split[1 : len(split)-1]
353
354 key := split[len(split)-1]
355
356 var err error
357
358 for _, part := range parts {
359
360 next, ok := doc.get(decodePatchKey(part))
361
362 if next == nil || ok != nil {
363 return nil, ""
364 }
365
366 if isArray(*next.raw) {
367 doc, err = next.intoAry()
368
369 if err != nil {
370 return nil, ""
371 }
372 } else {
373 doc, err = next.intoDoc()
374
375 if err != nil {
376 return nil, ""
377 }
378 }
379 }
380
381 return doc, decodePatchKey(key)
382 }
383
384 func (d *partialDoc) set(key string, val *lazyNode) error {
385 (*d)[key] = val
386 return nil
387 }
388
389 func (d *partialDoc) add(key string, val *lazyNode) error {
390 (*d)[key] = val
391 return nil
392 }
393
394 func (d *partialDoc) get(key string) (*lazyNode, error) {
395 return (*d)[key], nil
396 }
397
398 func (d *partialDoc) remove(key string) error {
399 _, ok := (*d)[key]
400 if !ok {
401 return errors.Wrapf(ErrMissing, "Unable to remove nonexistent key: %s", key)
402 }
403
404 delete(*d, key)
405 return nil
406 }
407
408
409
410 func (d *partialArray) set(key string, val *lazyNode) error {
411 idx, err := strconv.Atoi(key)
412 if err != nil {
413 return err
414 }
415
416 if idx < 0 {
417 if !SupportNegativeIndices {
418 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
419 }
420 if idx < -len(*d) {
421 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
422 }
423 idx += len(*d)
424 }
425
426 (*d)[idx] = val
427 return nil
428 }
429
430 func (d *partialArray) add(key string, val *lazyNode) error {
431 if key == "-" {
432 *d = append(*d, val)
433 return nil
434 }
435
436 idx, err := strconv.Atoi(key)
437 if err != nil {
438 return errors.Wrapf(err, "value was not a proper array index: '%s'", key)
439 }
440
441 sz := len(*d) + 1
442
443 ary := make([]*lazyNode, sz)
444
445 cur := *d
446
447 if idx >= len(ary) {
448 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
449 }
450
451 if idx < 0 {
452 if !SupportNegativeIndices {
453 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
454 }
455 if idx < -len(ary) {
456 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
457 }
458 idx += len(ary)
459 }
460
461 copy(ary[0:idx], cur[0:idx])
462 ary[idx] = val
463 copy(ary[idx+1:], cur[idx:])
464
465 *d = ary
466 return nil
467 }
468
469 func (d *partialArray) get(key string) (*lazyNode, error) {
470 idx, err := strconv.Atoi(key)
471
472 if err != nil {
473 return nil, err
474 }
475
476 if idx < 0 {
477 if !SupportNegativeIndices {
478 return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
479 }
480 if idx < -len(*d) {
481 return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
482 }
483 idx += len(*d)
484 }
485
486 if idx >= len(*d) {
487 return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
488 }
489
490 return (*d)[idx], nil
491 }
492
493 func (d *partialArray) remove(key string) error {
494 idx, err := strconv.Atoi(key)
495 if err != nil {
496 return err
497 }
498
499 cur := *d
500
501 if idx >= len(cur) {
502 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
503 }
504
505 if idx < 0 {
506 if !SupportNegativeIndices {
507 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
508 }
509 if idx < -len(cur) {
510 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
511 }
512 idx += len(cur)
513 }
514
515 ary := make([]*lazyNode, len(cur)-1)
516
517 copy(ary[0:idx], cur[0:idx])
518 copy(ary[idx:], cur[idx+1:])
519
520 *d = ary
521 return nil
522
523 }
524
525 func (p Patch) add(doc *container, op Operation) error {
526 path, err := op.Path()
527 if err != nil {
528 return errors.Wrapf(ErrMissing, "add operation failed to decode path")
529 }
530
531 con, key := findObject(doc, path)
532
533 if con == nil {
534 return errors.Wrapf(ErrMissing, "add operation does not apply: doc is missing path: \"%s\"", path)
535 }
536
537 err = con.add(key, op.value())
538 if err != nil {
539 return errors.Wrapf(err, "error in add for path: '%s'", path)
540 }
541
542 return nil
543 }
544
545 func (p Patch) remove(doc *container, op Operation) error {
546 path, err := op.Path()
547 if err != nil {
548 return errors.Wrapf(ErrMissing, "remove operation failed to decode path")
549 }
550
551 con, key := findObject(doc, path)
552
553 if con == nil {
554 return errors.Wrapf(ErrMissing, "remove operation does not apply: doc is missing path: \"%s\"", path)
555 }
556
557 err = con.remove(key)
558 if err != nil {
559 return errors.Wrapf(err, "error in remove for path: '%s'", path)
560 }
561
562 return nil
563 }
564
565 func (p Patch) replace(doc *container, op Operation) error {
566 path, err := op.Path()
567 if err != nil {
568 return errors.Wrapf(err, "replace operation failed to decode path")
569 }
570
571 if path == "" {
572 val := op.value()
573
574 if val.which == eRaw {
575 if !val.tryDoc() {
576 if !val.tryAry() {
577 return errors.Wrapf(err, "replace operation value must be object or array")
578 }
579 }
580 }
581
582 switch val.which {
583 case eAry:
584 *doc = &val.ary
585 case eDoc:
586 *doc = &val.doc
587 case eRaw:
588 return errors.Wrapf(err, "replace operation hit impossible case")
589 }
590
591 return nil
592 }
593
594 con, key := findObject(doc, path)
595
596 if con == nil {
597 return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing path: %s", path)
598 }
599
600 _, ok := con.get(key)
601 if ok != nil {
602 return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing key: %s", path)
603 }
604
605 err = con.set(key, op.value())
606 if err != nil {
607 return errors.Wrapf(err, "error in remove for path: '%s'", path)
608 }
609
610 return nil
611 }
612
613 func (p Patch) move(doc *container, op Operation) error {
614 from, err := op.From()
615 if err != nil {
616 return errors.Wrapf(err, "move operation failed to decode from")
617 }
618
619 con, key := findObject(doc, from)
620
621 if con == nil {
622 return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing from path: %s", from)
623 }
624
625 val, err := con.get(key)
626 if err != nil {
627 return errors.Wrapf(err, "error in move for path: '%s'", key)
628 }
629
630 err = con.remove(key)
631 if err != nil {
632 return errors.Wrapf(err, "error in move for path: '%s'", key)
633 }
634
635 path, err := op.Path()
636 if err != nil {
637 return errors.Wrapf(err, "move operation failed to decode path")
638 }
639
640 con, key = findObject(doc, path)
641
642 if con == nil {
643 return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing destination path: %s", path)
644 }
645
646 err = con.add(key, val)
647 if err != nil {
648 return errors.Wrapf(err, "error in move for path: '%s'", path)
649 }
650
651 return nil
652 }
653
654 func (p Patch) test(doc *container, op Operation) error {
655 path, err := op.Path()
656 if err != nil {
657 return errors.Wrapf(err, "test operation failed to decode path")
658 }
659
660 if path == "" {
661 var self lazyNode
662
663 switch sv := (*doc).(type) {
664 case *partialDoc:
665 self.doc = *sv
666 self.which = eDoc
667 case *partialArray:
668 self.ary = *sv
669 self.which = eAry
670 }
671
672 if self.equal(op.value()) {
673 return nil
674 }
675
676 return errors.Wrapf(ErrTestFailed, "testing value %s failed", path)
677 }
678
679 con, key := findObject(doc, path)
680
681 if con == nil {
682 return errors.Wrapf(ErrMissing, "test operation does not apply: is missing path: %s", path)
683 }
684
685 val, err := con.get(key)
686 if err != nil {
687 return errors.Wrapf(err, "error in test for path: '%s'", path)
688 }
689
690 if val == nil {
691 if op.value().raw == nil {
692 return nil
693 }
694 return errors.Wrapf(ErrTestFailed, "testing value %s failed", path)
695 } else if op.value() == nil {
696 return errors.Wrapf(ErrTestFailed, "testing value %s failed", path)
697 }
698
699 if val.equal(op.value()) {
700 return nil
701 }
702
703 return errors.Wrapf(ErrTestFailed, "testing value %s failed", path)
704 }
705
706 func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) error {
707 from, err := op.From()
708 if err != nil {
709 return errors.Wrapf(err, "copy operation failed to decode from")
710 }
711
712 con, key := findObject(doc, from)
713
714 if con == nil {
715 return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing from path: %s", from)
716 }
717
718 val, err := con.get(key)
719 if err != nil {
720 return errors.Wrapf(err, "error in copy for from: '%s'", from)
721 }
722
723 path, err := op.Path()
724 if err != nil {
725 return errors.Wrapf(ErrMissing, "copy operation failed to decode path")
726 }
727
728 con, key = findObject(doc, path)
729
730 if con == nil {
731 return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing destination path: %s", path)
732 }
733
734 valCopy, sz, err := deepCopy(val)
735 if err != nil {
736 return errors.Wrapf(err, "error while performing deep copy")
737 }
738
739 (*accumulatedCopySize) += int64(sz)
740 if AccumulatedCopySizeLimit > 0 && *accumulatedCopySize > AccumulatedCopySizeLimit {
741 return NewAccumulatedCopySizeError(AccumulatedCopySizeLimit, *accumulatedCopySize)
742 }
743
744 err = con.add(key, valCopy)
745 if err != nil {
746 return errors.Wrapf(err, "error while adding value during copy")
747 }
748
749 return nil
750 }
751
752
753 func Equal(a, b []byte) bool {
754 ra := make(json.RawMessage, len(a))
755 copy(ra, a)
756 la := newLazyNode(&ra)
757
758 rb := make(json.RawMessage, len(b))
759 copy(rb, b)
760 lb := newLazyNode(&rb)
761
762 return la.equal(lb)
763 }
764
765
766 func DecodePatch(buf []byte) (Patch, error) {
767 var p Patch
768
769 err := json.Unmarshal(buf, &p)
770
771 if err != nil {
772 return nil, err
773 }
774
775 return p, nil
776 }
777
778
779
780 func (p Patch) Apply(doc []byte) ([]byte, error) {
781 return p.ApplyIndent(doc, "")
782 }
783
784
785
786 func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) {
787 if len(doc) == 0 {
788 return doc, nil
789 }
790
791 var pd container
792 if doc[0] == '[' {
793 pd = &partialArray{}
794 } else {
795 pd = &partialDoc{}
796 }
797
798 err := json.Unmarshal(doc, pd)
799
800 if err != nil {
801 return nil, err
802 }
803
804 err = nil
805
806 var accumulatedCopySize int64
807
808 for _, op := range p {
809 switch op.Kind() {
810 case "add":
811 err = p.add(&pd, op)
812 case "remove":
813 err = p.remove(&pd, op)
814 case "replace":
815 err = p.replace(&pd, op)
816 case "move":
817 err = p.move(&pd, op)
818 case "test":
819 err = p.test(&pd, op)
820 case "copy":
821 err = p.copy(&pd, op, &accumulatedCopySize)
822 default:
823 err = fmt.Errorf("Unexpected kind: %s", op.Kind())
824 }
825
826 if err != nil {
827 return nil, err
828 }
829 }
830
831 if indent != "" {
832 return json.MarshalIndent(pd, "", indent)
833 }
834
835 return json.Marshal(pd)
836 }
837
838
839
840
841
842
843
844
845 var (
846 rfc6901Decoder = strings.NewReplacer("~1", "/", "~0", "~")
847 )
848
849 func decodePatchKey(k string) string {
850 return rfc6901Decoder.Replace(k)
851 }
852
View as plain text