@@ -20,30 +20,30 @@ package jsonpatch
20
20
import lcs ._
21
21
import jsonpointer ._
22
22
23
- import cats .implicits ._
23
+ import cats .syntax .all ._
24
+ import cats .data .Chain
25
+ import cats .Eval
24
26
25
27
import scala .annotation .tailrec
26
- import cats .data .Chain
27
28
28
29
class JsonDiff [Json ](diffArray : Boolean , rememberOld : Boolean )(implicit J : Jsony [Json ], Lcs : Lcs [Json ])
29
30
extends Diff [Json , JsonPatch [Json ]] {
30
31
def diff (json1 : Json , json2 : Json ): JsonPatch [Json ] =
31
- JsonPatch (diff(json1, json2, Pointer .Root ).toList)
32
+ JsonPatch (diff(json1, json2, Pointer .Root ).value. toList)
32
33
33
- private def diff (json1 : Json , json2 : Json , pointer : Pointer ): Chain [Operation [Json ]] =
34
- if (json1 === json2)
35
- // if they are equal, this one is easy...
36
- Chain .empty
37
- else
38
- (json1, json2) match {
39
- case (JsObject (fields1), JsObject (fields2)) => fieldsDiff(fields1.toList, fields2.toList, pointer)
40
- case (JsArray (arr1), JsArray (arr2)) if diffArray => arraysDiff(arr1.toList, arr2.toList, pointer)
41
- case (_, _) => Chain .one(Replace (pointer, json2, if (rememberOld) Some (json1) else None ))
42
- }
34
+ private def diff (json1 : Json , json2 : Json , pointer : Pointer ): Eval [Chain [Operation [Json ]]] =
35
+ (json1, json2) match {
36
+ case (JsObject (fields1), JsObject (fields2)) => fieldsDiff(fields1.toList, fields2.toList, pointer)
37
+ case (JsArray (arr1), JsArray (arr2)) if diffArray => arraysDiff(arr1.toList, arr2.toList, pointer)
38
+ case _ if json1 === json2 =>
39
+ // if they are equal, this one is easy...
40
+ Eval .now(Chain .empty)
41
+ case _ => Eval .now(Chain .one(Replace (pointer, json2, if (rememberOld) Some (json1) else None )))
42
+ }
43
43
44
44
private def fieldsDiff (fields1 : List [(String , Json )],
45
45
fields2 : List [(String , Json )],
46
- path : Pointer ): Chain [Operation [Json ]] = {
46
+ path : Pointer ): Eval [ Chain [Operation [Json ] ]] = {
47
47
// sort fields by name in both objects
48
48
val sorted1 = fields1.sortBy(_._1)
49
49
val sorted2 = fields2.sortBy(_._1)
@@ -67,28 +67,27 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
67
67
Chain .fromSeq(fields2.map(None -> Some (_))) ++ acc
68
68
}
69
69
70
- @ tailrec
71
70
def fields (fs : List [(Option [(String , Json )], Option [(String , Json )])],
72
- acc : Chain [Operation [Json ]]): Chain [Operation [Json ]] = fs match {
73
- case (Some (f1), Some (f2)) :: tl if f1 == f2 =>
71
+ acc : Chain [Operation [Json ]]): Eval [ Chain [Operation [Json ] ]] = fs match {
72
+ case (Some (f1), Some (f2)) :: tl if f1._2 === f2._2 =>
74
73
// all right, nothing changed
75
74
fields(tl, acc)
76
75
case (Some (f1), Some (f2)) :: tl =>
77
76
// same field name, different values
78
- fields(tl, diff(f1._2, f2._2, path / f1._1) ++ acc)
77
+ diff(f1._2, f2._2, path / f1._1).flatMap(d => fields(tl, d ++ acc) )
79
78
case (Some (f1), None ) :: tl =>
80
79
// the field was deleted
81
80
fields(tl, acc.prepend(Remove [Json ](path / f1._1, if (rememberOld) Some (f1._2) else None )))
82
81
case (None , Some (f2)) :: tl =>
83
82
// the field was added
84
83
fields(tl, acc.prepend(Add (path / f2._1, f2._2)))
85
84
case _ =>
86
- acc
85
+ Eval .now( acc)
87
86
}
88
87
fields(associate(sorted1, sorted2, Chain .empty).toList, Chain .empty)
89
88
}
90
89
91
- private def arraysDiff (arr1 : List [Json ], arr2 : List [Json ], path : Pointer ): Chain [Operation [Json ]] = {
90
+ private def arraysDiff (arr1 : List [Json ], arr2 : List [Json ], path : Pointer ): Eval [ Chain [Operation [Json ] ]] = {
92
91
// get the longest common subsequence in the array
93
92
val lcs = Lcs .lcs(arr1, arr2)
94
93
@@ -118,7 +117,6 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
118
117
yield Remove [Json ](path / idx, if (rememberOld) Some (arr(idx - shift)) else None ))
119
118
120
119
// now iterate over the first array to computes what was added, what was removed and what was modified
121
- @ tailrec
122
120
def loop (
123
121
arr1 : List [Json ], // the first array
124
122
arr2 : List [Json ], // the second array
@@ -127,7 +125,7 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
127
125
idx2 : Int , // current index in the second array
128
126
lcs : List [(Int , Int )], // the list of remaining matching indices
129
127
acc : Chain [Operation [Json ]] // the already accumulated result
130
- ): Chain [Operation [Json ]] = (arr1, arr2) match {
128
+ ): Eval [ Chain [Operation [Json ] ]] = (arr1, arr2) match {
131
129
case (_ :: tl1, _) if isCommon1(idx1, lcs) =>
132
130
// all values in arr2 were added until the index of common value
133
131
val until = lcs.head._2
@@ -150,13 +148,13 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
150
148
acc ++ remove(idx1 + shift1, until - 1 + shift1, idx1 + shift1, arr1))
151
149
case (v1 :: tl1, v2 :: tl2) =>
152
150
// values are different, recursively compute the diff of these values
153
- loop(tl1, tl2, idx1 + 1 , shift1, idx2 + 1 , lcs, acc ++ diff(v1, v2, path / (idx1 + shift1) ))
151
+ diff(v1, v2, path / (idx1 + shift1)).flatMap(d => loop(tl1, tl2, idx1 + 1 , shift1, idx2 + 1 , lcs, acc ++ d ))
154
152
case (_, Nil ) =>
155
153
// all subsequent values in arr1 were removed
156
- acc ++ remove(idx1 + shift1, idx1 + arr1.size - 1 + shift1, idx1 + shift1, arr1)
154
+ Eval .now( acc ++ remove(idx1 + shift1, idx1 + arr1.size - 1 + shift1, idx1 + shift1, arr1) )
157
155
case (Nil , _) =>
158
156
// all subsequent value in arr2 were added
159
- acc ++ Chain .fromSeq(arr2.map(Add (path / " -" , _)))
157
+ Eval .now( acc ++ Chain .fromSeq(arr2.map(Add (path / " -" , _) )))
160
158
}
161
159
162
160
loop(arr1, arr2, 0 , 0 , 0 , lcs, Chain .empty)
0 commit comments