Skip to content

Instantly share code, notes, and snippets.

@TonioGela
Last active June 14, 2023 01:42
Show Gist options
  • Save TonioGela/46f45f08d9f666d391a763e6cef5bfce to your computer and use it in GitHub Desktop.
Save TonioGela/46f45f08d9f666d391a763e6cef5bfce to your computer and use it in GitHub Desktop.
//> using scala "3.2.1"
//> using lib "com.monovore::decline-effect::2.4.1"
//> using lib "co.fs2::fs2-io::3.4.0"
//> using packaging.output "mkString"
//> using platform "scala-native"
//> using nativeMode "release-fast"
//> using nativeGc "none"
import cats.effect.*
import cats.effect.std.*
import cats.syntax.all.*
import com.monovore.decline.*
import com.monovore.decline.effect.*
import fs2.*
import fs2.io.*
import cats.data.NonEmptyList
val prefix: Opts[String] = Opts.option("prefix", "The prefix").withDefault("")
val delimiter: Opts[String] = Opts
.option[String]("delimiter", "Delimiter to place between the elements", "d")
.withDefault(",")
val suffix: Opts[String] = Opts.option("suffix", "The suffix").withDefault("")
val argument: Opts[String] = Opts.argument[String]("input").withDefault("--")
val chunkSize: Opts[Option[Int]] =
Opts.option[Int]("chunkSize", "Grouping size").orNone
def inputStream[F[_]: Sync](input: String): Stream[F, String] =
if input =!= "--" then Stream.emits(input.split("\n"))
else stdinUtf8[F](1024 * 1024 * 10)
.repartition(s => Chunk.array(s.split("\n", -1)))
.filterNot(_.isEmpty)
def composeString[F[_]: Sync](pre: String, delim: String, post: String)(
s: Stream[F, String]
): Stream[F, String] =
Stream.emit(pre) ++ s.intersperse(delim) ++ Stream.emit(post)
def streamArg[F[_]: Sync]: Opts[Stream[F, String]] =
(prefix, delimiter, suffix, argument, chunkSize).mapN {
(pre, delim, post, input, chunk) =>
val stringStream: Stream[F, String] = inputStream(input)
val pipe: Pipe[F, String, String] = composeString(pre, delim, post)
val unchunkedStream: Stream[F, String] = stringStream.through(pipe)
val chunkedStream: Int => Stream[F, String] = n => stringStream.chunkN(n)
.flatMap(c => Stream.chunk(c).through(pipe).append(Stream.emit("\n"))).append(Stream.emit("\n"))
chunk.fold(unchunkedStream)(chunkedStream)
}
def printStream[F[_]: Console: Sync](stream: Stream[F, String]): F[ExitCode] =
stream.foreach(Console[F].print).compile.drain.as(ExitCode.Success)
abstract class CommandStreamApp extends CommandIOApp(
name = "mkString",
header = """|Builds a string from a list of strings.
|Reads from stdin if no argument or -- is used""".stripMargin
)
object Main extends CommandStreamApp {
def main = streamArg[IO].map(printStream[IO])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment