diff --git a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgParser.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgParser.kt index 69101439071..ef6a435a84b 100644 --- a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgParser.kt +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgParser.kt @@ -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. diff --git a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Arguments.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Arguments.kt index 45049cc6df9..9e3478e4b1c 100644 --- a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Arguments.kt +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Arguments.kt @@ -87,7 +87,7 @@ class SingleNullableArgument internal constructor(descriptor: ArgDescri class MultipleArgument internal constructor(descriptor: ArgDescriptor>, owner: CLIEntityWrapper): Argument>(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 internal constructor(descriptor: ArgDescriptor AbstractSingleArgument.number(value: Int? = null): MultipleArgument { - if (value != null && value == 1) { - error("number() modifier with value 1 is unavailable. It's already set to 1.") +fun AbstractSingleArgument.multiple(value: Int): MultipleArgument { + 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).descriptor as ArgDescriptor) { MultipleArgument(ArgDescriptor(type, fullName, value, description, listOfNotNull(defaultValue), @@ -111,6 +111,18 @@ fun AbstractSingleArgument.number(value: Int? = n return newArgument } +/** + * Allow argument have several values. + */ +fun AbstractSingleArgument.vararg(): MultipleArgument { + val newArgument = with((delegate as ParsingValue).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 AbstractSingleArgument.default(value: T): Sing * @param value default value. */ fun MultipleArgument.default(value: Collection): MultipleArgument { + if (value.isEmpty()) { + error("Default value for argument can't be empty collection.") + } val newArgument = with((delegate as ParsingValue>).descriptor as ArgDescriptor) { MultipleArgument(ArgDescriptor(type, fullName, number, description, value.toList(), required, deprecatedWarning), owner) @@ -141,7 +156,7 @@ fun MultipleArgument.default(value: Collection): MultipleArgument /** * Allow argument be unprovided in command line. */ -fun AbstractSingleArgument.optional(): SingleNullableArgument { +fun SingleArgument.optional(): SingleNullableArgument { val newArgument = with((delegate as ParsingValue).descriptor as ArgDescriptor) { SingleNullableArgument(ArgDescriptor(type, fullName, number, description, defaultValue, false, deprecatedWarning), owner) diff --git a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Descriptors.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Descriptors.kt index 05965d287d1..5d3eb05d820 100644 --- a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Descriptors.kt +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Descriptors.kt @@ -127,7 +127,7 @@ internal class ArgDescriptor( 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.") } } diff --git a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Options.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Options.kt index d9d598a8554..cc1088fe658 100644 --- a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Options.kt +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Options.kt @@ -116,9 +116,6 @@ fun MultipleOption.multiple(): MultipleOption AbstractSingleOption.default(value: T): SingleOption { val newOption = with((delegate as ParsingValue).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 AbstractSingleOption.default(value: T): Single fun MultipleOption.default(value: Collection): MultipleOption { val newOption = with((delegate as ParsingValue>).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(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName, description, value.toList(), @@ -148,11 +145,8 @@ fun /** * Require option to be always provided in command line. */ -fun AbstractSingleOption.required(): SingleOption { +fun SingleNullableOption.required(): SingleOption { val newOption = with((delegate as ParsingValue).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 AbstractSingleOption.required(): SingleOption< fun MultipleOption.required(): MultipleOption { val newOption = with((delegate as ParsingValue>).descriptor as OptionDescriptor) { - if (required) { - printWarning("required() is unneeded, because option with default value is defined.") - } MultipleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName, description, defaultValue?.toList() ?: listOf(), true, multiple, delimiter, deprecatedWarning), owner) diff --git a/endorsedLibraries/kotlinx.cli/src/tests/ArgumentsTests.kt b/endorsedLibraries/kotlinx.cli/src/tests/ArgumentsTests.kt index 32069536507..db49aa8e2b4 100644 --- a/endorsedLibraries/kotlinx.cli/src/tests/ArgumentsTests.kt +++ b/endorsedLibraries/kotlinx.cli/src/tests/ArgumentsTests.kt @@ -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")) diff --git a/endorsedLibraries/kotlinx.cli/src/tests/ErrorTests.kt b/endorsedLibraries/kotlinx.cli/src/tests/ErrorTests.kt index 8e53ee35718..411d3f48738 100644 --- a/endorsedLibraries/kotlinx.cli/src/tests/ErrorTests.kt +++ b/endorsedLibraries/kotlinx.cli/src/tests/ErrorTests.kt @@ -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 { argParser.parse( diff --git a/endorsedLibraries/kotlinx.cli/src/tests/SubcommandsTests.kt b/endorsedLibraries/kotlinx.cli/src/tests/SubcommandsTests.kt index 1a8d0e5f89f..acaf848c77a 100644 --- a/endorsedLibraries/kotlinx.cli/src/tests/SubcommandsTests.kt +++ b/endorsedLibraries/kotlinx.cli/src/tests/SubcommandsTests.kt @@ -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() {