-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathpatch.go
119 lines (98 loc) · 2.83 KB
/
patch.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
package jsonpatch
import (
"encoding/json"
"reflect"
"slices"
)
// JSONPatch format is specified in RFC 6902
type JSONPatch struct {
Operation string `json:"op"`
Path string `json:"path"`
Value interface{} `json:"value,omitempty"`
}
// JSONPatchList is a list of JSONPatch
type JSONPatchList struct {
list []JSONPatch
raw []byte
}
// Empty returns true if the JSONPatchList is empty
func (l JSONPatchList) Empty() bool {
return l.Len() == 0
}
// Len returns the length of the JSONPatchList
func (l JSONPatchList) Len() int {
return len(l.list)
}
// String returns the encoded JSON string of the JSONPatchList
func (l JSONPatchList) String() string {
return string(l.raw)
}
// Raw returns the raw encoded JSON of the JSONPatchList
func (l JSONPatchList) Raw() []byte {
return l.raw
}
// List returns a copy of the underlying JSONPatch slice
func (l JSONPatchList) List() []JSONPatch {
return slices.Clone(l.list)
}
// CreateJSONPatch compares two JSON data structures and creates a JSONPatch according to RFC 6902
func CreateJSONPatch(modified, current interface{}, options ...Option) (JSONPatchList, error) {
// create a new walker
w := &walker{
handler: &DefaultHandler{},
predicate: Funcs{},
prefix: []string{""},
}
// apply options to the walker
for _, apply := range options {
apply(w)
}
if err := w.walk(reflect.ValueOf(modified), reflect.ValueOf(current), w.prefix); err != nil {
return JSONPatchList{}, err
}
list := w.patchList
if len(list) == 0 {
return JSONPatchList{}, nil
}
raw, err := json.Marshal(list)
return JSONPatchList{list: list, raw: raw}, err
}
// CreateThreeWayJSONPatch compares three JSON data structures and creates a three-way JSONPatch according to RFC 6902
func CreateThreeWayJSONPatch(modified, current, original interface{}, options ...Option) (JSONPatchList, error) {
var list []JSONPatch
// create a new walker
w := &walker{
handler: &DefaultHandler{},
predicate: Funcs{},
prefix: []string{""},
}
// apply options to the walker
for _, apply := range options {
apply(w)
}
// compare modified with current and only keep addition and changes
if err := w.walk(reflect.ValueOf(modified), reflect.ValueOf(current), w.prefix); err != nil {
return JSONPatchList{}, err
}
for _, patch := range w.patchList {
if patch.Operation != "remove" {
list = append(list, patch)
}
}
// reset walker
w.patchList = []JSONPatch{}
// compare modified with original and only keep deletions
if err := w.walk(reflect.ValueOf(modified), reflect.ValueOf(original), w.prefix); err != nil {
return JSONPatchList{}, err
}
for _, patch := range w.patchList {
if patch.Operation == "remove" {
list = append(list, patch)
}
}
if len(list) == 0 {
return JSONPatchList{}, nil
}
raw, err := json.Marshal(list)
return JSONPatchList{list: list, raw: raw}, err
}