Created
May 1, 2020 08:57
-
-
Save Slakah/9b905b0fd20b715c28862da913ff73c5 to your computer and use it in GitHub Desktop.
cats-effect support for case-app
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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