From 06578d6a9627b5111027f2b7fc56c2d3180d3c92 Mon Sep 17 00:00:00 2001
From: Lorenzo Gabriele <lorenzolespaul@gmail.com>
Date: Wed, 29 May 2024 11:52:18 +0200
Subject: [PATCH] Expose `command: Command` in `CommandIOApp`

If you want to show the help when handling errors in the `IO` part of
the app, you need to call `.showHelp` on the command instance.
This is not possible with `CommandIOApp` since it doesn't expose the
app but creates and uses it on the fly.
This PR adds a `val command: Command[IO[ExitCode]]` to `CommandIOApp`
so you can have similar errors to the errors printed by `validate` but
when handling IO errors in the app with `.handleErrorWith` in the
application logic
---
 .../decline/effect/CommandIOAppSpec.scala     | 12 +++++++++++
 .../decline/effect/CommandIOApp.scala         | 20 +++++++++++++++++--
 2 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/effect/jvm/src/test/scala/com/monovore/decline/effect/CommandIOAppSpec.scala b/effect/jvm/src/test/scala/com/monovore/decline/effect/CommandIOAppSpec.scala
index a392b287..918ab01b 100644
--- a/effect/jvm/src/test/scala/com/monovore/decline/effect/CommandIOAppSpec.scala
+++ b/effect/jvm/src/test/scala/com/monovore/decline/effect/CommandIOAppSpec.scala
@@ -24,6 +24,18 @@ class CommandIOAppSpec extends AnyFlatSpec with Matchers {
     runApp() shouldBe ExitCode.Error
   }
 
+  it should "expose command so showHelp can be called" in {
+    PureHelloWorld.command.showHelp shouldBe """Usage: pure-hello <to-greet>
+   |
+   |Pure Hello World with Decline
+   |
+   |Options and flags:
+   |    --help
+   |        Display this help text.
+   |    --version, -v
+   |        Print the version number and exit.""".stripMargin
+  }
+
   private[this] def runApp(args: String*): ExitCode =
     PureHelloWorld.run(args.toList).unsafeRunSync()(IORuntime.global)
 
diff --git a/effect/shared/src/main/scala/com/monovore/decline/effect/CommandIOApp.scala b/effect/shared/src/main/scala/com/monovore/decline/effect/CommandIOApp.scala
index 26dfa8af..ebec921f 100644
--- a/effect/shared/src/main/scala/com/monovore/decline/effect/CommandIOApp.scala
+++ b/effect/shared/src/main/scala/com/monovore/decline/effect/CommandIOApp.scala
@@ -18,9 +18,17 @@ abstract class CommandIOApp(
 
   def main: Opts[IO[ExitCode]]
 
+  val command: Command[IO[ExitCode]] =
+    CommandIOApp.createCommand(
+      name = name,
+      header = header,
+      helpFlag = helpFlag,
+      version = Option(version).filter(_.nonEmpty),
+      main
+    )
+
   override final def run(args: List[String]): IO[ExitCode] =
-    CommandIOApp
-      .run[IO](name, header, helpFlag, Option(version).filter(_.nonEmpty))(main, args)
+    CommandIOApp.run[IO](command, args)
 
 }
 
@@ -54,6 +62,14 @@ object CommandIOApp {
       else ExitCode.Success
     }
 
+  private[CommandIOApp] def createCommand(
+      name: String,
+      header: String,
+      helpFlag: Boolean,
+      version: Option[String],
+      opts: Opts[IO[ExitCode]]
+  ) = Command(name, header, helpFlag)(version.map(addVersionFlag(opts)).getOrElse(opts))
+
   private[CommandIOApp] def addVersionFlag[F[_]: Console: Functor](
       opts: Opts[F[ExitCode]]
   )(version: String): Opts[F[ExitCode]] = {