Skip to content

Commit ebf2f12

Browse files
committed
Progressing on the fs2-specific implementation
1 parent be75d0f commit ebf2f12

27 files changed

+248
-87
lines changed

build.sc

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,12 @@ object versions {
2222
}
2323
import versions._
2424

25-
object lib extends Cross[LibModule](versions.crossMap.keys.toSeq: _*)
26-
class LibModule(versionKey: String) extends define.Module {
25+
object core extends Cross[CoreModule](versions.crossMap.keys.toSeq: _*)
26+
class CoreModule(versionKey: String) extends define.Module {
2727

2828
val crossVersion = crossMap(versionKey)
2929
def crossPlatformIvyDeps: T[Agg[Dep]] = Agg(
30-
ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros::2.13.31",
31-
ivy"io.circe::circe-core::0.14.2"
30+
ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros::2.13.31"
3231
)
3332

3433
object jvm extends RpcCrossScalaModule {
@@ -63,19 +62,19 @@ class FS2Module(versionKey: String) extends define.Module {
6362
val crossVersion = crossMap(versionKey)
6463
def crossPlatformIvyDeps: T[Agg[Dep]] = Agg(
6564
ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros::2.13.31",
66-
ivy"io.circe::circe-core::0.14.2"
65+
ivy"co.fs2::fs2-core:3.2.8"
6766
)
6867

6968
object jvm extends RpcCrossScalaModule {
70-
def moduleDeps = Seq(lib(versionKey).jvm)
69+
def moduleDeps = Seq(core(versionKey).jvm)
7170
def crossScalaVersion = crossVersion
7271
def targetPlatform: Platform = Platform.JVM
7372
def ivyDeps = T(super.ivyDeps() ++ crossPlatformIvyDeps())
7473
object test extends Tests
7574
}
7675

7776
object js extends ScalaJSModule with RpcCrossScalaModule {
78-
def moduleDeps = Seq(lib(versionKey).js)
77+
def moduleDeps = Seq(core(versionKey).js)
7978
def crossScalaVersion = crossVersion
8079
def targetPlatform: Platform = Platform.JS
8180
def scalaJSVersion = versions.scalaJSVersion

core/src/jsonrpclib/Channel.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package jsonrpclib
2+
3+
import jsonrpclib.EndpointTemplate.NotificationTemplate
4+
import jsonrpclib.EndpointTemplate.RequestResponseTemplate
5+
6+
trait Channel[F[_]] {
7+
def mountEndpoint(endpoint: Endpoint[F]): F[Unit]
8+
def unmountEndpoint(method: String): F[Unit]
9+
10+
def notificationStub[In](method: String)(implicit inCodec: Codec[In]): In => F[Unit]
11+
def requestResponseStub[In, Err, Out](
12+
method: String
13+
)(implicit inCodec: Codec[In], errCodec: ErrorCodec[Err], outCodec: Codec[Out]): In => F[Either[Err, Out]]
14+
15+
def clientStub[In, Err, Out](template: EndpointTemplate[In, Err, Out]): In => F[Either[Err, Out]]
16+
}
17+
18+
object Channel {
19+
20+
protected[jsonrpclib] abstract class MonadicChannel[F[_]](implicit F: Monadic[F]) extends Channel[F] {
21+
22+
final def clientStub[In, Err, Out](template: EndpointTemplate[In, Err, Out]): In => F[Either[Err, Out]] =
23+
template match {
24+
case NotificationTemplate(method, inCodec) =>
25+
val stub = notificationStub(method)(inCodec)
26+
(in: In) => F.doFlatMap(stub(in))(unit => F.doPure(Right(unit)))
27+
case RequestResponseTemplate(method, inCodec, errCodec, outCodec) =>
28+
requestResponseStub(method)(inCodec, errCodec, outCodec)
29+
}
30+
}
31+
32+
}
File renamed without changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package jsonrpclib
2+
3+
final case class ConflictingMethodError(name: String) extends Exception {
4+
override def getMessage(): String = s"Method $name is already handled"
5+
}

core/src/jsonrpclib/Endpoint.scala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package jsonrpclib
2+
3+
sealed trait Endpoint[F[_]] {
4+
def method: String
5+
}
6+
7+
object Endpoint {
8+
9+
def apply[F[_]](method: String): PartiallyAppliedEndpoint[F] = new PartiallyAppliedEndpoint[F](method)
10+
11+
class PartiallyAppliedEndpoint[F[_]](method: String) {
12+
def apply[In, Err, Out](
13+
run: In => F[Either[Err, Out]]
14+
)(implicit inCodec: Codec[In], errCodec: ErrorCodec[Err], outCodec: Codec[Out]): Endpoint[F] =
15+
RequestResponseEndpoint(method, run, inCodec, errCodec, outCodec)
16+
17+
def simple[In, Out](
18+
run: In => F[Out]
19+
)(implicit F: Monadic[F], inCodec: Codec[In], outCodec: Codec[Out]) =
20+
apply[In, ErrorPayload, Out](in =>
21+
F.doFlatMap(F.doAttempt(run(in))) {
22+
case Left(error) => F.doPure(Left(ErrorPayload(0, error.getMessage(), None)))
23+
case Right(value) => F.doPure(Right(value))
24+
}
25+
)
26+
27+
def notification[In](run: In => F[Unit])(implicit inCodec: Codec[In]): Endpoint[F] =
28+
NotificationEndpoint(method, run, inCodec)
29+
30+
}
31+
32+
final case class NotificationEndpoint[F[_], In](method: String, run: In => F[Unit], inCodec: Codec[In])
33+
extends Endpoint[F]
34+
35+
final case class RequestResponseEndpoint[F[_], In, Err, Out](
36+
method: String,
37+
run: In => F[Either[Err, Out]],
38+
inCodec: Codec[In],
39+
errCodec: ErrorCodec[Err],
40+
outCodec: Codec[Out]
41+
) extends Endpoint[F]
42+
43+
}
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
package jsonrpclib
22

3-
sealed trait EndpointTemplate
4-
3+
sealed trait EndpointTemplate[In, Err, Out]
54
object EndpointTemplate {
65
final case class NotificationTemplate[In](
76
method: String,
87
inCodec: Codec[In]
9-
) extends EndpointTemplate
10-
8+
) extends EndpointTemplate[In, Nothing, Unit]
119
final case class RequestResponseTemplate[In, Err, Out](
1210
method: String,
1311
inCodec: Codec[In],
1412
errCodec: ErrorCodec[Err],
1513
outCodec: Codec[Out]
16-
) extends EndpointTemplate
14+
) extends EndpointTemplate[In, Err, Out]
1715
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package jsonrpclib
2+
3+
trait ErrorCodec[E] {
4+
5+
def encode(a: E): ErrorPayload
6+
def decode(error: ErrorPayload): Either[ProtocolError, E]
7+
8+
}
9+
10+
object ErrorCodec {
11+
implicit val errorPayloadCodec: ErrorCodec[ErrorPayload] = new ErrorCodec[ErrorPayload] {
12+
def encode(a: ErrorPayload): ErrorPayload = a
13+
def decode(error: ErrorPayload): Either[ProtocolError, ErrorPayload] = Right(error)
14+
}
15+
}

lib/src/ErrorPayload.scala renamed to core/src/jsonrpclib/ErrorPayload.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package jsonrpclib
33
import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec
44
import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker
55

6-
private[jsonrpclib] case class ErrorPayload(code: Int, message: String, data: Option[Payload])
6+
private[jsonrpclib] case class ErrorPayload(code: Int, message: String, data: Option[Payload]) extends Throwable {
7+
override def getMessage(): String = message
8+
}
79

810
private[jsonrpclib] object ErrorPayload {
911

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package jsonrpclib
2+
3+
/** Errors that should not be sent back through the json rpc channel (such as invalid notifications)
4+
*/
5+
case class ErrorReport(method: String, payload: Payload, error: ProtocolError)

core/src/jsonrpclib/Monadic.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package jsonrpclib
2+
3+
import scala.concurrent.Future
4+
import scala.concurrent.ExecutionContext
5+
6+
trait Monadic[F[_]] {
7+
def doFlatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
8+
def doPure[A](a: A): F[A]
9+
def doAttempt[A](fa: F[A]): F[Either[Throwable, A]]
10+
}
11+
12+
object Monadic {
13+
implicit def monadicFuture(implicit ec: ExecutionContext): Monadic[Future] = new Monadic[Future] {
14+
def doFlatMap[A, B](fa: Future[A])(f: A => Future[B]): Future[B] = fa.flatMap(f)
15+
16+
def doPure[A](a: A): Future[A] = Future.successful(a)
17+
18+
def doAttempt[A](fa: Future[A]): Future[Either[Throwable, A]] = fa.map(Right(_)).recover(Left(_))
19+
}
20+
}

0 commit comments

Comments
 (0)