From e263bfaf7b79ef4b485835591f3eddd6b8aa0c3d Mon Sep 17 00:00:00 2001 From: Jarek Sacha Date: Mon, 15 May 2023 15:07:40 -0400 Subject: [PATCH] Add parsing of command line options --- .scalafmt.conf | 14 ++- build.sbt | 16 +++- project/build.properties | 2 +- project/plugins.sbt | 2 +- src/main/scala/Main.scala | 4 - .../imagej_launcher/ErrorCode.scala | 17 ++++ .../ij_plugins/imagej_launcher/Launcher.scala | 14 +++ .../ij_plugins/imagej_launcher/Logger.scala | 28 ++++++ .../ij_plugins/imagej_launcher/Main.scala | 92 +++++++++++++++++++ 9 files changed, 180 insertions(+), 9 deletions(-) delete mode 100644 src/main/scala/Main.scala create mode 100644 src/main/scala/ij_plugins/imagej_launcher/ErrorCode.scala create mode 100644 src/main/scala/ij_plugins/imagej_launcher/Launcher.scala create mode 100644 src/main/scala/ij_plugins/imagej_launcher/Logger.scala create mode 100644 src/main/scala/ij_plugins/imagej_launcher/Main.scala diff --git a/.scalafmt.conf b/.scalafmt.conf index 422baeb..c3aee56 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,2 +1,14 @@ -version = "3.7.3" +version = 3.7.3 + runner.dialect = scala3 + +preset = IntelliJ +align.preset = more +maxColumn = 120 +docstrings.style = Asterisk +docstrings.blankFirstLine = yes +docstrings.wrap = no +importSelectors = singleLine +newlines.source = keep + +rewrite.scala3.convertToNewSyntax = yes \ No newline at end of file diff --git a/build.sbt b/build.sbt index 2f9b4e6..29a3e7c 100644 --- a/build.sbt +++ b/build.sbt @@ -5,12 +5,24 @@ enablePlugins(ScalaNativePlugin) // set to Debug for compilation details (Info is default) logLevel := Level.Info +libraryDependencies ++= Seq( + "com.github.scopt" %%% "scopt" % "4.1.0" +) + +Compile/run/mainClass := Some("ij_plugins.imagej_launcher.Main") + // import to add Scala Native options import scala.scalanative.build._ + // defaults set with common options shown nativeConfig ~= { c => - c.withLTO(LTO.none) // thin + c.withLTO(LTO.none) // thin .withMode(Mode.debug) // releaseFast - .withGC(GC.immix) // commix + .withGC(GC.immix) // commix } + +//// Enable verbose reporting during compilation +//nativeConfig ~= { c => +// c.withCompileOptions(c.compileOptions ++ Seq("-v")) +//} diff --git a/project/build.properties b/project/build.properties index f344c14..ef3d266 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 1.8.2 +sbt.version = 1.8.3 diff --git a/project/plugins.sbt b/project/plugins.sbt index ef0f7a0..f07f01d 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.10") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.12") diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala deleted file mode 100644 index 399e5f3..0000000 --- a/src/main/scala/Main.scala +++ /dev/null @@ -1,4 +0,0 @@ -object Main { - def main(args: Array[String]): Unit = - println("Hello, world!") -} diff --git a/src/main/scala/ij_plugins/imagej_launcher/ErrorCode.scala b/src/main/scala/ij_plugins/imagej_launcher/ErrorCode.scala new file mode 100644 index 0000000..16b20a9 --- /dev/null +++ b/src/main/scala/ij_plugins/imagej_launcher/ErrorCode.scala @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2000-2023 Jarek Sacha. All Rights Reserved. + * Author's e-mail: jpsacha at gmail.com + */ +package ij_plugins.imagej_launcher + +import scala.collection.immutable + +enum ErrorCode(val value: Int, val message: String): + case OK extends ErrorCode(0, "OK") + case InvalidCommandLineArguments extends ErrorCode(-10, "Invalid command line arguments") + case GeneralError extends ErrorCode(-100, "General error") + case UnhandledNonFatalError extends ErrorCode(-200, "Unhandled non-fatal error") + case UnhandledFatalError extends ErrorCode(-300, "Unhandled fatal error") + case NotImplemented extends ErrorCode(-400, "Functionality not implemented") + + override def toString: String = s"$message [exit code: $value]" diff --git a/src/main/scala/ij_plugins/imagej_launcher/Launcher.scala b/src/main/scala/ij_plugins/imagej_launcher/Launcher.scala new file mode 100644 index 0000000..eb94bb4 --- /dev/null +++ b/src/main/scala/ij_plugins/imagej_launcher/Launcher.scala @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2000-2023 Jarek Sacha. All Rights Reserved. + * Author's e-mail: jpsacha at gmail.com + */ + +package ij_plugins.imagej_launcher + +import ij_plugins.imagej_launcher.Main.Config + +class Launcher(logger: Logger) { + def run(config: Config): ErrorCode = { + ErrorCode.NotImplemented + } +} diff --git a/src/main/scala/ij_plugins/imagej_launcher/Logger.scala b/src/main/scala/ij_plugins/imagej_launcher/Logger.scala new file mode 100644 index 0000000..3d8c94b --- /dev/null +++ b/src/main/scala/ij_plugins/imagej_launcher/Logger.scala @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2000-2023 Jarek Sacha. All Rights Reserved. + * Author's e-mail: jpsacha at gmail.com + */ + +package ij_plugins.imagej_launcher + +import ij_plugins.imagej_launcher.Logger.Level + +class Logger { + + var level: Level = Level.Info + + def debug(msg: String): Unit = pprint(Level.Debug, msg) + def info(msg: String): Unit = pprint(Level.Info, msg) + def error(msg: String): Unit = pprint(Level.Error, msg) + + private def pprint(l: Level, message: String): Unit = + if l.level <= level.level then println(s"${l.name}: $message") +} + +object Logger: + enum Level(val level: Int, val name: String): + case Off extends Level(0, "OFF") + case Error extends Level(200, "ERROR") + case Info extends Level(400, "INFO") + case Debug extends Level(500, "DEBUG") + case All extends Level(Int.MaxValue, "DEBUG") diff --git a/src/main/scala/ij_plugins/imagej_launcher/Main.scala b/src/main/scala/ij_plugins/imagej_launcher/Main.scala new file mode 100644 index 0000000..0332ed6 --- /dev/null +++ b/src/main/scala/ij_plugins/imagej_launcher/Main.scala @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2000-2023 Jarek Sacha. All Rights Reserved. + * Author's e-mail: jpsacha at gmail.com + */ + +package ij_plugins.imagej_launcher + +import ij_plugins.imagej_launcher.ErrorCode +import scopt.{DefaultOEffectSetup, OParser} + +import java.io.File + +object Main { + private val logger = new Logger() + private val AppName = "ijp-imagej-launcher" + // private val AppVersion = s"${Version.version} [${Version.buildTimeStr}]" + private val AppVersion = s"0.1.0" + private val VersionMessage = s"v.$AppVersion" + private val AppDescription = + """Native launcher for ImageJ2 + |""".stripMargin + + case class Config( + logLevel: Logger.Level = Logger.Level.Error, + dryRun: Boolean = false, + javaHome: Option[File] = None + ) + + def main(args: Array[String]): Unit = + setupLogger(Logger.Level.All) + + val ret: ErrorCode = + parseCommandLine(args) match + case Some(config) => + setupLogger(config.logLevel) + runLauncher(config) + case None => + // arguments are bad + ErrorCode.InvalidCommandLineArguments + + val msg = s"${ret.message} [exit code: ${ret.value}]" + if ret == ErrorCode.OK then + logger.info(msg) + else + logger.error(msg) + +// System.exit(ret.value) + end main + + private def parseCommandLine(args: Array[String]): Option[Config] = + val builder = OParser.builder[Config] + val parser1 = { + import builder.* + OParser.sequence( + programName(AppName), + head(AppName, VersionMessage), + note(AppDescription), + // + help('h', "help").text("prints this usage text"), + // + version("version").text("prints version"), + // + opt[Unit]("dry-run") + .action((_, c) => c.copy(dryRun = true)) + .text("show the command line, but do not run anything"), + // + opt[Unit]("info") + .action((_, c) => c.copy(logLevel = Logger.Level.Info)) + .text("informational output"), + // + opt[Unit]("debug") + .action((_, c) => c.copy(logLevel = Logger.Level.Debug)) + .text("verbose output"), + // + opt[File]("java-home") + .valueName("") + .action((path, c) => c.copy(javaHome = Option(path))) + .text("specify JAVA_HOME explicitly"), + // + opt[Unit]("print-java-home") + .action((_, c) => c.copy(dryRun = true)) + .text("print ImageJ's idea of JAVA_HOME") + ) + } + + OParser.parse(parser1, args, Config()) + end parseCommandLine + + private def setupLogger(logLevel: Logger.Level): Unit = logger.level = logLevel + + private def runLauncher(config: Config): ErrorCode = new Launcher(logger).run(config) +}