Search code examples
scalaiteratorstdin

Converting disrete chunks of Stdin to usable form


Simply, if the user pastes a chunk of text (multiple lines) into the console all at once, I want to be able to grab that chunk and use it.

Currently my code is

val stringLines: List[String] = io.Source.stdin.getLines().toList
doStuff(stringLines)

However doStuff is never called. I realize the stdin iterator doesn't have an 'end', but how do I get the input as it is currently? I've checked many SO answers, but none of them work for multiple lines that need to be collated. I need to have all the lines of the user input at once, and it will always come as a single paste of data.


Solution

  • This is a rough outline, but it seems to work. I had to delve into Java Executors, which I hadn't encountered before, and I might not be using correctly.

    You might want to play around with the timeout value, but 10 milliseconds passes my tests.

    import java.util.concurrent.{Callable, Executors, TimeUnit}
    import scala.util.{Failure, Success, Try}
    
    def getChunk: List[String] = {  // blocks until StdIn has data
      val executor = Executors.newSingleThreadExecutor()
      val callable = new Callable[String]() {
        def call(): String = io.StdIn.readLine()
      }
    
      def nextLine(acc: List[String]): List[String] =
        Try {executor.submit(callable).get(10L, TimeUnit.MILLISECONDS)} match {
          case Success(str) => nextLine(str :: acc)
          case Failure(_)   => executor.shutdownNow() // should test for Failure type
                               acc.reverse
        }
    
      nextLine(List(io.StdIn.readLine()))  // this is the blocking part
    }
    

    Usage is quite simple.

    val input: List[String] = getChunk