diff options
Diffstat (limited to 'cli/src/main/scala/com/rockymadden/stringmetric/cli/package.scala')
-rwxr-xr-x | cli/src/main/scala/com/rockymadden/stringmetric/cli/package.scala | 105 |
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") + } +} |