Skip to content

Commit 5359542

Browse files
feat: Replace jsoniter macros with circe (#83)
Co-authored-by: ghostbuster91 <ghostbuster91@users.noreply.github.com> Co-authored-by: Jakub Kozłowski <kubukoz@gmail.com>
1 parent 2ba2bab commit 5359542

File tree

23 files changed

+211
-204
lines changed

23 files changed

+211
-204
lines changed

build.sbt

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ val commonSettings = Seq(
3333
"com.disneystreaming" %%% "weaver-cats" % "0.8.4" % Test
3434
),
3535
mimaPreviousArtifacts := Set(
36-
organization.value %%% name.value % "0.0.7"
36+
// organization.value %%% name.value % "0.0.7"
3737
),
3838
scalacOptions ++= {
3939
CrossVersion.partialVersion(scalaVersion.value) match {
@@ -69,7 +69,7 @@ val core = projectMatrix
6969
name := "jsonrpclib-core",
7070
commonSettings,
7171
libraryDependencies ++= Seq(
72-
"com.github.plokhotnyuk.jsoniter-scala" %%% "jsoniter-scala-macros" % "2.30.2"
72+
"com.github.plokhotnyuk.jsoniter-scala" %%% "jsoniter-scala-circe" % "2.30.2"
7373
)
7474
)
7575

@@ -84,7 +84,8 @@ val fs2 = projectMatrix
8484
name := "jsonrpclib-fs2",
8585
commonSettings,
8686
libraryDependencies ++= Seq(
87-
"co.fs2" %%% "fs2-core" % fs2Version
87+
"co.fs2" %%% "fs2-core" % fs2Version,
88+
"io.circe" %%% "circe-generic" % "0.14.7" % Test
8889
)
8990
)
9091

@@ -127,7 +128,6 @@ val smithy4s = projectMatrix
127128
commonSettings,
128129
mimaPreviousArtifacts := Set.empty,
129130
libraryDependencies ++= Seq(
130-
"co.fs2" %%% "fs2-core" % fs2Version,
131131
"com.disneystreaming.smithy4s" %%% "smithy4s-json" % smithy4sVersion.value
132132
),
133133
buildTimeProtocolDependency
@@ -141,7 +141,8 @@ val exampleServer = projectMatrix
141141
commonSettings,
142142
publish / skip := true,
143143
libraryDependencies ++= Seq(
144-
"co.fs2" %%% "fs2-io" % fs2Version
144+
"co.fs2" %%% "fs2-io" % fs2Version,
145+
"io.circe" %%% "circe-generic" % "0.14.7"
145146
)
146147
)
147148
.disablePlugins(MimaPlugin)
@@ -161,7 +162,8 @@ val exampleClient = projectMatrix
161162
commonSettings,
162163
publish / skip := true,
163164
libraryDependencies ++= Seq(
164-
"co.fs2" %%% "fs2-io" % fs2Version
165+
"co.fs2" %%% "fs2-io" % fs2Version,
166+
"io.circe" %%% "circe-generic" % "0.14.7"
165167
)
166168
)
167169
.disablePlugins(MimaPlugin)
@@ -236,17 +238,7 @@ val root = project
236238
).flatMap(_.projectRefs): _*
237239
)
238240

239-
// The core compiles are a workaround for https://github.com/plokhotnyuk/jsoniter-scala/issues/564
240-
// when we switch to SN 0.5, we can use `makeWithSkipNestedOptionValues` instead: https://github.com/plokhotnyuk/jsoniter-scala/issues/564#issuecomment-2787096068
241-
val compileCoreModules = {
242-
for {
243-
scalaVersionSuffix <- List("", "3")
244-
platformSuffix <- List("", "JS", "Native")
245-
task <- List("compile", "package")
246-
} yield s"core$platformSuffix$scalaVersionSuffix/$task"
247-
}.mkString(";")
248-
249241
addCommandAlias(
250242
"ci",
251-
s"$compileCoreModules;test;scalafmtCheckAll;mimaReportBinaryIssues"
243+
s"compile;test;scalafmtCheckAll;mimaReportBinaryIssues"
252244
)
Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,24 @@
11
package jsonrpclib
22

3-
import com.github.plokhotnyuk.jsoniter_scala.core._
4-
import scala.annotation.switch
3+
import io.circe.{Decoder, Encoder, Json, Codec}
54

65
sealed trait CallId
76
object CallId {
87
final case class NumberId(long: Long) extends CallId
98
final case class StringId(string: String) extends CallId
109
case object NullId extends CallId
1110

12-
implicit val callIdRW: JsonValueCodec[CallId] = new JsonValueCodec[CallId] {
13-
def decodeValue(in: JsonReader, default: CallId): CallId = {
14-
val nt = in.nextToken()
15-
16-
(nt: @switch) match {
17-
case 'n' => in.readNullOrError(default, "expected null")
18-
case '"' => in.rollbackToken(); StringId(in.readString(null))
19-
case _ => in.rollbackToken(); NumberId(in.readLong())
20-
21-
}
11+
implicit val codec: Codec[CallId] = Codec.from(
12+
Decoder
13+
.decodeOption(Decoder.decodeString.map(StringId(_): CallId).or(Decoder.decodeLong.map(NumberId(_): CallId)))
14+
.map {
15+
case None => NullId
16+
case Some(v) => v
17+
},
18+
{
19+
case NumberId(n) => Json.fromLong(n)
20+
case StringId(str) => Json.fromString(str)
21+
case NullId => Json.Null
2222
}
23-
24-
def encodeValue(x: CallId, out: JsonWriter): Unit = x match {
25-
case NumberId(long) => out.writeVal(long)
26-
case StringId(string) => out.writeVal(string)
27-
case NullId => out.writeNull()
28-
}
29-
30-
def nullValue: CallId = CallId.NullId
31-
}
23+
)
3224
}

modules/core/src/main/scala/jsonrpclib/Channel.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package jsonrpclib
22

3+
import io.circe.Codec
4+
35
trait Channel[F[_]] {
46
def mountEndpoint(endpoint: Endpoint[F]): F[Unit]
57
def unmountEndpoint(method: String): F[Unit]

modules/core/src/main/scala/jsonrpclib/Codec.scala

Lines changed: 0 additions & 35 deletions
This file was deleted.

modules/core/src/main/scala/jsonrpclib/Endpoint.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package jsonrpclib
22

3+
import io.circe.Codec
4+
35
sealed trait Endpoint[F[_]] {
46
def method: String
57
}
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
package jsonrpclib
22

3-
import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec
4-
import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker
3+
import io.circe.{Decoder, Encoder}
54

65
case class ErrorPayload(code: Int, message: String, data: Option[Payload]) extends Throwable {
76
override def getMessage(): String = s"JsonRPC Error $code: $message"
87
}
98

109
object ErrorPayload {
1110

12-
implicit val rawMessageStubJsonValueCodecs: JsonValueCodec[ErrorPayload] =
13-
JsonCodecMaker.make
11+
implicit val errorPayloadEncoder: Encoder[ErrorPayload] =
12+
Encoder.forProduct3("code", "message", "data")(e => (e.code, e.message, e.data))
1413

14+
implicit val errorPayloadDecoder: Decoder[ErrorPayload] =
15+
Decoder.forProduct3("code", "message", "data")(ErrorPayload.apply)
1516
}
Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,43 @@
11
package jsonrpclib
22

3-
import com.github.plokhotnyuk.jsoniter_scala.core.JsonReader
4-
import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec
5-
import com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter
3+
import io.circe.{Decoder, Encoder}
4+
import io.circe.syntax._
5+
import io.circe.Codec
66

77
sealed trait Message { def maybeCallId: Option[CallId] }
88
sealed trait InputMessage extends Message { def method: String }
99
sealed trait OutputMessage extends Message {
10-
def callId: CallId; final override def maybeCallId: Option[CallId] = Some(callId)
10+
def callId: CallId
11+
final override def maybeCallId: Option[CallId] = Some(callId)
1112
}
1213

1314
object InputMessage {
1415
case class RequestMessage(method: String, callId: CallId, params: Option[Payload]) extends InputMessage {
1516
def maybeCallId: Option[CallId] = Some(callId)
1617
}
18+
1719
case class NotificationMessage(method: String, params: Option[Payload]) extends InputMessage {
1820
def maybeCallId: Option[CallId] = None
1921
}
22+
2023
}
24+
2125
object OutputMessage {
2226
def errorFrom(callId: CallId, protocolError: ProtocolError): OutputMessage =
2327
ErrorMessage(callId, ErrorPayload(protocolError.code, protocolError.getMessage(), None))
2428

2529
case class ErrorMessage(callId: CallId, payload: ErrorPayload) extends OutputMessage
2630
case class ResponseMessage(callId: CallId, data: Payload) extends OutputMessage
31+
2732
}
2833

2934
object Message {
35+
import jsonrpclib.internals.RawMessage
3036

31-
implicit val messageJsonValueCodecs: JsonValueCodec[Message] = new JsonValueCodec[Message] {
32-
val rawMessageCodec = implicitly[JsonValueCodec[internals.RawMessage]]
33-
def decodeValue(in: JsonReader, default: Message): Message =
34-
rawMessageCodec.decodeValue(in, null).toMessage match {
35-
case Left(error) => throw error
36-
case Right(value) => value
37-
}
38-
def encodeValue(x: Message, out: JsonWriter): Unit =
39-
rawMessageCodec.encodeValue(internals.RawMessage.from(x), out)
40-
def nullValue: Message = null
41-
}
37+
implicit val codec: Codec[Message] = Codec.from(
38+
{ c =>
39+
c.as[RawMessage].flatMap(_.toMessage.left.map(e => io.circe.DecodingFailure(e.getMessage, c.history)))
40+
},
41+
RawMessage.from(_).asJson
42+
)
4243
}
Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,15 @@
11
package jsonrpclib
22

3-
import com.github.plokhotnyuk.jsoniter_scala.core.JsonReader
4-
import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec
5-
import com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter
3+
import io.circe.{Decoder, Encoder, Json}
64

7-
import java.util.Base64
8-
import jsonrpclib.Payload.Data
9-
import jsonrpclib.Payload.NullPayload
10-
11-
sealed trait Payload extends Product with Serializable {
12-
def stripNull: Option[Payload.Data] = this match {
13-
case d @ Data(_) => Some(d)
14-
case NullPayload => None
15-
}
5+
case class Payload(data: Json) {
6+
def stripNull: Option[Payload] = Option(Payload(data)).filter(p => !p.data.isNull)
167
}
178

189
object Payload {
19-
def apply(value: Array[Byte]) = {
20-
if (value == null) NullPayload
21-
else Data(value)
22-
}
23-
final case class Data(array: Array[Byte]) extends Payload {
24-
override def equals(other: Any) = other match {
25-
case bytes: Data => java.util.Arrays.equals(array, bytes.array)
26-
case _ => false
27-
}
28-
29-
override lazy val hashCode: Int = java.util.Arrays.hashCode(array)
30-
31-
override def toString = Base64.getEncoder.encodeToString(array)
32-
}
33-
34-
case object NullPayload extends Payload
35-
36-
implicit val payloadJsonValueCodec: JsonValueCodec[Payload] = new JsonValueCodec[Payload] {
37-
def decodeValue(in: JsonReader, default: Payload): Payload = {
38-
Data(in.readRawValAsBytes())
39-
}
40-
41-
def encodeValue(bytes: Payload, out: JsonWriter): Unit =
42-
bytes match {
43-
case Data(array) => out.writeRawVal(array)
44-
case NullPayload => out.writeNull()
4510

46-
}
11+
val NullPayload: Payload = Payload(Json.Null)
4712

48-
def nullValue: Payload = null
49-
}
13+
implicit val payloadEncoder: Encoder[Payload] = Encoder[Json].contramap(_.data)
14+
implicit val payloadDecoder: Decoder[Payload] = Decoder[Json].map(Payload(_))
5015
}

modules/core/src/main/scala/jsonrpclib/StubTemplate.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package jsonrpclib
22

3+
import io.circe.Codec
4+
35
sealed trait StubTemplate[In, Err, Out]
46
object StubTemplate {
57
def notification[In](method: String)(implicit inCodec: Codec[In]): StubTemplate[In, Nothing, Unit] =

modules/core/src/main/scala/jsonrpclib/internals/FutureBaseChannel.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import scala.concurrent.ExecutionContext
77
import scala.concurrent.Future
88
import scala.concurrent.Promise
99
import scala.util.Try
10+
import io.circe.Codec
11+
import io.circe.Encoder
1012

1113
abstract class FutureBasedChannel(endpoints: List[Endpoint[Future]])(implicit ec: ExecutionContext)
1214
extends MessageDispatcher[Future] {
@@ -25,7 +27,7 @@ abstract class FutureBasedChannel(endpoints: List[Endpoint[Future]])(implicit ec
2527
protected def getEndpoint(method: String): Future[Option[Endpoint[Future]]] =
2628
Future.successful(endpointsMap.get(method))
2729
protected def sendMessage(message: Message): Future[Unit] = {
28-
sendPayload(Codec.encode(message)).map(_ => ())
30+
sendPayload(Payload(Encoder[Message].apply(message))).map(_ => ())
2931
}
3032
protected def nextCallId(): Future[CallId] = Future.successful(CallId.NumberId(nextID.incrementAndGet()))
3133

0 commit comments

Comments
 (0)