diff --git a/build.sbt b/build.sbt index 860726d..3a1fab7 100644 --- a/build.sbt +++ b/build.sbt @@ -33,7 +33,7 @@ val Versions = new { val scalaVersions = Seq(Scala3) val fansi = "0.5.0" val jna = "5.14.0" - val catsEffect = "3.6.0" + val catsEffect = "3.7.0" val osLib = "0.11.3" val scalaJavaTime = "2.6.0" } @@ -126,7 +126,7 @@ lazy val example = projectMatrix .nativePlatform( Versions.scalaVersions, settings = Seq( - Compile / mainClass := Some("cue4s_example.tictac"), + Compile / mainClass := Some("cue4s_example.sync"), libraryDependencies += "io.github.cquiroz" %%% "scala-java-time" % Versions.scalaJavaTime, ), ) diff --git a/modules/core/src/main/scalanative/ChangeModeMac.scala b/modules/core/src/main/scalanative/ChangeModeMac.scala index 9aef747..abc07e0 100644 --- a/modules/core/src/main/scalanative/ChangeModeMac.scala +++ b/modules/core/src/main/scalanative/ChangeModeMac.scala @@ -21,10 +21,23 @@ import cue4s.ChangeModeUnix.Termios import scalanative.unsafe.* object ChangeModeMac extends ChangeModeUnix: - // int read(int fd, byte[] buf, int count); import scalanative.posix.termios.{TCSANOW, ECHO, ICANON} - type termios = scalanative.posix.termios.termios + type tcflag_t = CLong + type cc_t = CChar + type speed_t = CLong + import scalanative.unsafe.Nat.* + type NCCS = Digit2[_2, _0] + type c_cc = CArray[cc_t, NCCS] + type termios = CStruct7[ + tcflag_t, /* c_iflag - input flags */ + tcflag_t, /* c_oflag - output flags */ + tcflag_t, /* c_cflag - control flags */ + tcflag_t, /* c_lflag - local flags */ + c_cc, /* cc_t c_cc[NCCS] - control chars */ + speed_t, /* c_ispeed - input speed */ + speed_t, /* c_ospeed - output speed */ + ] private val VTIME = 17 private val VMIN = 16 diff --git a/modules/example/src/main/scala-jvm-native/sync.scala b/modules/example/src/main/scala-jvm-native/sync.scala index 01ebfb0..9c6b48f 100644 --- a/modules/example/src/main/scala-jvm-native/sync.scala +++ b/modules/example/src/main/scala-jvm-native/sync.scala @@ -19,69 +19,104 @@ package cue4s_example import cue4s.* import cue4s.Prompt.PasswordInput.Password -@main def sync = +@main def sync(which: String*) = Prompts.sync.use: prompts => - val likeCats = prompts - .confirm("Do you like cats?") - .toOption - - val day = prompts - .singleChoice( - "How was your day?", - List( - "amazing", - "productive", - "relaxing", - "stressful", - "exhausting", - "challenging", - "wonderful", - "uneventful", - "interesting", - "exciting", - "boring", - "demanding", - "satisfying", - "frustrating", - "peaceful", - "overwhelming", - "busy", - "calm", - "enjoyable", - "memorable", - "ordinary", - "fantastic", - "rewarding", - "chaotic", - ), - _.withWindowSize(7), - ) - .toOption - - val skyColor: Completion[Boolean] = prompts.run( - Prompt - .Input("What color is the sky?") - .mapValidated: s => - Either.cond(s == "blue", true, PromptError("hint: it's blue")), + val examples = Map[String, (String, SyncPrompts => Unit)]( + "confirm" -> ("Yes/No confirmation", likeCats), + "single-choice" -> ("Single choice", day), + "multi-choice" -> ("Multiple choice", letters), + "validated-int" -> ("Validated integer", seasons), + "password" -> ("Password", password), ) - val letters: Completion[List[String]] = - prompts.multiChoiceNoneSelected( - "What are your favourite letters?", - ('A' to 'Z').map(_.toString).toList, - _.withWindowSize(7), - ) + if which.isEmpty then + val maxLength = examples.keys.map(_.length).max + + val selected = prompts + .multiChoiceNoneSelected( + "Which examples would you like to run?", + examples + .map { case (k, (desc, _)) => + s"${k.padTo(maxLength, ' ')}: $desc" + } + .toList + .sorted, + ) + .getOrThrow - val seasons: Completion[Int] = - prompts.int( - "How many seasons of Stargate SG-1 are there", - _.validate: - case x if x < 10 => Option(PromptError("More!")) - case x if x > 10 => Option(PromptError("Fewer!")) - case _ => None, - ) + val toShow = selected.map(_.split(": ").head.trim) + toShow.foreach { k => examples(k)._2(prompts) } + else + val allowed = examples.keySet + val missing = which.toSet -- allowed - val password: Completion[Password] = - prompts.password("Choose a new password") + if missing.nonEmpty then + sys.error(s"Unknown example names: ${missing + .mkString(", ")}. Available examples: ${allowed.mkString(", ")}") + else which.foreach { k => examples(k)._2(prompts) } + end if end sync + +def likeCats(prompts: SyncPrompts) = prompts + .confirm("Do you like cats?") + .toOption + +def day(prompts: SyncPrompts) = prompts + .singleChoice( + "How was your day?", + List( + "amazing", + "productive", + "relaxing", + "stressful", + "exhausting", + "challenging", + "wonderful", + "uneventful", + "interesting", + "exciting", + "boring", + "demanding", + "satisfying", + "frustrating", + "peaceful", + "overwhelming", + "busy", + "calm", + "enjoyable", + "memorable", + "ordinary", + "fantastic", + "rewarding", + "chaotic", + ), + _.withWindowSize(7), + ) + .toOption + +def skyColor(prompts: SyncPrompts): Completion[Boolean] = prompts.run( + Prompt + .Input("What color is the sky?") + .mapValidated: s => + Either.cond(s == "blue", true, PromptError("hint: it's blue")), +) + +def letters(prompts: SyncPrompts): Completion[List[String]] = + prompts.multiChoiceNoneSelected( + "What are your favourite letters?", + ('A' to 'Z').map(_.toString).toList, + _.withWindowSize(7), + ) + +def seasons(prompts: SyncPrompts): Completion[Int] = + prompts.int( + "How many seasons of Stargate SG-1 are there", + _.validate: + case x if x < 10 => Option(PromptError("More!")) + case x if x > 10 => Option(PromptError("Fewer!")) + case _ => None, + ) + +def password(prompts: SyncPrompts): Completion[Password] = + prompts.password("Choose a new password") diff --git a/project/build.properties b/project/build.properties index 01a16ed..08a6fc0 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.11.7 +sbt.version=1.12.8 diff --git a/project/plugins.sbt b/project/plugins.sbt index a644b53..e24572b 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -17,7 +17,7 @@ addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.8.1") // Scala.js and Scala Native addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.1") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.9") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.11") addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.16") @@ -25,4 +25,4 @@ addSbtPlugin("com.indoorvivants.snapshots" % "sbt-snapshots" % "0.0.11") addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.3.4") -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.0") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1")