@@ -11,11 +11,10 @@ import cats.syntax.all._
1111import fs2 .Stream
1212import jsonrpclib .StubTemplate
1313import cats .effect .std .Dispatcher
14- import scala .sys .process .ProcessIO
1514import cats .effect .implicits ._
16- import scala .sys .process .{Process => SProcess }
1715import java .io .OutputStream
1816import java .io .InputStream
17+ import examples .client .ChildProcess
1918
2019object ClientMain extends IOApp .Simple {
2120
@@ -38,69 +37,22 @@ object ClientMain extends IOApp.Simple {
3837 _ <- log(" Starting client" )
3938 serverJar <- sys.env.get(" SERVER_JAR" ).liftTo[IOStream ](new Exception (" SERVER_JAR env var does not exist" ))
4039 // Starting the server
41- (serverStdin, serverStdout, serverStderr) <- Stream .resource(process(" java" , " -jar" , serverJar))
42- pipeErrors = serverStderr.through(fs2.io.stderr)
40+ rp <- ChildProcess .spawn[IO ](" java" , " -jar" , serverJar)
4341 // Creating a channel that will be used to communicate to the server
4442 fs2Channel <- FS2Channel
45- .lspCompliant[IO ](serverStdout, serverStdin , cancelTemplate = cancelEndpoint.some)
46- .concurrently(pipeErrors )
43+ .lspCompliant[IO ](rp.stdout, rp.stdin , cancelTemplate = cancelEndpoint.some)
44+ .concurrently(rp.stderr.through(fs2.io.stderr) )
4745 // Opening the stream to be able to send and receive data
4846 _ <- fs2Channel.openStream
4947 // Creating a `IntWrapper => IO[IntWrapper]` stub that can call the server
5048 increment = fs2Channel.simpleStub[IntWrapper , IntWrapper ](" increment" )
51- result <- Stream .eval(increment(IntWrapper (0 )))
52- _ <- log(s " Client received $result" )
49+ result1 <- Stream .eval(increment(IntWrapper (0 )))
50+ _ <- log(s " Client received $result1" )
51+ result2 <- Stream .eval(increment(result1))
52+ _ <- log(s " Client received $result2" )
5353 _ <- log(" Terminating client" )
5454 } yield ()
55- run.compile.drain.timeout( 2 .second)
55+ run.compile.drain
5656 }
5757
58- /** Wraps the spawning of a subprocess into fs2 friendly semantics
59- */
60- import scala .concurrent .duration ._
61- def process (command : String * ) = for {
62- dispatcher <- Dispatcher [IO ]
63- stdinPromise <- IO .deferred[fs2.Pipe [IO , Byte , Unit ]].toResource
64- stdoutPromise <- IO .deferred[fs2.Stream [IO , Byte ]].toResource
65- stderrPromise <- IO .deferred[fs2.Stream [IO , Byte ]].toResource
66- makeProcessBuilder = IO (sys.process.stringSeqToProcess(command))
67- makeProcessIO = IO (
68- new ProcessIO (
69- in = { (outputStream : OutputStream ) =>
70- val pipe = writeOutputStreamFlushingChunks(IO (outputStream))
71- val fulfil = stdinPromise.complete(pipe)
72- dispatcher.unsafeRunSync(fulfil)
73- },
74- out = { (inputStream : InputStream ) =>
75- val stream = fs2.io.readInputStream(IO (inputStream), 512 )
76- val fulfil = stdoutPromise.complete(stream)
77- dispatcher.unsafeRunSync(fulfil)
78- },
79- err = { (inputStream : InputStream ) =>
80- val stream = fs2.io.readInputStream(IO (inputStream), 512 )
81- val fulfil = stderrPromise.complete(stream)
82- dispatcher.unsafeRunSync(fulfil)
83- }
84- )
85- )
86- makeProcess = (makeProcessBuilder, makeProcessIO).flatMapN { case (b, io) => IO .blocking(b.run(io)) }
87- _ <- Resource .make(makeProcess)((runningProcess) => IO .blocking(runningProcess.destroy()))
88- pipes <- (stdinPromise.get, stdoutPromise.get, stderrPromise.get).tupled.toResource
89- } yield pipes
90-
91- /** Adds a flush after each chunk
92- */
93- def writeOutputStreamFlushingChunks [F [_]](
94- fos : F [OutputStream ],
95- closeAfterUse : Boolean = true
96- )(implicit F : Sync [F ]): fs2.Pipe [F , Byte , Nothing ] =
97- s => {
98- def useOs (os : OutputStream ): Stream [F , Nothing ] =
99- s.chunks.foreach(c => F .interruptible(os.write(c.toArray)) >> F .blocking(os.flush()))
100-
101- val os =
102- if (closeAfterUse) Stream .bracket(fos)(os => F .blocking(os.close()))
103- else Stream .eval(fos)
104- os.flatMap(os => useOs(os) ++ Stream .exec(F .blocking(os.flush())))
105- }
10658}
0 commit comments