Update scalatest to 3.2.1
1 |
/*
|
|
2 |
* This file is part of the diffson project.
|
|
3 |
* Copyright (c) 2019 Lucas Satabin
|
|
4 |
*
|
|
5 |
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6 |
* you may not use this file except in compliance with the License.
|
|
7 |
* You may obtain a copy of the License at
|
|
8 |
*
|
|
9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10 |
*
|
|
11 |
* Unless required by applicable law or agreed to in writing, software
|
|
12 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14 |
* See the License for the specific language governing permissions and
|
|
15 |
* limitations under the License.
|
|
16 |
*/
|
|
17 |
package diffson |
|
18 |
package playJson |
|
19 |
|
|
20 |
import jsonpatch._ |
|
21 |
import jsonpointer._ |
|
22 |
import jsonmergepatch._ |
|
23 |
|
|
24 |
import cats._ |
|
25 |
import cats.implicits._ |
|
26 |
|
|
27 |
import play.api.libs.json._ |
|
28 |
|
|
29 |
import scala.annotation.tailrec |
|
30 |
|
|
31 |
object DiffsonProtocol { |
|
32 |
|
|
33 |
private def errorToException(error: JsError): Exception = |
|
34 |
JsError.toFlatForm(error) match { |
|
35 |
case Seq((_, Seq(e, _*)), _*) => new PatchException(e.message) |
|
36 |
case _ => new PatchException("Empty json error") |
|
37 |
}
|
|
38 |
|
|
39 |
implicit object JsResultInstances extends MonadError[JsResult, Throwable] { |
|
40 |
def pure[A](a: A): JsResult[A] = |
|
41 | 1 |
JsSuccess(a) |
42 |
|
|
43 |
def handleErrorWith[A](fa: JsResult[A])(f: Throwable => JsResult[A]): JsResult[A] = |
|
44 |
fa.recoverWith(f.compose(errorToException(_))) |
|
45 |
|
|
46 |
def raiseError[A](e: Throwable): JsResult[A] = |
|
47 |
JsError(e.getMessage) |
|
48 |
|
|
49 |
def flatMap[A, B](fa: JsResult[A])(f: A => JsResult[B]): JsResult[B] = |
|
50 | 1 |
fa.flatMap(f) |
51 |
|
|
52 |
@tailrec
|
|
53 |
def tailRecM[A, B](a: A)(f: A => JsResult[Either[A, B]]): JsResult[B] = |
|
54 |
f(a) match { |
|
55 |
case e @ JsError(_) => e |
|
56 |
case JsSuccess(Left(a), _) => tailRecM(a)(f) |
|
57 |
case JsSuccess(Right(b), p) => JsSuccess(b, p) |
|
58 |
}
|
|
59 |
|
|
60 |
}
|
|
61 |
|
|
62 |
implicit val PointerFormat: Format[Pointer] = |
|
63 | 1 |
Format[Pointer](Reads { |
64 |
case JsString(s) => Pointer.parse[JsResult](s) |
|
65 |
case value => JsError(f"Pointer expected: $value") |
|
66 | 1 |
}, Writes(p => JsString(p.show))) |
67 |
|
|
68 |
implicit val OperationFormat: Format[Operation[JsValue]] = |
|
69 | 1 |
Format[Operation[JsValue]]( |
70 | 1 |
Reads { |
71 | 1 |
case obj @ play.api.libs.json.JsObject(fields) if fields.contains("op") => |
72 | 1 |
fields("op") match { |
73 |
case JsString("add") => |
|
74 |
(fields.get("path"), fields.get("value")) match { |
|
75 |
case (Some(JsString(path)), Some(value)) => |
|
76 | 1 |
Pointer.parse[JsResult](path).map(Add(_, value)) |
77 |
case _ => |
|
78 | 1 |
JsError("missing 'path' or 'value' field") |
79 |
}
|
|
80 |
case JsString("remove") => |
|
81 |
(fields.get("path"), fields.get("old")) match { |
|
82 |
case (Some(JsString(path)), old) => |
|
83 | 1 |
Pointer.parse[JsResult](path).map(Remove(_, old)) |
84 |
case _ => |
|
85 |
JsError("missing 'path' field") |
|
86 |
}
|
|
87 |
case JsString("replace") => |
|
88 |
(fields.get("path"), fields.get("value"), fields.get("old")) match { |
|
89 |
case (Some(JsString(path)), Some(value), old) => |
|
90 | 1 |
Pointer.parse[JsResult](path).map(Replace(_, value, old)) |
91 |
case _ => |
|
92 | 1 |
JsError("missing 'path' or 'value' field") |
93 |
}
|
|
94 |
case JsString("move") => |
|
95 |
(fields.get("from"), fields.get("path")) match { |
|
96 |
case (Some(JsString(from)), Some(JsString(path))) => |
|
97 | 1 |
(Pointer.parse[JsResult](from), Pointer.parse[JsResult](path)).mapN(Move(_, _)) |
98 |
case _ => |
|
99 | 1 |
JsError("missing 'from' or 'path' field") |
100 |
}
|
|
101 |
case JsString("copy") => |
|
102 |
(fields.get("from"), fields.get("path")) match { |
|
103 |
case (Some(JsString(from)), Some(JsString(path))) => |
|
104 | 1 |
(Pointer.parse[JsResult](from), Pointer.parse[JsResult](path)).mapN(Copy(_, _)) |
105 |
case _ => |
|
106 | 1 |
JsError("missing 'from' or 'path' field") |
107 |
}
|
|
108 |
case JsString("test") => |
|
109 |
(fields.get("path"), fields.get("value")) match { |
|
110 |
case (Some(JsString(path)), Some(value)) => |
|
111 | 1 |
Pointer.parse[JsResult](path).map(Test(_, value)) |
112 |
case _ => |
|
113 | 1 |
JsError("missing 'path' or 'value' field") |
114 |
}
|
|
115 |
case op => |
|
116 | 1 |
JsError(f"Unknown operation ${Json.stringify(op)}") |
117 |
}
|
|
118 |
case value => |
|
119 |
JsError(f"Operation[JsValue] expected: $value") |
|
120 |
},
|
|
121 | 1 |
Writes { |
122 |
case Add(path, value) => |
|
123 | 1 |
Json.obj("op" -> JsString("add"), "path" -> JsString(path.show), "value" -> value) |
124 |
case Remove(path, Some(old)) => |
|
125 |
Json.obj("op" -> JsString("remove"), "path" -> JsString(path.show), "old" -> old) |
|
126 |
case Remove(path, None) => |
|
127 | 1 |
Json.obj("op" -> JsString("remove"), "path" -> JsString(path.show)) |
128 |
case Replace(path, value, Some(old)) => |
|
129 |
Json.obj("op" -> JsString("replace"), "path" -> JsString(path.show), "value" -> value, "old" -> old) |
|
130 |
case Replace(path, value, None) => |
|
131 | 1 |
Json.obj("op" -> JsString("replace"), "path" -> JsString(path.show), "value" -> value) |
132 |
case Move(from, path) => |
|
133 | 1 |
Json.obj("op" -> JsString("move"), "from" -> JsString(from.show), "path" -> JsString(path.show)) |
134 |
case Copy(from, path) => |
|
135 | 1 |
Json.obj("op" -> JsString("copy"), "from" -> JsString(from.show), "path" -> JsString(path.show)) |
136 |
case Test(path, value) => |
|
137 | 1 |
Json.obj("op" -> JsString("test"), "path" -> JsString(path.show), "value" -> value) |
138 |
})
|
|
139 |
|
|
140 |
implicit val JsonPatchFormat: Format[JsonPatch[JsValue]] = |
|
141 | 1 |
Format[JsonPatch[JsValue]]( |
142 | 1 |
Reads[JsonPatch[JsValue]] { js => |
143 | 1 |
js.validate[List[Operation[JsValue]]].map(JsonPatch(_)).recoverWith { |
144 | 1 |
case JsError(errors) => JsError((JsPath -> Seq(JsonValidationError("JsonPatch[JsValue] expected"))) +: errors) |
145 |
}
|
|
146 |
},
|
|
147 | 1 |
Writes(patch => play.api.libs.json.JsArray(patch.ops.map(Json.toJson(_)).toVector))) |
148 |
|
|
149 |
implicit val JsonMergePatchFormat: Format[JsonMergePatch[JsValue]] = |
|
150 | 1 |
Format[JsonMergePatch[JsValue]]( |
151 | 1 |
Reads[JsonMergePatch[JsValue]] { |
152 | 1 |
case play.api.libs.json.JsObject(flds) => JsSuccess(JsonMergePatch.Object(flds.toMap)) |
153 | 1 |
case value => JsSuccess(JsonMergePatch.Value(value)) |
154 |
},
|
|
155 | 1 |
Writes { |
156 | 1 |
case JsonMergePatch.Object(flds) => play.api.libs.json.JsObject(flds) |
157 |
case JsonMergePatch.Value(v) => v |
|
158 |
})
|
|
159 |
|
|
160 |
}
|
Read our documentation on viewing source code .