Skip to content

Commit c429203

Browse files
authored
Merge pull request #401 from esarbe/main
add ujson bridge
2 parents 175e5d8 + 721da18 commit c429203

14 files changed

+474
-3
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ jobs:
8080

8181
- name: Make target directories
8282
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
83-
run: mkdir -p circe/jvm/target playJson/native/target testkit/native/target testkit/js/target core/.native/target playJson/jvm/target core/.js/target circe/js/target core/.jvm/target circe/native/target playJson/js/target testkit/jvm/target sprayJson/.jvm/target project/target
83+
run: mkdir -p ujson/jvm/target circe/jvm/target playJson/native/target testkit/native/target testkit/js/target core/.native/target playJson/jvm/target ujson/js/target core/.js/target circe/js/target core/.jvm/target circe/native/target ujson/native/target playJson/js/target testkit/jvm/target sprayJson/.jvm/target project/target
8484

8585
- name: Compress target directories
8686
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
87-
run: tar cf targets.tar circe/jvm/target playJson/native/target testkit/native/target testkit/js/target core/.native/target playJson/jvm/target core/.js/target circe/js/target core/.jvm/target circe/native/target playJson/js/target testkit/jvm/target sprayJson/.jvm/target project/target
87+
run: tar cf targets.tar ujson/jvm/target circe/jvm/target playJson/native/target testkit/native/target testkit/js/target core/.native/target playJson/jvm/target ujson/js/target core/.js/target circe/js/target core/.jvm/target circe/native/target ujson/native/target playJson/js/target testkit/jvm/target sprayJson/.jvm/target project/target
8888

8989
- name: Upload target directories
9090
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')

README.markdown

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ where `jsonLib` is either:
4343
- `spray-json`
4444
- `play-json`
4545
- `circe`
46+
- `ujson` for ujson/upickle
4647

4748
These versions are built for Scala 2.12, 2.13, and 3.
4849

build.sbt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ lazy val commonSettings = Seq(
2828
homepage := Some(url("https://github.com/gnieh/diffson"))
2929
)
3030

31-
lazy val diffson = tlCrossRootProject.aggregate(core, sprayJson, circe, playJson, testkit)
31+
lazy val diffson = tlCrossRootProject.aggregate(core, sprayJson, circe, playJson, ujson, testkit)
3232

3333
lazy val core = crossProject(JSPlatform, JVMPlatform, NativePlatform)
3434
.crossType(CrossType.Pure)
@@ -92,6 +92,21 @@ lazy val circe = crossProject(JSPlatform, JVMPlatform, NativePlatform)
9292
)
9393
.dependsOn(core, testkit % Test)
9494

95+
val ujsonVersion = "3.1.3"
96+
lazy val ujson = crossProject(JSPlatform, JVMPlatform, NativePlatform)
97+
.crossType(CrossType.Full)
98+
.in(file("ujson"))
99+
.settings(commonSettings: _*)
100+
.settings(
101+
name := "diffson-ujson",
102+
libraryDependencies ++= Seq(
103+
"com.lihaoyi" %%% "ujson" % ujsonVersion,
104+
"com.lihaoyi" %%% "upickle" % ujsonVersion
105+
),
106+
tlVersionIntroduced := Map("3" -> "4.6.0", "2.13" -> "4.6.0", "2.12" -> "4.6.0")
107+
)
108+
.dependsOn(core, testkit % Test)
109+
95110
lazy val benchmarks = crossProject(JVMPlatform)
96111
.crossType(CrossType.Pure)
97112
.in(file("benchmarks"))
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2022 Lucas Satabin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package diffson
18+
package ujson
19+
20+
case class FieldMissing(fieldName: String) extends Exception(s"Expected field `$fieldName`, but it is missing")
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*
2+
* Copyright 2022 Lucas Satabin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package diffson
18+
19+
import cats.implicits._
20+
import diffson.jsonmergepatch.JsonMergePatch
21+
import diffson.jsonpatch._
22+
import diffson.jsonpointer.Pointer
23+
import _root_.ujson.{Value, Arr, Obj, Str}
24+
import upickle.default._
25+
26+
import scala.util.Try
27+
28+
package object ujson {
29+
30+
implicit val jsonyUjson: Jsony[Value] = new Jsony[Value] {
31+
32+
def Null: Value = _root_.ujson.Null
33+
34+
def array(json: Value): Option[Vector[Value]] =
35+
json.arrOpt.map(_.toVector)
36+
37+
def fields(json: Value): Option[Map[String, Value]] =
38+
json.objOpt.map(_.toMap)
39+
40+
def makeArray(values: Vector[Value]): Value =
41+
Arr.from(values)
42+
43+
def makeObject(fields: Map[String, Value]): Value =
44+
Obj.from(fields)
45+
46+
def show(json: Value): String =
47+
json.render()
48+
49+
def eqv(json1: Value, json2: Value): Boolean = json1 == json2
50+
}
51+
52+
implicit val pointerEncoder: Writer[Pointer] =
53+
implicitly[Writer[String]].comap(a => a.show)
54+
55+
implicit val pointerDecoder: Reader[Pointer] =
56+
implicitly[Reader[String]].map { str =>
57+
Pointer.parse[Try](str).fold(throw _, identity)
58+
}
59+
60+
private val operationAsJson: Operation[Value] => Value = {
61+
case Add(path, value) =>
62+
Obj(
63+
"op" -> Str("add"),
64+
"path" -> Str(path.show),
65+
"value" -> value
66+
)
67+
case Remove(path, Some(old)) =>
68+
Obj(
69+
"op" -> Str("remove"),
70+
"path" -> Str(path.show),
71+
"old" -> old
72+
)
73+
case Remove(path, None) =>
74+
Obj(
75+
"op" -> Str("remove"),
76+
"path" -> Str(path.show)
77+
)
78+
case Replace(path, value, Some(old)) =>
79+
Obj(
80+
"op" -> Str("replace"),
81+
"path" -> Str(path.show),
82+
"value" -> value,
83+
"old" -> old
84+
)
85+
case Replace(path, value, None) =>
86+
Obj(
87+
"op" -> Str("replace"),
88+
"path" -> Str(path.show),
89+
"value" -> value
90+
)
91+
case Move(from, path) =>
92+
Obj(
93+
"op" -> Str("move"),
94+
"from" -> Str(from.show),
95+
"path" -> Str(path.show)
96+
)
97+
case Copy(from, path) =>
98+
Obj(
99+
"op" -> Str("copy"),
100+
"from" -> Str(from.show),
101+
"path" -> Str(path.show)
102+
)
103+
case Test(path, value) =>
104+
Obj(
105+
"op" -> Str("test"),
106+
"path" -> Str(path.show),
107+
"value" -> value
108+
)
109+
}
110+
111+
implicit val operationEncoder: Writer[Operation[Value]] =
112+
implicitly[Writer[Value]].comap(operationAsJson)
113+
114+
private def decodeOperation(value: Value): Operation[Value] = {
115+
def readPointer(value: Value, path: String = "path"): Pointer = {
116+
val serializedPointer =
117+
value.objOpt
118+
.flatMap(_.get(path))
119+
.getOrElse(throw FieldMissing(path))
120+
121+
read[Pointer](serializedPointer)
122+
}
123+
124+
val op =
125+
value.objOpt
126+
.flatMap(_.get("op").flatMap(_.strOpt))
127+
.getOrElse(throw FieldMissing("op"))
128+
129+
def getField(key: String) = {
130+
value.objOpt
131+
.flatMap(_.get(key))
132+
.getOrElse(throw FieldMissing(key))
133+
}
134+
135+
op match {
136+
case "add" =>
137+
val path = readPointer(value)
138+
val v = getField("value")
139+
Add(path, v)
140+
141+
case "remove" =>
142+
val path = readPointer(value)
143+
val old = value.objOpt.flatMap(_.get("old"))
144+
Remove(path, old)
145+
case "replace" =>
146+
val path = readPointer(value)
147+
val payload = getField("value")
148+
val old = value.objOpt.flatMap(_.get("old"))
149+
Replace(path, payload, old)
150+
case "move" =>
151+
val path = readPointer(value)
152+
val from = readPointer(value, "from")
153+
Move(from, path)
154+
case "copy" =>
155+
val path = readPointer(value)
156+
val from = readPointer(value, "from")
157+
Copy(from, path)
158+
case "test" =>
159+
val path = readPointer(value)
160+
val payload = getField("value")
161+
Test(path, payload)
162+
case other =>
163+
throw new Exception(s"Expected operation `$other` but it is missing")
164+
}
165+
}
166+
167+
implicit val operationDecoder: Reader[Operation[Value]] =
168+
implicitly[Reader[Value]].map(decodeOperation)
169+
170+
implicit val jsonPatchEncoder: Writer[JsonPatch[Value]] = {
171+
implicitly[Writer[List[Value]]].comap(_.ops.map(operationAsJson))
172+
}
173+
174+
implicit val jsonPatchDecoder: Reader[JsonPatch[Value]] = {
175+
implicitly[Reader[List[Value]]].map(q => JsonPatch(q.map(decodeOperation)))
176+
}
177+
178+
implicit val jsonMergePatchEncoder: Writer[JsonMergePatch[Value]] = {
179+
implicitly[Writer[Value]].comap(_.toJson)
180+
}
181+
182+
implicit val jsonMergePatchDecoder: Reader[JsonMergePatch[Value]] = {
183+
implicitly[Reader[Value]]
184+
.map { value =>
185+
value.objOpt
186+
.map(obj => JsonMergePatch.Object(obj.toMap))
187+
.getOrElse(JsonMergePatch.Value(value))
188+
}
189+
}
190+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2022 Lucas Satabin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package diffson
18+
package ujson
19+
20+
import jsonpatch._
21+
import _root_.ujson._
22+
23+
class UjsonJsonDiff extends TestJsonDiff[Value] with UjsonTestProtocol
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2022 Lucas Satabin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package diffson
18+
package ujson
19+
20+
import _root_.ujson._
21+
import jsonmergepatch._
22+
23+
class UjsonJsonJsonMergeDiff extends TestJsonMergeDiff[Value] with UjsonTestProtocol
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2022 Lucas Satabin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package diffson
18+
package ujson
19+
20+
import jsonpatch._
21+
import _root_.ujson._
22+
23+
class UjsonSimpleDiff extends TestSimpleDiff[Value] with UjsonTestProtocol
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2022 Lucas Satabin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package diffson
18+
package ujson
19+
20+
import jsonpatch._
21+
import _root_.ujson.Value
22+
23+
class UjsonTestArrayDiff extends TestArrayDiff[Value] with UjsonTestProtocol
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2022 Lucas Satabin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package diffson
18+
package ujson
19+
20+
import jsonmergepatch._
21+
import _root_.ujson._
22+
23+
class UjsonTestJsonMergePatch extends TestJsonMergePatch[Value] with UjsonTestProtocol

0 commit comments

Comments
 (0)