Scala 3 library for CLI prompts that works on JVM, JS, and Native.
The inspiration is taken from a JS library prompts, and the eventual goal is to have cue4s support all the same functionality.
CleanShot.2024-11-25.at.09.26.30.mp4
- Scala CLI:
//> using dep tech.neander::cue4s::<version>
- SBT:
libraryDependencies += "tech.neander" %%% "cue4s" % "<version>"
This example is runnable on both JVM and Native (note how we're using sync
).
In fact, if you put into a file named test.sc
you can run it with Scala CLI.
//> using dep tech.neander::cue4s::latest.release
import cue4s.*
Prompts.sync.use: prompts =>
val day = prompts
.singleChoice("How was your day?", List("great", "okay"))
.getOrThrow
val work = prompts.text("Where do you work?").getOrThrow
val letters = prompts.multiChoiceAllSelected(
"What are your favourite letters?",
('A' to 'F').map(_.toString).toList
).getOrThrow
For this to work on JS, you need to use the Future
-based methods, example for that is provided in examples folder.
cue4s includes an experimental auto-derivation for case classes (and only them, currently), allowing you to create prompt chains:
//> using tech.neander::cue4s::latest.release
import cue4s.*
val validateName: String => Option[PromptError] = s =>
Option.when(s.trim.isEmpty)(PromptError("name cannot be empty!"))
case class Attributes(
@cue(_.text("Your name").validate(validateName))
name: String,
@cue(_.text("Checklist").multi("Wake up" -> true, "Grub a brush" -> true, "Put a little makeup" -> false))
doneToday: Set[String],
@cue(_.text("What did you have for breakfast").options("eggs", "sadness"))
breakfast: String,
@cue(_.text("Do you want to build a snowman?"))
snowman: Boolean,
@cue(_.text("How old are you?"))
age: Int,
@cue(_.text("What is the value of PI?"))
pi: Float
) derives PromptChain
val attributes: Attributes =
Prompts.sync.use: p =>
p.run(PromptChain[Attributes]).getOrThrow
There is no generic mechanism to define how parameters of different types will be handled, just a set of rules that felt right at the time of writing this library:
- If the type is
String
, and.options(...)
is present in annotation, the prompt will becomeSingleChoice
- If the type is
F[String]
whereF
is one ofList, Vector, Set
, and either.options(...)
or.multi(...)
are present, then the prompt will becomeMultipleChoice
- If the type is
Option[String]
, then empty value will be turned intoNone
(check for emptiness will be run before any validation)
In the future more combinations can be added.
A simple Cats Effect integration is provided, which wraps the future-based implementation of terminal interactions.
The integration is available only for JVM and JS targets.
Installation
- Scala CLI:
//> using dep tech.neander::cue4s-cats-effect::<version>
- SBT:
libraryDependencies += "tech.neander" %%% "cue4s-cats-effect" % "<version>"
Usage
//> using dep tech.neander::cue4s-cats-effect::latest.release
import cue4s.catseffect.*
import cats.effect.*
import cats.syntax.all.*
case class Info(
day: String,
work: String,
letters: List[String],
)
object ioExample extends IOApp.Simple:
def run: IO[Unit] =
PromptsIO.make.use: prompts =>
for
_ <- IO.println("let's go")
day = prompts
.singleChoice("How was your day?", List("great", "okay"))
.map(_.toEither)
.flatMap(IO.fromEither)
work = prompts
.text("Where do you work?")
.map(_.toEither)
.flatMap(IO.fromEither)
letter = prompts
.multiChoiceAllSelected(
"What are your favourite letters?",
('A' to 'F').map(_.toString).toList,
)
.map(_.toEither)
.flatMap(IO.fromEither)
info <- (day, work, letter).mapN(Info.apply)
_ <- IO.println(info)
yield ()
end ioExample
On JS, we can only execute the prompts asynchronously, so the minimal
usable implementation of a prompt will always return Future[Completion[Result]]
.
On JVM and Native, we can execute prompts synchronously, so the simplest
implementation returns Completion[Result]
- but methods wrapping the result in Future
are provided for convenience.
This is encoded in the availablity of sync
and async
methods on the Prompts
object (e.g. only Prompts.async
is available on JS).
This library is published for Scala.js 1.16.0+, Scala Native 0.5, and JVM.