Skip to content

Instantly share code, notes, and snippets.

@Slakah
Created May 1, 2020 08:57
Show Gist options
  • Save Slakah/9b905b0fd20b715c28862da913ff73c5 to your computer and use it in GitHub Desktop.
Save Slakah/9b905b0fd20b715c28862da913ff73c5 to your computer and use it in GitHub Desktop.
cats-effect support for case-app
package com.gubbns
import caseapp.core.Error
import caseapp.core.help.{Help, WithHelp}
import caseapp.core.parser.Parser
import caseapp.core.RemainingArgs
import cats.effect.{ExitCode, IO, IOApp}
/** IO version of CaseApp. */
abstract class IOCaseApp[T](implicit val parser: Parser[T], val messages: Help[T]) extends IOApp {
def run(options: T, remainingArgs: RemainingArgs): IO[ExitCode]
def error(message: Error): IO[ExitCode] = IO {
Console.err.println(message.message)
ExitCode(1)
}
def helpAsked: IO[ExitCode] = IO {
println(messages.withHelp.help)
ExitCode.Success
}
def usageAsked: IO[ExitCode] = IO {
println(messages.withHelp.usage)
ExitCode.Success
}
/**
* Arguments are expanded then parsed. By default, argument expansion is the identity function.
* Overriding this method allows plugging in an arbitrary argument expansion logic.
*
* One such expansion logic involves replacing each argument of the form '@<file>' with the
* contents of that file where each line in the file becomes a distinct argument.
* To enable this behavior, override this method as shown below.
* @example
* {{{
* import caseapp.core.parser.PlatformArgsExpander
* override def expandArgs(args: List[String]): List[String]
* = PlatformArgsExpander.expand(args)
* }}}
*
* @param args
* @return
*/
def expandArgs(args: List[String]): List[String] = args
override def run(args: List[String]): IO[ExitCode] =
parser.withHelp.detailedParse(expandArgs(args)) match {
case Left(err) =>
error(err)
case Right((WithHelp(true, _, _), _)) =>
usageAsked
case Right((WithHelp(_, true, _), _)) =>
helpAsked
case Right((WithHelp(_, _, t), remainingArgs)) =>
t.fold(
error,
run(_, remainingArgs)
)
}
}
package com.gubbns
import caseapp.core.Error
import caseapp.core.help.{CommandsHelp, Help, WithHelp}
import caseapp.core.parser.Parser
import caseapp.core.RemainingArgs
import cats.effect.{ExitCode, IO, IOApp}
import caseapp.core.commandparser.CommandParser
/** IO version of CommandCaseApp. */
@SuppressWarnings(Array("DisableSyntax.valInAbstract"))
abstract class IOCommandCaseApp[T](
implicit val beforeCommandParser: Parser[None.type],
val commandParser: CommandParser[T],
val commandsMessages: CommandsHelp[T]
) extends IOApp {
protected def run(options: T, remainingArgs: RemainingArgs): IO[ExitCode]
private def error(message: Error): IO[ExitCode] = IO {
Console.err.println(message.message)
ExitCode.Error
}
private def beforeCommandMessages: Help[None.type] = {
Help[None.type]
.withAppName(appName)
.withAppVersion(appVersion)
.withProgName(progName)
.withOptionsDesc(s"[options] [command] [command-options]")
.asInstanceOf[Help[None.type]]
}
private lazy val commands: Seq[String] = CommandsHelp[T].messages.flatMap(_._1)
private def helpAsked(): IO[ExitCode] = IO {
print(beforeCommandMessages.help)
println(s"Available commands: ${commands.mkString(", ")}\n")
println(s"Type $progName command --help for help on an individual command")
ExitCode.Success
}
private def commandHelpAsked(command: Seq[String]): IO[ExitCode] = IO {
println(commandsMessages.messagesMap(command).helpMessage(beforeCommandMessages.progName, command))
ExitCode.Success
}
private def usageAsked(): IO[ExitCode] = IO {
println(beforeCommandMessages.usage)
println(s"Available commands: ${commands.mkString(", ")}\n")
println(s"Type $progName command --usage for usage of an individual command")
ExitCode.Success
}
private def commandUsageAsked(command: Seq[String]): IO[ExitCode] = IO {
println(commandsMessages.messagesMap(command).usageMessage(beforeCommandMessages.progName, command))
ExitCode.Success
}
val appName: String = Help[None.type].appName
val appVersion: String = Help[None.type].appVersion
val progName: String = Help[None.type].progName
override def run(args: List[String]): IO[ExitCode] = {
commandParser.withHelp.detailedParse(args.toVector)(beforeCommandParser.withHelp) match {
case Left(err) =>
error(err)
case Right((WithHelp(true, _, _), _, _)) =>
usageAsked()
case Right((WithHelp(_, true, _), _, _)) =>
helpAsked()
case Right((_, _, optCmd)) =>
optCmd
.map {
case Left(err) =>
error(err)
case Right((c, WithHelp(true, _, _), _)) =>
commandUsageAsked(c)
case Right((c, WithHelp(_, true, _), _)) =>
commandHelpAsked(c)
case Right((_, WithHelp(_, _, t), commandArgs)) =>
t.fold(
error,
run(_, commandArgs)
)
}
.getOrElse(IO(ExitCode.Success))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment