summaryrefslogtreecommitdiff
path: root/cli/src/main/scala/com/rockymadden/stringmetric/cli/package.scala
diff options
context:
space:
mode:
Diffstat (limited to 'cli/src/main/scala/com/rockymadden/stringmetric/cli/package.scala')
-rwxr-xr-xcli/src/main/scala/com/rockymadden/stringmetric/cli/package.scala105
1 files changed, 105 insertions, 0 deletions
diff --git a/cli/src/main/scala/com/rockymadden/stringmetric/cli/package.scala b/cli/src/main/scala/com/rockymadden/stringmetric/cli/package.scala
new file mode 100755
index 0000000..617f4a5
--- /dev/null
+++ b/cli/src/main/scala/com/rockymadden/stringmetric/cli/package.scala
@@ -0,0 +1,105 @@
+package com.rockymadden.stringmetric
+
+/**
+ * Provides core CLI functionality. Note that some things might look sloppy (e.g. access modifiers, broad imports,
+ * repetitive imports, etc), but are required because of the way scalascript is ultimately compiled.
+ */
+package object cli {
+ import scala.language.implicitConversions
+
+
+ implicit def optionStringToArray(os: OptionString): Array[String] =
+ if (os.get.length == 0) Array.empty[String]
+ else os.get.split(" ")
+ implicit def optionStringToBigDecimal(os: OptionString): BigDecimal = BigDecimal(os.get)
+ implicit def optionStringToBigInt(os: OptionString): BigInt = BigInt(os.get)
+ implicit def optionStringToDouble(os: OptionString): Double = os.get.toDouble
+ implicit def optionStringToFloat(os: OptionString): Float = os.get.toFloat
+ implicit def optionStringToInt(os: OptionString): Int = os.get.toInt
+ implicit def optionStringToLong(os: OptionString): Long = os.get.toLong
+ implicit def optionStringToShort(os: OptionString): Short = os.get.toShort
+ implicit def optionStringToString(os: OptionString): String = os.get
+ implicit def stringToOptionString(s: String): OptionString = OptionString(s)
+ implicit def arrayOfStringToOptionMap(stringArray: Array[String]): OptionMap = OptionMap(stringArray)
+
+
+ final val Ls = sys.props("line.separator")
+ final val Tab = " "
+
+
+ class OptionString(val get: String)
+
+ object OptionString {
+ def apply(s: String): OptionString = new OptionString(s)
+ }
+
+
+ type OptionMap = Map[Symbol, OptionString]
+
+ object OptionMap {
+ def apply(args: Array[String]): OptionMap = apply(args: _*)
+
+ def apply(varargs: String*): OptionMap = {
+ @annotation.tailrec
+ def next(om: OptionMap, a: List[String]): OptionMap = {
+ val double = """^(--[a-zA-Z0-9]+)(=[a-zA-Z0-9\.\-_]+)?""".r
+ val single = """^(-[a-zA-Z0-9]+)(=[a-zA-Z0-9\.\-_]+)?""".r
+ val less = """([a-zA-Z0-9/\-_\$\.]+)""".r
+
+ a match {
+ // Empty, return.
+ case Nil => om
+ // Double dash options without value.
+ case double(k, null) :: t => next(om + (Symbol(k.tail.tail) -> ""), t)
+ // Double dash options with value.
+ case double(k, v) :: t => next(om + (Symbol(k.tail.tail) -> v.tail), t)
+ // Single dash options without value.
+ case single(k, null) :: t => next(om + (Symbol(k.tail) -> ""), t)
+ // Single dash options with value. Value is discarded.
+ case single(k, v) :: t => next(om + (Symbol(k.tail) -> ""), t)
+ // Dashless options.
+ case less(v) :: t if v.head != '-' =>
+ if (om.contains('dashless))
+ next((om - 'dashless) + ('dashless -> (om('dashless).get + " " + v.trim)), t)
+ else next(om + ('dashless -> v.trim), t)
+ // Invalid option, ignore.
+ case _ :: t => next(om, t)
+ }
+ }
+
+ next(Map.empty[Symbol, OptionString], varargs.toList)
+ }
+ }
+
+
+ abstract class Command(
+ protected val help: (OptionMap => String),
+ protected val predicate: (OptionMap => Boolean),
+ protected val execute: (OptionMap => String)
+ ) {
+ final def main(args: Array[String]): Unit = {
+ val opts: OptionMap = args
+
+ try
+ if (opts.contains('h) || opts.contains('help)) {
+ println(help(opts))
+ exit(opts)
+ } else if (predicate(opts)) {
+ println(execute(opts))
+ exit(opts)
+ } else throw new IllegalArgumentException("Expected valid syntax. See --help.")
+ catch { case e: Throwable => error(e, opts) }
+ }
+
+ private def error(error: Throwable, opts: OptionMap): Unit =
+ if (!isUnitTest(opts)) {
+ println(error.getMessage)
+ sys.exit(1)
+ } else throw error
+
+ private def exit(opts: OptionMap): Unit = if (!isUnitTest(opts)) sys.exit(0)
+
+ private def isUnitTest(opts: OptionMap) =
+ opts.contains('ut) || (opts.contains('unitTest) && opts.get('unitTest) != "false")
+ }
+}