This is a command line parser to be used in Scala and Java applications. Some features that this parser supports are:
- Nested Commands
- Usage generation (including types specified in usage)
- Options - Default, Required, Flags
- Any type arguments (arguments can be a type as simple as an integer, but also as complex as your user-defined type)
- parsing operations for arguments
- Automatic failure on
- value extraction failure (an option was supposed to be an int, but was given with an alpha character)
- parse failure
- missing options
- etc.
<dependency>
<groupId>io.github.jrosenkranz.opts</groupId>
<artifactId>opts_2.11</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>io.github.jrosenkranz.opts</groupId>
<artifactId>opts</artifactId>
<version>0.0.1</version>
</dependency>
OptSet - A Set of Options to use for parsing arguments
RequiredOpt - Options that are required
DefaultOpt - Options that are not required (have a default value)
FlagOpt - Special form of DefaultOpt that specifies true or false (true if provided, otherwise false)
CommandOpt - Option that specifies a command (commands have sub-options called ChildOptSets)
Scala
OptSet(
RequiredOpt("host","kafka host","h"),
DefaultOpt("port",2181,"kafka port","p"),
CommandOpt("add","add a topic",ChildOptSet(
RequiredOpt("topicName","name of topic to add"),
FlagOpt("help","flag which provides a help message for the add command")
)),
CommandOpt("delete","delete a topic",ChildOptSet(
RequiredOpt("topicName","name of topic to delete"),
FlagOpt("help","flag which provides a help message for the delete command")
)),
CommandOpt("send","send values on a given topic",ChildOptSet(
RequiredOpt("topicName","name of topic to send values on"),
RequiredOpt("values","values to send (comma separated)","v",_.split(",").map(_.toDouble))
))
)
Java
OptSet.build(
OptBuilder.requiredOpt("host").message("kafka host").abrev("h"),
OptBuilder.defaultOpt("port",2181).message("kafka port").abrev("p").parseOp(Integer::valueOf),
OptBuilder.commandOpt("add").message("name of topic to add")
.addChild(OptBuilder.requiredOpt("topicName").message("name of topic to add"))
.addChild(OptBuilder.flagOpt("help").message("flag which provides a help message for the add command")),
OptBuilder.commandOpt("delete").message("delete a topic")
.addChild(OptBuilder.requiredOpt("topicName").message("name of topic to delete"))
.addChild(OptBuilder.flagOpt("help").message("flag which provides a help message for the delete command")),
OptBuilder.commandOpt("send").message("send values on a given topic")
.addChild(OptBuilder.requiredOpt("topicName").message("name of topic to send values on"))
.addChild(
OptBuilder.requiredOpt("values").message("values to send (comma separated)")
.abrev("v")
.parseOp(s -> Arrays.stream(s.split(",")).map(Double::valueOf).collect(Collectors.toList()))
)
);
The above schema maps to this if arguments are not provided properly
Usage: [cmd] --host <HOST> [options]
--host, -h <String> | required
kafka host
--port, -p <Integer> | default value = 2181
kafka port
Command: delete --host <HOST> --topicName <TOPICNAME>
delete a topic
--topicName <String> | required
name of topic to delete
--help
flag which provides a help message for the delete command
Command: add --host <HOST> --topicName <TOPICNAME>
add a topic
--topicName <String> | required
name of topic to add
--help
flag which provides a help message for the add command
Command: send --host <HOST> --topicName <TOPICNAME> --values <VALUES>
send values on a given topic
--topicName <String> | required
name of topic to send values on
--values, -v <Array> | required
values to send (comma separated)
fail fast option (This code will exit and print a message without explicitly asking)
Scala
val options = Opts(os,args)
Java
Opts options = new Opts(os,args)
fail match with options
Scala
os.parse(args) match {
case Some(options) => {
//passed parse
}
case None => {
//failed parse
}
}
Java
Optional<Opts> optOptions = os.parse(args);
if (optOptions.isPresent()) {
//passed parse
Opts options = optOptions.get();
} else {
//failed parsed
}
fail match with options and failure response (scala only)
os.parseWithResponse(args) match {
case (Some(options),_) => {
//passed parse
}
case (None,res) => {
//failed parse
}
}
Scala
//string can directly get
val host = options("host")
//other types require using the as method to properly get as type
val port = options.as[Int]("port")
val values = options.as[Array[Double]]("values")
Java
String host = options.getAs("host");
int port = options.getAs("port");
List<Double> values = options.getAs("values");
For DefaultOpt and RequiredOpt, one need not create a parsing operation when using an argument of type Int, Long, Double, and String if the argument doesn't require any additional parsing other than a conversion from string. For DefaultOpt this comes for free since the type is inferred from the defaultValue. For RequiredOpt, if a type is specified, the parseOp will be generated automatically.
The following is an example
//no parseOp is required and this option will require an integer
val opt = DefaultOpt("port",2181)
//no parseOp is required and this option will require a Double
val opt = RequiredOpt[Double]("average")
For RequiredOpt, since one does not explicitly specify a defaultValue as in the case of DefaultOpt, the type is not known of RequiredOpt and therefore would normally require the setting of the optType parameter in order to see the type needed for the opt in the error message. With implicit type inference, RequiredOpt now can infer the type if a parseOp is given
//the type of this option is inferred from the parseOp
val opt = RequiredOpt("port",parseOp = _.toInt)
One may also specify the type bound to have the option find the type
//the type of this option is taken from the type bound
val opt = RequiredOpt[Int]("port")
If an error message is to occur, the following will be printed:
--port <Integer> | required
The same schema as above can be built in the form of a class. The main benefit of this is that is it is by default strongly typed and values can be retrieved without specifying the name
val options = new ArgParser {
val host = requiredOpt[String]("host","kafka host","h")
val port = defaultOpt("port",2181,"kafka port","p")
val add = new commandOpt("add","add a topic") {
val topicName = requiredOpt[String]("topicName","name of topic to add")
val help = flagOpt("help","flag which provides a help message for the add command")
}
val delete = new commandOpt("delete","delete a topic") {
val topicName = requiredOpt[String]("topicName","name of topic to delete")
val help = flagOpt("help","flag which provides a help message for the delete command")
}
val send = new commandOpt("send","send values on a given topic") {
val topicName = requiredOpt("topicName","name of topic to send values on")
val values = requiredOpt("values","values to send (comma separated)","v",OptTypes.ARRAY,_.split(",").map(_.toDouble))
}
}
Note: nested sub-commands are not yet supported here, but if enough need arises, will provide
//initialize ArgParser and fail fast
options.init(args)
//initialize ArgParser and return true if succeeded
if (options.parse(args)) {
//use options here since it succeeded and is initialized
} else {
println(options)
System.exit(0)
}
val host = options.host()
val port = options.port()
val values = options.send.values()