Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 26 additions & 34 deletions v5/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,13 @@ type Operation map[string]*json.RawMessage
type Patch []Operation

type partialDoc struct {
self *lazyNode
keys []string
obj map[string]*lazyNode

opts *ApplyOptions
}

type partialArray struct {
self *lazyNode
nodes []*lazyNode
}

Expand Down Expand Up @@ -600,10 +598,6 @@ func (d *partialDoc) add(key string, val *lazyNode, options *ApplyOptions) error
}

func (d *partialDoc) get(key string, options *ApplyOptions) (*lazyNode, error) {
if key == "" {
return d.self, nil
}

if d.obj == nil {
return nil, ErrExpectedObject
}
Expand Down Expand Up @@ -701,10 +695,6 @@ func (d *partialArray) add(key string, val *lazyNode, options *ApplyOptions) err
}

func (d *partialArray) get(key string, options *ApplyOptions) (*lazyNode, error) {
if key == "" {
return d.self, nil
}

idx, err := strconv.Atoi(key)

if err != nil {
Expand Down Expand Up @@ -777,12 +767,9 @@ func (p Patch) add(doc *container, op Operation, options *ApplyOptions) error {

var pd container
if (*val.raw)[0] == '[' {
pd = &partialArray{
self: val,
}
pd = &partialArray{}
} else {
pd = &partialDoc{
self: val,
opts: options,
}
}
Expand Down Expand Up @@ -1069,23 +1056,29 @@ func (p Patch) move(doc *container, op Operation, options *ApplyOptions) error {
return nil
}

func containerNode(doc container) *lazyNode {
var self lazyNode

switch sv := doc.(type) {
case *partialDoc:
self.doc = sv
self.which = eDoc
case *partialArray:
self.ary = sv
self.which = eAry
}

return &self
}

func (p Patch) test(doc *container, op Operation, options *ApplyOptions) error {
path, err := op.Path()
if err != nil {
return fmt.Errorf("test operation failed to decode path: %w", err)
}

if path == "" {
var self lazyNode

switch sv := (*doc).(type) {
case *partialDoc:
self.doc = sv
self.which = eDoc
case *partialArray:
self.ary = sv
self.which = eAry
}
self := containerNode(*doc)

if self.equal(op.value()) {
return nil
Expand Down Expand Up @@ -1135,9 +1128,14 @@ func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64, op
return fmt.Errorf("copy operation does not apply: doc is missing from path: \"%s\": %w", from, ErrMissing)
}

val, err := con.get(key, options)
if err != nil {
return fmt.Errorf("error in copy for from: '%s': %w", from, err)
var val *lazyNode
if from == "" {
val = containerNode(con)
} else {
val, err = con.get(key, options)
if err != nil {
return fmt.Errorf("error in copy for from: '%s': %w", from, err)
}
}

path, err := op.Path()
Expand Down Expand Up @@ -1227,17 +1225,11 @@ func (p Patch) ApplyIndentWithOptions(doc []byte, indent string, options *ApplyO
return nil, ErrInvalid
}

raw := json.RawMessage(doc)
self := newLazyNode(&raw)

var pd container
if doc[0] == '[' {
pd = &partialArray{
self: self,
}
pd = &partialArray{}
} else {
pd = &partialDoc{
self: self,
opts: options,
}
}
Expand Down
51 changes: 51 additions & 0 deletions v5/patch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,34 @@ var Cases = []Case{
false,
false,
},
{
`{"foo": 1}`,
`[{"op": "add", "path": "/qux", "value": 2}, {"op": "copy", "from": "", "path": "/bar"}]`,
`{"foo": 1, "qux": 2, "bar": {"foo": 1, "qux": 2}}`,
false,
false,
},
{
`{"": 1, "foo": 2}`,
`[{"op": "copy", "from": "/", "path": "/bar"}]`,
`{"": 1, "foo": 2, "bar": 1}`,
false,
false,
},
{
`{"": 1, "foo": 2}`,
`[{"op": "move", "from": "/", "path": "/bar"}]`,
`{"foo": 2, "bar": 1}`,
false,
false,
},
{
`{"": {"a": 1}, "a": 2}`,
`[{"op": "replace", "path": "//a", "value": 3}]`,
`{"": {"a": 3}, "a": 2}`,
false,
false,
},
}

type BadCase struct {
Expand Down Expand Up @@ -772,6 +800,11 @@ var BadCases = []BadCase{
`[{"op": "move", "path": "/qux", "from": ""}]`,
false,
},
{
`{ "foo": "bar"}`,
`[{"op": "replace", "path": "/", "value": "qux"}]`,
false,
},
}

// This is not thread safe, so we cannot run patch tests in parallel.
Expand Down Expand Up @@ -949,6 +982,24 @@ var TestCases = []TestCase{
true,
"/baz",
},
{
`{ "": 1 }`,
`[ { "op": "test", "path": "/", "value": 1 } ]`,
true,
"",
},
{
`{ "": { "a": 1 }, "a": 2 }`,
`[ { "op": "test", "path": "//a", "value": 1 } ]`,
true,
"",
},
{
`{ "foo": "bar" }`,
`[ { "op": "test", "path": "/", "value": { "foo": "bar" } } ]`,
false,
"/",
},
}

func TestAllTest(t *testing.T) {
Expand Down