This commit is contained in:
LepilkinaElena
2019-09-18 09:44:39 +03:00
committed by GitHub
parent 09b21d687e
commit 52c5fd1fef
7 changed files with 35 additions and 29 deletions
@@ -180,14 +180,14 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
// Previous argument has default value. // Previous argument has default value.
it.descriptor.defaultValue?.let { it.descriptor.defaultValue?.let {
if (currentArgument.descriptor.defaultValue == null && currentArgument.descriptor.required) { 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.") "because next argument ${currentArgument.descriptor.fullName} is always required and has no default value.")
} }
} }
// Previous argument is optional. // Previous argument is optional.
if (!it.descriptor.required) { if (!it.descriptor.required) {
if (currentArgument.descriptor.defaultValue == null && currentArgument.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.") "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. // Add default list with arguments if there can be extra free arguments.
if (skipExtraArguments) { if (skipExtraArguments) {
argument(ArgType.String, "").number() argument(ArgType.String, "").vararg()
} }
// Map declared options and arguments to maps. // 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): class MultipleArgument<T : Any> internal constructor(descriptor: ArgDescriptor<T, List<T>>, owner: CLIEntityWrapper):
Argument<List<T>>(owner) { Argument<List<T>>(owner) {
init { 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.") failAssertion("Argument with multiple values can't be initialized with descriptor for single one.")
} }
delegate = ArgumentMultipleValues(descriptor) delegate = ArgumentMultipleValues(descriptor)
@@ -97,11 +97,11 @@ class MultipleArgument<T : Any> internal constructor(descriptor: ArgDescriptor<T
/** /**
* Allow argument have several values. * 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> { fun <T : Any, TResult> AbstractSingleArgument<T, TResult>.multiple(value: Int): MultipleArgument<T> {
if (value != null && value == 1) { if (value < 2) {
error("number() modifier with value 1 is unavailable. It's already set to 1.") 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) { val newArgument = with((delegate as ParsingValue<T, T>).descriptor as ArgDescriptor) {
MultipleArgument(ArgDescriptor(type, fullName, value, description, listOfNotNull(defaultValue), 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 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. * Set default value for argument.
* *
@@ -130,6 +142,9 @@ fun <T: Any, TResult> AbstractSingleArgument<T, TResult>.default(value: T): Sing
* @param value default value. * @param value default value.
*/ */
fun <T: Any> MultipleArgument<T>.default(value: Collection<T>): MultipleArgument<T> { 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) { val newArgument = with((delegate as ParsingValue<T, List<T>>).descriptor as ArgDescriptor) {
MultipleArgument(ArgDescriptor(type, fullName, number, description, value.toList(), MultipleArgument(ArgDescriptor(type, fullName, number, description, value.toList(),
required, deprecatedWarning), owner) required, deprecatedWarning), owner)
@@ -141,7 +156,7 @@ fun <T: Any> MultipleArgument<T>.default(value: Collection<T>): MultipleArgument
/** /**
* Allow argument be unprovided in command line. * 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) { val newArgument = with((delegate as ParsingValue<T, T>).descriptor as ArgDescriptor) {
SingleNullableArgument(ArgDescriptor(type, fullName, number, description, defaultValue, SingleNullableArgument(ArgDescriptor(type, fullName, number, description, defaultValue,
false, deprecatedWarning), owner) false, deprecatedWarning), owner)
@@ -127,7 +127,7 @@ internal class ArgDescriptor<T : Any, TResult>(
init { init {
// Check arguments number correctness. // Check arguments number correctness.
number?.let { number?.let {
if (it < 0) if (it < 1)
error("Number of arguments for argument description $fullName should be greater than zero.") 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> { fun <T: Any, TResult> AbstractSingleOption<T, TResult>.default(value: T): SingleOption<T> {
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) { 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, SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, value, required, multiple, delimiter, deprecatedWarning), owner) 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> fun <T: Any, OptionType: MultipleOptionType>
MultipleOption<T, OptionType>.default(value: Collection<T>): MultipleOption<T, OptionType> { MultipleOption<T, OptionType>.default(value: Collection<T>): MultipleOption<T, OptionType> {
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) { val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
if (required) { if (value.isEmpty()) {
printWarning("required() is unneeded, because option with default value is defined.") error("Default value for option can't be empty collection.")
} }
MultipleOption<T, OptionType>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, MultipleOption<T, OptionType>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName,
shortName, description, value.toList(), shortName, description, value.toList(),
@@ -148,11 +145,8 @@ fun <T: Any, OptionType: MultipleOptionType>
/** /**
* Require option to be always provided in command line. * 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) { 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, SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName,
shortName, description, defaultValue, shortName, description, defaultValue,
true, multiple, delimiter, deprecatedWarning), owner) true, multiple, delimiter, deprecatedWarning), owner)
@@ -167,9 +161,6 @@ fun <T: Any, TResult> AbstractSingleOption<T, TResult>.required(): SingleOption<
fun <T: Any, OptionType: MultipleOptionType> fun <T: Any, OptionType: MultipleOptionType>
MultipleOption<T, OptionType>.required(): MultipleOption<T, OptionType> { MultipleOption<T, OptionType>.required(): MultipleOption<T, OptionType> {
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) { 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, MultipleOption<T, OptionType>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, defaultValue?.toList() ?: listOf(), description, defaultValue?.toList() ?: listOf(),
true, multiple, delimiter, deprecatedWarning), owner) true, multiple, delimiter, deprecatedWarning), owner)
@@ -26,7 +26,7 @@ class ArgumentsTests {
fun testArgumetsWithAnyNumberOfValues() { fun testArgumetsWithAnyNumberOfValues() {
val argParser = ArgParser("testParser") val argParser = ArgParser("testParser")
val output by argParser.argument(ArgType.String, "output", "Output file") 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", argParser.parse(arrayOf("out.txt", "input1.txt", "input2.txt", "input3.txt",
"input4.txt")) "input4.txt"))
assertEquals("out.txt", output) assertEquals("out.txt", output)
@@ -36,7 +36,7 @@ class ArgumentsTests {
@Test @Test
fun testArgumetsWithSeveralValues() { fun testArgumetsWithSeveralValues() {
val argParser = ArgParser("testParser") 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 output by argParser.argument(ArgType.String, "output", "Output file")
val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode") val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
argParser.parse(arrayOf("2", "-d", "3", "out.txt")) argParser.parse(arrayOf("2", "-d", "3", "out.txt"))
@@ -50,7 +50,7 @@ class ArgumentsTests {
@Test @Test
fun testSkippingExtraArguments() { fun testSkippingExtraArguments() {
val argParser = ArgParser("testParser", skipExtraArguments = true) 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 output by argParser.argument(ArgType.String, "output", "Output file")
val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode") val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
argParser.parse(arrayOf("2", "-d", "3", "out.txt", "something", "else", "in", "string")) argParser.parse(arrayOf("2", "-d", "3", "out.txt", "something", "else", "in", "string"))
@@ -13,7 +13,7 @@ class ErrorTests {
@Test @Test
fun testExtraArguments() { fun testExtraArguments() {
val argParser = ArgParser("testParser") 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 output by argParser.argument(ArgType.String, "output", "Output file")
val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode") val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
val exception = assertFailsWith<IllegalStateException> { argParser.parse( val exception = assertFailsWith<IllegalStateException> { argParser.parse(
@@ -18,7 +18,7 @@ class SubcommandsTests {
val output by argParser.option(ArgType.String, "output", "o", "Output file") val output by argParser.option(ArgType.String, "output", "o", "Output file")
class Summary: Subcommand("summary") { class Summary: Subcommand("summary") {
val invert by option(ArgType.Boolean, "invert", "i", "Invert results") 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 var result: Int = 0
override fun execute() { override fun execute() {
@@ -36,7 +36,7 @@ class SubcommandsTests {
@Test @Test
fun testCommonOptions() { fun testCommonOptions() {
abstract class CommonOptions(name: String): Subcommand(name) { 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") { class Summary: CommonOptions("summary") {
val invert by option(ArgType.Boolean, "invert", "i", "Invert results") val invert by option(ArgType.Boolean, "invert", "i", "Invert results")
@@ -74,7 +74,7 @@ class SubcommandsTests {
val argParser = ArgParser("testParser") val argParser = ArgParser("testParser")
class Summary: Subcommand("summary") { 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 var result: Int = 0
override fun execute() { override fun execute() {