-
Notifications
You must be signed in to change notification settings - Fork 446
Description
I would like to add an option multiple times and add sub-options/parameters to it. In my scenario, I have a viewer application and, among other options and one positional argument, I would like to add datasets, somewhat like this:
viewer <positional-argument> \
--add-dataset --container=c1 --dataset=d1 \
--add-dataset --dataset=<d2 --type=label \
--fallback-container=fbc \ # use this as fallback, if no container is specified
[other options]For this call, I would add two datasets to my container:
d1in containerc1(type is auto-detected from meta data)d2in containerfbcas label dataset
As far as I can tell, this is not (yet) supported in picocli (please correct me if I am wrong), and my - kind of hackish way - to achieve that, is to add an Option
@CommandLine.Option(names = arrayOf("--add-dataset"), arity = "+")
var addDatasetStrings: List<String> = mutableListOf()that collects greedily all Strings that follow that option into a list. Users need to append a split-char after the last option of each --add-dataset (I chose _ here). I then split the list at _, and parse each of the sublists with a separate parser. This works well for me but it has a few downsides, as far as I can tell:
- I do not think it is possible to have overlapping arguments between the main arguments/options and the options/arguments for
--add-dataset. - Need delimiter string (I used
_) - help message for sub option (
--add-dataset) not included automatically
I added my (kotlin) code and an invocation example at the very end of this comment for reference.
I think that this would be a useful addition. There are several issues that found that are related but not the same in my understanding:
- Repeated options #559
- Support repeating composite arguments (was: Parameter options/modifiers) #358
- Allow repeatable subcommands? #454
Out of these, #454 seems to be the most closely related issues. For my real-world use case (viewer application with one positional argument, and more options), I could turn the one positional argument into an option and #454 could be a working solution for me. For anything that requires positional arguments, I would be concerned that the positional arguments might clash with the subcommands.
import org.apache.commons.lang3.builder.ToStringBuilder
import picocli.CommandLine
import java.util.concurrent.Callable
@CommandLine.Command
class AddDataset(val fallbackContainer: String?) : Callable<AddDataset> {
@CommandLine.Option(names = arrayOf("--container"))
var container: String? = null
@CommandLine.Option(names = arrayOf("--dataset"), required=true)
var dataset: String? = null
@CommandLine.Option(names = arrayOf("--help", "-h"), usageHelp = true)
var helpRequested = false
override fun call(): AddDataset {
return this
}
override fun toString(): String {
return ToStringBuilder(this)
.append("container", container?: fallbackContainer)
.append("dataset", dataset)
.toString()
}
}
class Args : Callable<List<AddDataset>> {
@CommandLine.Option(names = arrayOf("--default-container"))
var defaultContainer: String? = null
@CommandLine.Option(names = arrayOf("--add-dataset"), arity = "+")
var addDatasetStrings: List<String> = mutableListOf()
@CommandLine.Option(names = arrayOf("--help", "-h"), usageHelp = true)
var helpRequested = false
override fun call(): List<AddDataset> {
val indices = addDatasetStrings
.withIndex()
.filter { it.value.equals("_", ignoreCase = true) }
.map { it.index }
val subLists = mutableListOf<List<String>>()
var nextStartingIndex = 0
for (i in 0 until indices.size) {
subLists.add(addDatasetStrings.subList(nextStartingIndex, indices[i]))
nextStartingIndex = indices[i] + 1
}
return subLists.map { CommandLine.call(AddDataset(defaultContainer), *it.toTypedArray()) }
}
}
fun main(argv: Array<String>) {
val args = Args()
val datasets = CommandLine.call(args, *argv)
println(datasets)
}$ Command --add-dataset --container=123 --dataset=456 _ --default-container=abracadabra --add-dataset --dataset=789 _
[AddDataset@5b87ed94[container=123,dataset=456], AddDataset@6e0e048a[container=abracadabra,dataset=789]]