Skip to content

Commit b25a9fb

Browse files
authored
Support --foo=bar syntax (#98)
Fixes #97 Relatively straightforward change. We do `head.split("=", 2) match` to see if it contains any `=`, and if so treat the portion of the string after the first `=` as the value. Added some unit test to cover both success cases and failure cases: use with short names or with flags is unsupported.
1 parent 81c7eb6 commit b25a9fb

File tree

2 files changed

+87
-11
lines changed

2 files changed

+87
-11
lines changed

mainargs/src/TokenGrouping.scala

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,53 @@ object TokenGrouping {
1818
}
1919

2020
val flatArgs = flatArgs0.toList
21-
val keywordArgMap = argSigs
21+
def makeKeywordArgMap(getNames: ArgSig => Iterable[String]) = argSigs
2222
.collect {
2323
case (a, r: TokensReader.Simple[_]) if !a.positional => a
2424
case (a, r: TokensReader.Flag) => a
2525
}
26-
.flatMap { x => (x.name.map("--" + _) ++ x.shortName.map("-" + _)).map(_ -> x) }
26+
.flatMap { x => getNames(x).map(_ -> x) }
2727
.toMap[String, ArgSig]
2828

29+
lazy val keywordArgMap = makeKeywordArgMap(
30+
x => x.name.map("--" + _) ++ x.shortName.map("-" + _)
31+
)
32+
33+
lazy val longKeywordArgMap = makeKeywordArgMap(x => x.name.map("--" + _))
34+
2935
@tailrec def rec(
3036
remaining: List[String],
3137
current: Map[ArgSig, Vector[String]]
3238
): Result[TokenGrouping[B]] = {
3339
remaining match {
3440
case head :: rest =>
41+
42+
def lookupArgMap(k: String, m: Map[String, ArgSig]): Option[(ArgSig, mainargs.TokensReader[_])] = {
43+
m.get(k).map(a => (a, a.reader))
44+
}
45+
3546
if (head.startsWith("-") && head.exists(_ != '-')) {
36-
keywordArgMap.get(head) match {
37-
case Some(cliArg: ArgSig) if cliArg.reader.isFlag =>
38-
rec(rest, Util.appendMap(current, cliArg, ""))
39-
case Some(cliArg: ArgSig) if !cliArg.reader.isLeftover =>
40-
rest match {
41-
case next :: rest2 => rec(rest2, Util.appendMap(current, cliArg, next))
42-
case Nil =>
43-
Result.Failure.MismatchedArguments(Nil, Nil, Nil, incomplete = Some(cliArg))
47+
head.split("=", 2) match{
48+
case Array(first, second) =>
49+
lookupArgMap(first, longKeywordArgMap) match {
50+
case Some((cliArg, _: TokensReader.Simple[_])) =>
51+
rec(rest, Util.appendMap(current, cliArg, second))
52+
53+
case _ => complete(remaining, current)
4454
}
4555

46-
case _ => complete(remaining, current)
56+
case _ =>
57+
lookupArgMap(head, keywordArgMap) match {
58+
case Some((cliArg, _: TokensReader.Flag)) =>
59+
rec(rest, Util.appendMap(current, cliArg, ""))
60+
case Some((cliArg, _: TokensReader.Simple[_])) =>
61+
rest match {
62+
case next :: rest2 => rec(rest2, Util.appendMap(current, cliArg, next))
63+
case Nil =>
64+
Result.Failure.MismatchedArguments(Nil, Nil, Nil, incomplete = Some(cliArg))
65+
}
66+
case _ => complete(remaining, current)
67+
}
4768
}
4869
} else {
4970
positionalArgSigs.find(!current.contains(_)) match {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package mainargs
2+
import utest._
3+
4+
object EqualsSyntaxTests extends TestSuite {
5+
6+
object Main {
7+
@main
8+
def run(
9+
@arg(short = 'f', doc = "String to print repeatedly")
10+
foo: String,
11+
@arg(name = "my-num", doc = "How many times to print string")
12+
myNum: Int = 2,
13+
@arg(doc = "Example flag")
14+
bool: Flag
15+
) = {
16+
foo * myNum + " " + bool.value
17+
}
18+
}
19+
20+
val tests = Tests {
21+
test("simple") {
22+
ParserForMethods(Main).runOrThrow(Array("--foo=bar", "--my-num=3")) ==>
23+
"barbarbar false"
24+
}
25+
test("multipleEquals") {
26+
// --foo=bar syntax still works when there's an `=` on the right
27+
ParserForMethods(Main).runOrThrow(Array("--foo=bar=qux")) ==>
28+
"bar=quxbar=qux false"
29+
}
30+
test("shortName") {
31+
// -f=bar syntax doesn't work for short names
32+
ParserForMethods(Main).runEither(Array("-f=bar")) ==>
33+
Left(
34+
"""Missing argument: -f --foo <str>
35+
|Unknown argument: "-f=bar"
36+
|Expected Signature: run
37+
| -f --foo <str> String to print repeatedly
38+
| --my-num <int> How many times to print string
39+
| --bool Example flag
40+
|
41+
|""".stripMargin)
42+
}
43+
test("notFlags") {
44+
// -f=bar syntax doesn't work for flags
45+
ParserForMethods(Main).runEither(Array("--foo=bar", "--bool=true")) ==>
46+
Left("""Unknown argument: "--bool=true"
47+
|Expected Signature: run
48+
| -f --foo <str> String to print repeatedly
49+
| --my-num <int> How many times to print string
50+
| --bool Example flag
51+
|
52+
|""".stripMargin)
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)