1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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)
) {
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")
}
}
|