CLI fixes (#3331)
This commit is contained in:
@@ -180,14 +180,14 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
|
||||
// Previous argument has default value.
|
||||
it.descriptor.defaultValue?.let {
|
||||
if (currentArgument.descriptor.defaultValue == null && currentArgument.descriptor.required) {
|
||||
printWarning("Default value of argument ${previousArgument.descriptor.fullName} will be unused, " +
|
||||
printError("Default value of argument ${previousArgument.descriptor.fullName} will be unused, " +
|
||||
"because next argument ${currentArgument.descriptor.fullName} is always required and has no default value.")
|
||||
}
|
||||
}
|
||||
// Previous argument is optional.
|
||||
if (!it.descriptor.required) {
|
||||
if (currentArgument.descriptor.defaultValue == null && currentArgument.descriptor.required) {
|
||||
printWarning("Argument ${previousArgument.descriptor.fullName} will be always required, " +
|
||||
printError("Argument ${previousArgument.descriptor.fullName} will be always required, " +
|
||||
"because next argument ${currentArgument.descriptor.fullName} is always required.")
|
||||
}
|
||||
}
|
||||
@@ -313,7 +313,7 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
|
||||
|
||||
// Add default list with arguments if there can be extra free arguments.
|
||||
if (skipExtraArguments) {
|
||||
argument(ArgType.String, "").number()
|
||||
argument(ArgType.String, "").vararg()
|
||||
}
|
||||
|
||||
// Map declared options and arguments to maps.
|
||||
|
||||
@@ -87,7 +87,7 @@ class SingleNullableArgument<T : Any> internal constructor(descriptor: ArgDescri
|
||||
class MultipleArgument<T : Any> internal constructor(descriptor: ArgDescriptor<T, List<T>>, owner: CLIEntityWrapper):
|
||||
Argument<List<T>>(owner) {
|
||||
init {
|
||||
if (descriptor.number != null && descriptor.number == 1) {
|
||||
if (descriptor.number != null && descriptor.number < 2) {
|
||||
failAssertion("Argument with multiple values can't be initialized with descriptor for single one.")
|
||||
}
|
||||
delegate = ArgumentMultipleValues(descriptor)
|
||||
@@ -97,11 +97,11 @@ class MultipleArgument<T : Any> internal constructor(descriptor: ArgDescriptor<T
|
||||
/**
|
||||
* Allow argument have several values.
|
||||
*
|
||||
* @param number number of arguments are expected. In case of null value any number of arguments can be set.
|
||||
* @param value number of arguments are expected.
|
||||
*/
|
||||
fun <T : Any, TResult> AbstractSingleArgument<T, TResult>.number(value: Int? = null): MultipleArgument<T> {
|
||||
if (value != null && value == 1) {
|
||||
error("number() modifier with value 1 is unavailable. It's already set to 1.")
|
||||
fun <T : Any, TResult> AbstractSingleArgument<T, TResult>.multiple(value: Int): MultipleArgument<T> {
|
||||
if (value < 2) {
|
||||
error("multiple() modifier with value less than 2 is unavailable. It's already set to 1.")
|
||||
}
|
||||
val newArgument = with((delegate as ParsingValue<T, T>).descriptor as ArgDescriptor) {
|
||||
MultipleArgument(ArgDescriptor(type, fullName, value, description, listOfNotNull(defaultValue),
|
||||
@@ -111,6 +111,18 @@ fun <T : Any, TResult> AbstractSingleArgument<T, TResult>.number(value: Int? = n
|
||||
return newArgument
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow argument have several values.
|
||||
*/
|
||||
fun <T : Any, TResult> AbstractSingleArgument<T, TResult>.vararg(): MultipleArgument<T> {
|
||||
val newArgument = with((delegate as ParsingValue<T, T>).descriptor as ArgDescriptor) {
|
||||
MultipleArgument(ArgDescriptor(type, fullName, null, description, listOfNotNull(defaultValue),
|
||||
required, deprecatedWarning), owner)
|
||||
}
|
||||
owner.entity = newArgument
|
||||
return newArgument
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default value for argument.
|
||||
*
|
||||
@@ -130,6 +142,9 @@ fun <T: Any, TResult> AbstractSingleArgument<T, TResult>.default(value: T): Sing
|
||||
* @param value default value.
|
||||
*/
|
||||
fun <T: Any> MultipleArgument<T>.default(value: Collection<T>): MultipleArgument<T> {
|
||||
if (value.isEmpty()) {
|
||||
error("Default value for argument can't be empty collection.")
|
||||
}
|
||||
val newArgument = with((delegate as ParsingValue<T, List<T>>).descriptor as ArgDescriptor) {
|
||||
MultipleArgument(ArgDescriptor(type, fullName, number, description, value.toList(),
|
||||
required, deprecatedWarning), owner)
|
||||
@@ -141,7 +156,7 @@ fun <T: Any> MultipleArgument<T>.default(value: Collection<T>): MultipleArgument
|
||||
/**
|
||||
* Allow argument be unprovided in command line.
|
||||
*/
|
||||
fun <T: Any, TResult> AbstractSingleArgument<T, TResult>.optional(): SingleNullableArgument<T> {
|
||||
fun <T: Any> SingleArgument<T>.optional(): SingleNullableArgument<T> {
|
||||
val newArgument = with((delegate as ParsingValue<T, T>).descriptor as ArgDescriptor) {
|
||||
SingleNullableArgument(ArgDescriptor(type, fullName, number, description, defaultValue,
|
||||
false, deprecatedWarning), owner)
|
||||
|
||||
@@ -127,7 +127,7 @@ internal class ArgDescriptor<T : Any, TResult>(
|
||||
init {
|
||||
// Check arguments number correctness.
|
||||
number?.let {
|
||||
if (it < 0)
|
||||
if (it < 1)
|
||||
error("Number of arguments for argument description $fullName should be greater than zero.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,9 +116,6 @@ fun <T : Any> MultipleOption<T, DelimitedOption>.multiple(): MultipleOption<T, R
|
||||
*/
|
||||
fun <T: Any, TResult> AbstractSingleOption<T, TResult>.default(value: T): SingleOption<T> {
|
||||
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
|
||||
if (required) {
|
||||
printWarning("required() is unneeded, because option with default value is defined.")
|
||||
}
|
||||
SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
|
||||
description, value, required, multiple, delimiter, deprecatedWarning), owner)
|
||||
}
|
||||
@@ -134,8 +131,8 @@ fun <T: Any, TResult> AbstractSingleOption<T, TResult>.default(value: T): Single
|
||||
fun <T: Any, OptionType: MultipleOptionType>
|
||||
MultipleOption<T, OptionType>.default(value: Collection<T>): MultipleOption<T, OptionType> {
|
||||
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
|
||||
if (required) {
|
||||
printWarning("required() is unneeded, because option with default value is defined.")
|
||||
if (value.isEmpty()) {
|
||||
error("Default value for option can't be empty collection.")
|
||||
}
|
||||
MultipleOption<T, OptionType>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName,
|
||||
shortName, description, value.toList(),
|
||||
@@ -148,11 +145,8 @@ fun <T: Any, OptionType: MultipleOptionType>
|
||||
/**
|
||||
* Require option to be always provided in command line.
|
||||
*/
|
||||
fun <T: Any, TResult> AbstractSingleOption<T, TResult>.required(): SingleOption<T> {
|
||||
fun <T: Any> SingleNullableOption<T>.required(): SingleOption<T> {
|
||||
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
|
||||
defaultValue?.let {
|
||||
printWarning("required() is unneeded, because option with default value is defined.")
|
||||
}
|
||||
SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName,
|
||||
shortName, description, defaultValue,
|
||||
true, multiple, delimiter, deprecatedWarning), owner)
|
||||
@@ -167,9 +161,6 @@ fun <T: Any, TResult> AbstractSingleOption<T, TResult>.required(): SingleOption<
|
||||
fun <T: Any, OptionType: MultipleOptionType>
|
||||
MultipleOption<T, OptionType>.required(): MultipleOption<T, OptionType> {
|
||||
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
|
||||
if (required) {
|
||||
printWarning("required() is unneeded, because option with default value is defined.")
|
||||
}
|
||||
MultipleOption<T, OptionType>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
|
||||
description, defaultValue?.toList() ?: listOf(),
|
||||
true, multiple, delimiter, deprecatedWarning), owner)
|
||||
|
||||
@@ -26,7 +26,7 @@ class ArgumentsTests {
|
||||
fun testArgumetsWithAnyNumberOfValues() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val output by argParser.argument(ArgType.String, "output", "Output file")
|
||||
val inputs by argParser.argument(ArgType.String, description = "Input files").number()
|
||||
val inputs by argParser.argument(ArgType.String, description = "Input files").vararg()
|
||||
argParser.parse(arrayOf("out.txt", "input1.txt", "input2.txt", "input3.txt",
|
||||
"input4.txt"))
|
||||
assertEquals("out.txt", output)
|
||||
@@ -36,7 +36,7 @@ class ArgumentsTests {
|
||||
@Test
|
||||
fun testArgumetsWithSeveralValues() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val addendums by argParser.argument(ArgType.Int, "addendums", description = "Addendums").number(2)
|
||||
val addendums by argParser.argument(ArgType.Int, "addendums", description = "Addendums").multiple(2)
|
||||
val output by argParser.argument(ArgType.String, "output", "Output file")
|
||||
val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
|
||||
argParser.parse(arrayOf("2", "-d", "3", "out.txt"))
|
||||
@@ -50,7 +50,7 @@ class ArgumentsTests {
|
||||
@Test
|
||||
fun testSkippingExtraArguments() {
|
||||
val argParser = ArgParser("testParser", skipExtraArguments = true)
|
||||
val addendums by argParser.argument(ArgType.Int, "addendums", description = "Addendums").number(2)
|
||||
val addendums by argParser.argument(ArgType.Int, "addendums", description = "Addendums").multiple(2)
|
||||
val output by argParser.argument(ArgType.String, "output", "Output file")
|
||||
val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
|
||||
argParser.parse(arrayOf("2", "-d", "3", "out.txt", "something", "else", "in", "string"))
|
||||
|
||||
@@ -13,7 +13,7 @@ class ErrorTests {
|
||||
@Test
|
||||
fun testExtraArguments() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val addendums by argParser.argument(ArgType.Int, "addendums", description = "Addendums").number(2)
|
||||
val addendums by argParser.argument(ArgType.Int, "addendums", description = "Addendums").multiple(2)
|
||||
val output by argParser.argument(ArgType.String, "output", "Output file")
|
||||
val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
|
||||
val exception = assertFailsWith<IllegalStateException> { argParser.parse(
|
||||
|
||||
@@ -18,7 +18,7 @@ class SubcommandsTests {
|
||||
val output by argParser.option(ArgType.String, "output", "o", "Output file")
|
||||
class Summary: Subcommand("summary") {
|
||||
val invert by option(ArgType.Boolean, "invert", "i", "Invert results")
|
||||
val addendums by argument(ArgType.Int, "addendums", description = "Addendums").number()
|
||||
val addendums by argument(ArgType.Int, "addendums", description = "Addendums").vararg()
|
||||
var result: Int = 0
|
||||
|
||||
override fun execute() {
|
||||
@@ -36,7 +36,7 @@ class SubcommandsTests {
|
||||
@Test
|
||||
fun testCommonOptions() {
|
||||
abstract class CommonOptions(name: String): Subcommand(name) {
|
||||
val numbers by argument(ArgType.Int, "numbers", description = "Numbers").number()
|
||||
val numbers by argument(ArgType.Int, "numbers", description = "Numbers").vararg()
|
||||
}
|
||||
class Summary: CommonOptions("summary") {
|
||||
val invert by option(ArgType.Boolean, "invert", "i", "Invert results")
|
||||
@@ -74,7 +74,7 @@ class SubcommandsTests {
|
||||
val argParser = ArgParser("testParser")
|
||||
|
||||
class Summary: Subcommand("summary") {
|
||||
val addendums by argument(ArgType.Int, "addendums", description = "Addendums").number()
|
||||
val addendums by argument(ArgType.Int, "addendums", description = "Addendums").vararg()
|
||||
var result: Int = 0
|
||||
|
||||
override fun execute() {
|
||||
|
||||
Reference in New Issue
Block a user