Name: case-app
Owner: Hammer Lab
Description: Type-level & seamless command-line argument parsing for Scala
Forked from: alexarchambault/case-app
Created: 2017-07-05 17:22:43.0
Updated: 2017-07-05 17:22:49.0
Pushed: 2017-07-05 17:23:12.0
Size: 194
Language: Scala
GitHub Committers
User | Most Recent Commit | # Commits |
---|
Other Committers
User | Most Recent Commit | # Commits |
---|
Type-level & seamless command-line argument parsing for Scala
The code snippets below assume that the content of caseapp
is imported,
rt caseapp._
class Options(
er: Option[String],
ableFoo: Boolean = false,
le: List[String]
App.parse[Options](
q("--user", "alice", "--file", "a", "--file", "b")
Right((Options(Some("alice"), false, List("a", "b")), Seq.empty))
All arguments are required by default. To define an optional argument simply
wrap its type into Option[T]
.
Optional arguments can also be defined by providing a default value. There are two ways to do that:
class Options(
er: Option[String],
ableFoo: Boolean = false,
le: List[String] = Nil
App.parse[Options](Seq()) == Right((Options(None, false, Nil), Seq.empty))
Some arguments can be specified several times on the command-line. These
should be typed as lists, e.g. file
in
class Options(
er: Option[String],
ableFoo: Boolean = false,
le: List[String]
App.parse[Options](
q("--file", "a", "--file", "b")
Right((Options(None, false, List("a", "b")), Seq.empty))
If an argument is specified several times, but is not typed as a List
(or an accumulating type,
see below), the final value of its corresponding field is the last provided in the arguments.
case-app can take care of the creation of the main
method parsing
command-line arguments.
rt caseapp._
class ExampleOptions(
o: String,
r: Int
ct Example extends CaseApp[ExampleOptions] {
f run(options: ExampleOptions, arg: RemainingArgs): Unit = {
// Core of the app
// ...
Example
in the above example will then have a main
method, parsing
the arguments it is given to an ExampleOptions
, then calling the run
method
if parsing was successful.
Running the above example with the --help
(or -h
) option will print an help message
of the form
ple
e: example [options]
foo <value>
bar <value>
Calling it with the --usage
option will print
e: example [options]
Several parts of the above help message can be customized by annotating
ExampleOptions
or its fields:
Name("MyApp")
Version("0.1.0")
gName("my-app-cli")
class ExampleOptions(
elpMessage("the foo")
alueDescription("foo")
foo: String,
elpMessage("the bar")
alueDescription("bar")
bar: Int
Called with the --help
or -h
option, would print
p 0.1.0
e: my-app-cli [options]
foo <foo>: the foo
bar <bar>: the bar
Note the application name that changed, on the first line. Note also the version
number appended next to it. The program name, after Usage:
, was changed too.
Lastly, the options value descriptions (<foo>
and <bar>
) and help messages
(the foo
and the bar
), were customized.
Alternative option names can be specified, like
class ExampleOptions(
xtraName("f")
foo: String,
xtraName("b")
bar: Int
--foo
and -f
, and --bar
and -b
would then be equivalent.
Field names, or extra names as above, longer than one letter are considered
long options, prefixed with --
. One letter long names are short options,
prefixed with a single -
.
class ExampleOptions(
Int,
o: String
would accept --foo bar
and -a 2
as arguments to set foo
or a
.
Field names or extra names as above, written in pascal case, are split and hyphenized.
class Options(
oBar: Double
would accept arguments like --foo-bar 2.2
.
Sets of options can be shared between applications:
class CommonOptions(
o: String,
r: Int
class First(
z: Double,
ecurse
common: CommonOptions
...
class Second(
s: Long,
ecurse
common: CommonOptions
...
case-app has a support for commands.
ed trait DemoCommand
class First(
o: Int,
r: String
tends DemoCommand
class Second(
z: Double
tends DemoCommand
ct MyApp extends CommandApp[DemoCommand] {
f run(command: DemoCommand, args: RemainingArgs): Unit = {}
MyApp
can then be called with arguments like
pp first --foo 2 --bar a
pp second --baz 2.4
Needs to be updated
Some more complex options can be specified multiple times on the command-line and should be “accumulated”. For example, one would want to define a verbose option like
class Options(
xtraName("v") verbose: Int
Verbosity would then have be specified on the command-line like --verbose 3
.
But the usual preferred way of increasing verbosity is to repeat the verbosity
option, like in -v -v -v
. To accept the latter,
tag verbose
type with Counter
:
class Options(
xtraName("v") verbose: Int @@ Counter
verbose
(and v
) option will then be viewed as a flag, and the
verbose
variable will contain
the number of times this flag is specified on the command-line.
It can optionally be given a default value other than 0. This
value will be increased by the number of times -v
or --verbose
was specified in the arguments.
Needs to be updated
Use your own option types by defining implicit ArgParser
s for them, like in
rt caseapp.core.ArgParser
t Custom
icit val customArgParser: ArgParser[Custom] =
gParser.instance[Custom]("custom") { s =>
// parse s
// return
// - Left("error message") in case of error
// - Right(custom) in case of success
???
Then use them like
class Options(
stom: Custom,
o: String
Shared options used to be automatic, and now require the @Recurse
annotation on the field corresponding to the shared options. This prevents
ambiguities with custom types as above.
Add to your build.sbt
lvers += Resolver.sonatypeRepo("releases")
aryDependencies += "com.github.alexarchambault" %% "case-app" % "1.2.0-M2"
Note that case-app depends on shapeless 2.3. Use the 1.0.0
version if you depend on shapeless 2.2.
It is built against scala 2.10, 2.11, and 2.12.
If you are using scala 2.10.x, also add the macro paradise plugin to your build,
aryDependencies +=
mpilerPlugin("org.scalamacros" % "paradise" % "2.0.1" cross CrossVersion.full)
Eugene Yokota, the current maintainer of scopt, and others, compiled an (eeextremeeeely long) list of command-line argument parsing libraries for Scala, in this StackOverflow question.
Unlike scopt, case-app is less monadic / abstract data types based, and more straight-to-the-point and descriptive / algebric data types oriented.
Copyright (c) 2014-2017 Alexandre Archambault and contributors. See LICENSE file for more details.
Released under Apache 2.0 license.