@@ -14,37 +14,43 @@ import jsonrpclib.internals.MessageDispatcher
14
14
import jsonrpclib .internals ._
15
15
16
16
import scala .util .Try
17
+ import _root_ .fs2 .concurrent .SignallingRef
17
18
18
19
trait FS2Channel [F [_]] extends Channel [F ] {
19
20
def withEndpoint (endpoint : Endpoint [F ])(implicit F : Functor [F ]): Resource [F , Unit ] =
20
21
Resource .make(mountEndpoint(endpoint))(_ => unmountEndpoint(endpoint.method))
21
22
22
23
def withEndpoints (endpoint : Endpoint [F ], rest : Endpoint [F ]* )(implicit F : Monad [F ]): Resource [F , Unit ] =
23
24
(endpoint :: rest.toList).traverse_(withEndpoint)
25
+
26
+ def open : Resource [F , Unit ]
27
+ def openStream : Stream [F , Unit ]
24
28
}
25
29
26
30
object FS2Channel {
27
31
28
32
def lspCompliant [F [_]: Concurrent ](
29
33
byteStream : Stream [F , Byte ],
30
34
byteSink : Pipe [F , Byte , Nothing ],
31
- startingEndpoints : List [Endpoint [F ]] = List .empty,
32
35
bufferSize : Int = 512
33
36
): 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)
35
38
}
36
39
37
40
def apply [F [_]: Concurrent ](
38
41
payloadStream : Stream [F , Payload ],
39
- payloadSink : Payload => F [Unit ],
40
- startingEndpoints : List [Endpoint [F ]] = List .empty[Endpoint [F ]]
42
+ payloadSink : Payload => F [Unit ]
41
43
): Stream [F , FS2Channel [F ]] = {
42
- val endpointsMap = startingEndpoints.map(ep => ep.method -> ep).toMap
43
44
for {
44
45
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
+ }
48
54
} yield impl
49
55
}
50
56
@@ -72,6 +78,7 @@ object FS2Channel {
72
78
private class Impl [F [_]](
73
79
private val sink : Payload => F [Unit ],
74
80
private val state : Ref [F , FS2Channel .State [F ]],
81
+ private val isOpen : SignallingRef [F , Boolean ],
75
82
supervisor : Supervisor [F ]
76
83
)(implicit F : Concurrent [F ])
77
84
extends MessageDispatcher [F ]
@@ -88,6 +95,9 @@ object FS2Channel {
88
95
89
96
def unmountEndpoint (method : String ): F [Unit ] = state.update(_.removeEndpoint(method))
90
97
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
+
91
101
protected def background [A ](fa : F [A ]): F [Unit ] = supervisor.supervise(fa).void
92
102
protected def reportError (params : Option [Payload ], error : ProtocolError , method : String ): F [Unit ] = ???
93
103
protected def getEndpoint (method : String ): F [Option [Endpoint [F ]]] = state.get.map(_.endpoints.get(method))
0 commit comments