Advanced topics¶
Recovering argument positions¶
One can recover the exact positions of each options and remaining arguments.
For options, one should use the Indexed
class, like
import caseapp.core.Indexed
case class Options(
foo: Indexed[String] = Indexed("")
)
val (options, _) = CaseApp.parse[Options](Seq("a", "--foo", "thing")).toOption.get
// options: Options = Options(
// foo = Indexed(index = 1, length = 2, value = "thing")
// )
For arguments that are not options or option values, indices are retained in the
RemainingArgs
instance:
args.indexed
// res1: Seq[Indexed[String]] = List(
// Indexed(index = 0, length = 1, value = "a"),
// Indexed(index = 3, length = 1, value = "b")
// )
Partial parsing¶
One can stop parsing arguments at the first argument that is not an option, or that is an unknown option. This can be useful when "pre-processing" arguments passed to another application later on: case-app parses as much arguments as possible, then stops, so that the other application can parse the remaining arguments later on.
case class Options(
foo: String = ""
)
object Options {
implicit lazy val parser: Parser[Options] = {
val parser0: Parser[Options] = Parser.derive
parser0.stopAtFirstUnrecognized
}
implicit lazy val help: Help[Options] = Help.derive
}
val (options, args) = CaseApp.parse[Options](Seq("--foo", "a", "--other", "thing")).toOption.get
// options: Options = Options(foo = "a")
// args: Seq[String] = List("--other", "thing")
Alternatively, when extending CaseApp
, one can do:
object MyApp extends CaseApp[Options] {
override def stopAtFirstUnrecognized = true
def run(options: Options, args: RemainingArgs): Unit = {
???
}
}
Ignore unrecognized options¶
One can ignore non-recognized options, like
case class Options(
foo: String = ""
)
object Options {
implicit lazy val parser: Parser[Options] = {
val parser0: Parser[Options] = Parser.derive
parser0.ignoreUnrecognized
}
implicit lazy val help: Help[Options] = Help.derive
}
val (options, args) = CaseApp.parse[Options](Seq("--other", "thing", "--foo", "a")).toOption.get
// options: Options = Options(foo = "a")
// args: Seq[String] = List("--other", "thing")
Unrecognized options end up in the "remaining arguments", along with non-option or non-option values arguments.
Alternatively, when extending CaseApp
, one can do:
object MyApp extends CaseApp[Options] {
override def ignoreUnrecognized = true
def run(options: Options, args: RemainingArgs): Unit = {
???
}
}
Check for duplicate options in tests¶
When using the @Recurse
or @Name
annotations, some options might be given the same
name. In practice, at runtime, one will shadow the other.
To ensure your options don't contain duplicates, you can call ensureNoDuplicates
on the Help
type class instance of your options, or on the CaseApp
instance
if you defined one:
case class Options(
foo: String = "",
@Name("foo")
count: Int = 0
)
object MyApp extends CaseApp[Options] {
def run(options: Options, args: RemainingArgs): Unit = {
???
}
}
Help[Options].ensureNoDuplicates()
// java.lang.Exception: Found duplicated arguments: --foo (List(Arg(Name(foo), List(), Some(ValueDescription(string)), None, false, false, None, Some(Options), List()), Arg(Name(count), List(Name(foo)), Some(ValueDescription(int)), None, false, false, None, Some(Options), List())) times).
// at caseapp.core.help.Help.ensureNoDuplicates(Help.scala:90)
// at repl.MdocSession$MdocApp8$$anonfun$56.apply$mcV$sp(advanced.md:208)
// at repl.MdocSession$MdocApp8$$anonfun$56.apply(advanced.md:208)
// at repl.MdocSession$MdocApp8$$anonfun$56.apply(advanced.md:208)
MyApp.ensureNoDuplicates()
// java.lang.Exception: Found duplicated arguments: --foo (List(Arg(Name(foo), List(), Some(ValueDescription(string)), None, false, false, None, Some(Options), List()), Arg(Name(count), List(Name(foo)), Some(ValueDescription(int)), None, false, false, None, Some(Options), List())) times).
// at caseapp.core.help.Help.ensureNoDuplicates(Help.scala:90)
// at caseapp.core.app.CaseApp.ensureNoDuplicates(CaseApp.scala:108)
// at repl.MdocSession$MdocApp8$$anonfun$57.apply$mcV$sp(advanced.md:218)
// at repl.MdocSession$MdocApp8$$anonfun$57.apply(advanced.md:218)
// at repl.MdocSession$MdocApp8$$anonfun$57.apply(advanced.md:218)