@@ -14,37 +14,43 @@ import jsonrpclib.internals.MessageDispatcher
1414import jsonrpclib .internals ._
1515
1616import scala .util .Try
17+ import _root_ .fs2 .concurrent .SignallingRef
1718
1819trait FS2Channel [F [_]] extends Channel [F ] {
1920 def withEndpoint (endpoint : Endpoint [F ])(implicit F : Functor [F ]): Resource [F , Unit ] =
2021 Resource .make(mountEndpoint(endpoint))(_ => unmountEndpoint(endpoint.method))
2122
2223 def withEndpoints (endpoint : Endpoint [F ], rest : Endpoint [F ]* )(implicit F : Monad [F ]): Resource [F , Unit ] =
2324 (endpoint :: rest.toList).traverse_(withEndpoint)
25+
26+ def open : Resource [F , Unit ]
27+ def openStream : Stream [F , Unit ]
2428}
2529
2630object FS2Channel {
2731
2832 def lspCompliant [F [_]: Concurrent ](
2933 byteStream : Stream [F , Byte ],
3034 byteSink : Pipe [F , Byte , Nothing ],
31- startingEndpoints : List [Endpoint [F ]] = List .empty,
3235 bufferSize : Int = 512
3336 ): Stream [F , FS2Channel [F ]] = internals.LSP .writeSink(byteSink, bufferSize).flatMap { sink =>
34- apply[F ](internals.LSP .readStream(byteStream), sink, startingEndpoints )
37+ apply[F ](internals.LSP .readStream(byteStream), sink)
3538 }
3639
3740 def apply [F [_]: Concurrent ](
3841 payloadStream : Stream [F , Payload ],
39- payloadSink : Payload => F [Unit ],
40- startingEndpoints : List [Endpoint [F ]] = List .empty[Endpoint [F ]]
42+ payloadSink : Payload => F [Unit ]
4143 ): Stream [F , FS2Channel [F ]] = {
42- val endpointsMap = startingEndpoints.map(ep => ep.method -> ep).toMap
4344 for {
4445 supervisor <- Stream .resource(Supervisor [F ])
45- ref <- Ref [F ].of(State [F ](Map .empty, endpointsMap, 0 )).toStream
46- impl = new Impl (payloadSink, ref, supervisor)
47- _ <- Stream (()).concurrently(payloadStream.evalMap(impl.handleReceivedPayload))
46+ ref <- Ref [F ].of(State [F ](Map .empty, Map .empty, 0 )).toStream
47+ isOpen <- SignallingRef [F ].of(false ).toStream
48+ awaitingSink = isOpen.waitUntil(identity) >> payloadSink(_ : Payload )
49+ impl = new Impl (awaitingSink, ref, isOpen, supervisor)
50+ _ <- Stream (()).concurrently {
51+ // Gatekeeping the pull until the channel is actually marked as open
52+ payloadStream.pauseWhen(isOpen.map(b => ! b)).evalMap(impl.handleReceivedPayload)
53+ }
4854 } yield impl
4955 }
5056
@@ -72,6 +78,7 @@ object FS2Channel {
7278 private class Impl [F [_]](
7379 private val sink : Payload => F [Unit ],
7480 private val state : Ref [F , FS2Channel .State [F ]],
81+ private val isOpen : SignallingRef [F , Boolean ],
7582 supervisor : Supervisor [F ]
7683 )(implicit F : Concurrent [F ])
7784 extends MessageDispatcher [F ]
@@ -88,6 +95,9 @@ object FS2Channel {
8895
8996 def unmountEndpoint (method : String ): F [Unit ] = state.update(_.removeEndpoint(method))
9097
98+ def open : Resource [F , Unit ] = Resource .make[F , Unit ](isOpen.set(true ))(_ => isOpen.set(false ))
99+ def openStream : Stream [F , Unit ] = Stream .resource(open)
100+
91101 protected def background [A ](fa : F [A ]): F [Unit ] = supervisor.supervise(fa).void
92102 protected def reportError (params : Option [Payload ], error : ProtocolError , method : String ): F [Unit ] = ???
93103 protected def getEndpoint (method : String ): F [Option [Endpoint [F ]]] = state.get.map(_.endpoints.get(method))
0 commit comments