Skip to content

Commit f587a8e

Browse files
Merge pull request #435 from alexarchambault/fix-full-help
Ensure WithFullHelp args have a non-empty origin field
2 parents 5a653bd + 22123fd commit f587a8e

File tree

6 files changed

+133
-28
lines changed

6 files changed

+133
-28
lines changed

build.sbt

+5-2
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,11 @@ lazy val tests = crossProject(JSPlatform, JVMPlatform, NativePlatform)
115115
.settings(
116116
shared,
117117
caseAppPrefix,
118-
publish / skip := true,
119-
libraryDependencies += Deps.utest.value % Test,
118+
publish / skip := true,
119+
libraryDependencies ++= Seq(
120+
Deps.pprint.value % Test,
121+
Deps.utest.value % Test
122+
),
120123
testFrameworks += new TestFramework("utest.runner.Framework")
121124
)
122125

Original file line numberDiff line numberDiff line change
@@ -1,19 +1,63 @@
11
package caseapp.core.help
22

3-
import caseapp.core.parser.Parser
4-
import caseapp.{ExtraName, HelpMessage}
3+
import caseapp.{ExtraName, Group, HelpMessage, Parser}
4+
import caseapp.core.parser.{Argument, NilParser, StandardArgument}
5+
import caseapp.core.{Arg, Error}
6+
import caseapp.core.parser.RecursiveConsParser
7+
import caseapp.core.util.Formatter
8+
9+
import shapeless.{HNil, :: => :*:}
510

611
abstract class WithFullHelpCompanion {
712

8-
implicit def parser[T, D](implicit underlying: Parser.Aux[T, D]): Parser[WithFullHelp[T]] =
9-
Parser.nil
10-
.addAll[WithHelp[T]].apply
11-
.add[Boolean](
12-
"helpFull",
13-
default = Some(false),
14-
extraNames = Seq(ExtraName("fullHelp")),
15-
helpMessage = Some(HelpMessage("Print help message, including hidden options, and exit"))
16-
)
17-
.as[WithFullHelp[T]]
13+
implicit def parser[T, D](implicit
14+
underlying: Parser.Aux[T, D]
15+
): Parser.Aux[
16+
WithFullHelp[T],
17+
(Option[Boolean] :*: Option[Boolean] :*: D :*: HNil) :*: Option[Boolean] :*: HNil
18+
] = {
19+
20+
val baseHelpArgument = StandardArgument[Boolean](
21+
Arg("helpFull")
22+
.withExtraNames(Seq(
23+
ExtraName("fullHelp"),
24+
ExtraName("-help-full"),
25+
ExtraName("-full-help")
26+
))
27+
.withGroup(Some(Group("Help")))
28+
.withOrigin(Some("WithFullHelp"))
29+
.withHelpMessage(Some(
30+
HelpMessage("Print help message, including hidden options, and exit")
31+
))
32+
.withIsFlag(true)
33+
).withDefault(() => Some(false))
34+
35+
// accept "-help" too (single dash)
36+
val helpArgument: Argument[Boolean] =
37+
new Argument[Boolean] {
38+
def arg = baseHelpArgument.arg
39+
def withDefaultOrigin(origin: String) =
40+
this
41+
def init = baseHelpArgument.init
42+
def step(
43+
args: List[String],
44+
index: Int,
45+
d: Option[Boolean],
46+
nameFormatter: Formatter[ExtraName]
47+
): Either[(Error, List[String]), Option[(Option[Boolean], List[String])]] =
48+
args match {
49+
case ("-help-full" | "-full-help") :: rem => Right(Some((Some(true), rem)))
50+
case _ => baseHelpArgument.step(args, index, d, nameFormatter)
51+
}
52+
def get(d: Option[Boolean], nameFormatter: Formatter[ExtraName]) =
53+
baseHelpArgument.get(d, nameFormatter)
54+
}
55+
56+
val withHelpParser = WithHelp.parser[T, D](underlying)
57+
58+
val p = RecursiveConsParser(withHelpParser, helpArgument :: NilParser)
59+
60+
p.to[WithFullHelp[T]]
61+
}
1862

1963
}
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,59 @@
11
package caseapp.core.help
22

3-
import caseapp.core.parser.Parser
4-
import caseapp.{ExtraName, HelpMessage}
3+
import caseapp.{ExtraName, Group, HelpMessage, Parser}
4+
import caseapp.core.Scala3Helpers.*
5+
import caseapp.core.parser.{Argument, NilParser, StandardArgument}
6+
import caseapp.core.{Arg, Error}
7+
import caseapp.core.parser.RecursiveConsParser
8+
import caseapp.core.util.Formatter
59

610
abstract class WithFullHelpCompanion {
711

8-
implicit def parser[T: Parser]: Parser[WithFullHelp[T]] =
9-
Parser.nil
10-
.addAll[WithHelp[T]](using WithHelp.parser[T])
11-
.add[Boolean](
12-
"helpFull",
13-
default = Some(false),
14-
extraNames = Seq(ExtraName("fullHelp")),
15-
helpMessage = Some(HelpMessage("Print help message, including hidden options, and exit"))
16-
)
17-
.as[WithFullHelp[T]]
12+
implicit def parser[T](implicit
13+
underlying: Parser[T]
14+
): Parser[WithFullHelp[T]] = {
15+
16+
val baseHelpArgument = StandardArgument[Boolean](
17+
Arg("helpFull")
18+
.withExtraNames(Seq(
19+
ExtraName("fullHelp"),
20+
ExtraName("-help-full"),
21+
ExtraName("-full-help")
22+
))
23+
.withGroup(Some(Group("Help")))
24+
.withOrigin(Some("WithFullHelp"))
25+
.withHelpMessage(Some(
26+
HelpMessage("Print help message, including hidden options, and exit")
27+
))
28+
.withIsFlag(true)
29+
).withDefault(() => Some(false))
30+
31+
// accept "-help" too (single dash)
32+
val helpArgument: Argument[Boolean] =
33+
new Argument[Boolean] {
34+
def arg = baseHelpArgument.arg
35+
def withDefaultOrigin(origin: String) =
36+
this
37+
def init = baseHelpArgument.init
38+
def step(
39+
args: List[String],
40+
index: Int,
41+
d: Option[Boolean],
42+
nameFormatter: Formatter[ExtraName]
43+
): Either[(Error, List[String]), Option[(Option[Boolean], List[String])]] =
44+
args match {
45+
case ("-help-full" | "-full-help") :: rem => Right(Some((Some(true), rem)))
46+
case _ => baseHelpArgument.step(args, index, d, nameFormatter)
47+
}
48+
def get(d: Option[Boolean], nameFormatter: Formatter[ExtraName]) =
49+
baseHelpArgument.get(d, nameFormatter)
50+
}
51+
52+
val withHelpParser = WithHelp.parser[T](underlying)
53+
54+
val p = RecursiveConsParser(withHelpParser, helpArgument :: NilParser)
55+
56+
p.to[WithFullHelp[T]]
57+
}
1858

1959
}

project/Deps.scala

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ object Deps {
1111
def catsEffect3 = setting("org.typelevel" %%% "cats-effect" % "3.3.14")
1212
def dataClass = "io.github.alexarchambault" %% "data-class" % "0.2.6"
1313
def macroParadise = "org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.patch
14+
def pprint = setting("com.lihaoyi" %%% "pprint" % "0.8.0")
1415
def scalaCompiler = setting("org.scala-lang" % "scala-compiler" % scalaVersion.value)
1516
def scalaReflect = setting("org.scala-lang" % "scala-reflect" % scalaVersion.value)
1617
def shapeless = setting("com.chuusai" %%% "shapeless" % "2.3.10")

project/Mima.scala

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@ import scala.sys.process._
88
object Mima {
99

1010
def binaryCompatibilityVersions: Set[String] =
11-
Seq("git", "tag", "--merged", "HEAD^", "--contains", "cacc9a0340fde584a10a814037db6a8947881931")
11+
Seq("git", "tag", "--merged", "HEAD^", "--contains", "5a653bdd41972587bd3fb3d9751c0e0cf11c1e74")
1212
.!!
1313
.linesIterator
1414
.map(_.trim)
1515
.filter(_.startsWith("v"))
1616
.map(_.stripPrefix("v"))
17-
.filter(_ != "2.1.0-M19")
1817
.toSet
1918

2019
def settings = Def.settings(

tests/shared/src/test/scala/caseapp/ParserTests.scala

+18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package caseapp
22

33
import caseapp.core.{Arg, Error, Indexed}
4+
import caseapp.core.help.{WithFullHelp, WithHelp}
45
import caseapp.core.parser.{Argument, NilParser, StandardArgument}
56
import caseapp.core.parser.ParserOps
67
import caseapp.core.util.Formatter
@@ -87,6 +88,23 @@ object ParserTests extends TestSuite {
8788
assert(numFooArg.origin == Some("FewArgs"))
8889
}
8990

91+
test("WithHelp args have an origin") {
92+
case class Dummy()
93+
val parser: Parser[WithHelp[Dummy]] = WithHelp.parser
94+
val args = parser.args
95+
assert(args.nonEmpty)
96+
assert(args.forall(_.origin.contains("WithHelp")))
97+
}
98+
99+
test("WithFullHelp args have an origin") {
100+
case class Dummy()
101+
val parser: Parser[WithFullHelp[Dummy]] = WithFullHelp.parser
102+
val args = parser.args
103+
assert(args.exists(_.origin.contains("WithHelp")))
104+
assert(args.exists(_.origin.contains("WithFullHelp")))
105+
assert(args.forall(_.origin.exists(o => o == "WithHelp" || o == "WithFullHelp")))
106+
}
107+
90108
test("Custom Argument type") {
91109
case class Helper(n: Int, values: List[String])
92110

0 commit comments

Comments
 (0)