Updated kotlinx.cli to release version (#3762)

This commit is contained in:
LepilkinaElena
2020-01-20 10:22:48 +03:00
committed by GitHub
parent bdeca55651
commit 6bca6d05c6
9 changed files with 453 additions and 232 deletions
@@ -41,11 +41,27 @@ internal class ArgumentsQueue(argumentsDescriptors: List<ArgDescriptor<*, *>>) {
}
/**
* Interface of argument value.
* A property delegate that provides access to the argument/option value.
*/
interface ArgumentValueDelegate<T> {
/**
* The value of an option or argument parsed from command line.
*
* Accessing this value before [ArgParser.parse] method is called will result in an exception.
*
* @see CLIEntity.value
*/
var value: T
/** Provides the value for the delegated property getter. Returns the [value] property.
* @throws IllegalStateException in case of accessing the value before [ArgParser.parse] method is called.
*/
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value
/** Sets the [value] to the [ArgumentValueDelegate.value] property from the delegated property setter.
* This operation is possible only after command line arguments were parsed with [ArgParser.parse]
* @throws IllegalStateException in case of resetting value before command line arguments are parsed.
*/
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
@@ -54,7 +70,6 @@ interface ArgumentValueDelegate<T> {
/**
* Abstract base class for subcommands.
*/
@SinceKotlin("1.3")
@ExperimentalCli
abstract class Subcommand(val name: String): ArgParser(name) {
/**
@@ -74,14 +89,18 @@ class ArgParserResult(val commandName: String)
/**
* Arguments parser.
*
* @property programName name of current program.
* @property useDefaultHelpShortName add or not -h flag for help message.
* @property prefixStyle style of expected options prefix.
* @property skipExtraArguments just skip extra arguments in command line string without producing error message.
* @property programName the name of the current program.
* @property useDefaultHelpShortName specifies whether to register "-h" option for printing the usage information.
* @property prefixStyle the style of option prefixing.
* @property skipExtraArguments specifies whether the extra unmatched arguments in a command line string
* can be skipped without producing an error message.
*/
open class ArgParser(val programName: String, var useDefaultHelpShortName: Boolean = true,
var prefixStyle: OPTION_PREFIX_STYLE = OPTION_PREFIX_STYLE.LINUX,
var skipExtraArguments: Boolean = false) {
open class ArgParser(
val programName: String,
var useDefaultHelpShortName: Boolean = true,
var prefixStyle: OptionPrefixStyle = OptionPrefixStyle.LINUX,
var skipExtraArguments: Boolean = false
) {
/**
* Map of options: key - full name of option, value - pair of descriptor and parsed values.
@@ -102,6 +121,11 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
*/
private val declaredArguments = mutableListOf<CLIEntityWrapper>()
/**
* State of parser. Stores last parsing result or null.
*/
private var parsingState: ArgParserResult? = null
/**
* Map of subcommands.
*/
@@ -116,7 +140,7 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
/**
* Used prefix form for full option form.
*/
private val optionFullFormPrefix = if (prefixStyle == OPTION_PREFIX_STYLE.LINUX) "--" else "-"
private val optionFullFormPrefix = if (prefixStyle == OptionPrefixStyle.LINUX) "--" else "-"
/**
* Used prefix form for short option form.
@@ -129,39 +153,66 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
protected val fullCommandName = mutableListOf(programName)
/**
* Origin of option/argument value.
*
* Possible values:
* SET_BY_USER - value of option was provided in command line string;
* SET_DEFAULT_VALUE - value of option wasn't provided in command line, but set using default value;
* UNSET - value of option is unset
* REDEFINED - value of option was redefined in source code after parsing.
* The way an option/argument has got its value.
*/
enum class ValueOrigin { SET_BY_USER, SET_DEFAULT_VALUE, UNSET, REDEFINED }
enum class ValueOrigin {
/* The value was parsed from command line arguments. */
SET_BY_USER,
/* The value was missing in command line, therefore the default value was used. */
SET_DEFAULT_VALUE,
/* The value is not initialized by command line values or by default values. */
UNSET,
/* The value was redefined after parsing manually (usually with the property setter). */
REDEFINED,
/* The value is undefined, because parsing wasn't called. */
UNDEFINED
}
/**
* Options prefix style.
*
* Possible values:
* LINUX - Linux style, for full forms of options "--", for short form - "-"
* JVM - JVM style, both for full and short forms of options "-"
* The style of option prefixing.
*/
enum class OPTION_PREFIX_STYLE { LINUX, JVM }
enum class OptionPrefixStyle {
/* Linux style: the full name of an option is prefixed with two hyphens "--" and the short name — with one "-". */
LINUX,
/* JVM style: both full and short names are prefixed with one hyphen "-". */
JVM,
}
@Deprecated("OPTION_PREFIX_STYLE is deprecated. Please, use OptionPrefixStyle.",
ReplaceWith("OptionPrefixStyle", "kotlinx.cli.OptionPrefixStyle"))
@Suppress("TOPLEVEL_TYPEALIASES_ONLY")
typealias OPTION_PREFIX_STYLE = OptionPrefixStyle
/**
* Add option with single possible value and get delegator to its value.
* Declares a named option and returns an object which can be used to access the option value
* after all arguments are parsed or to delegate a property for accessing the option value to.
*
* @param type argument type, one of [ArgType].
* @param fullName argument full name.
* @param shortName option short name.
* @param description text description of Argument.
* @param deprecatedWarning text message with information in case if option is deprecated.
* By default, the option supports only a single value, is optional, and has no default value,
* therefore its value's type is `T?`.
*
* You can alter the option properties by chaining extensions for the option type on the returned object:
* - [AbstractSingleOption.default] to provide a default value that is used when the option is not specified;
* - [SingleNullableOption.required] to make the option non-optional;
* - [AbstractSingleOption.delimiter] to allow specifying multiple values in one command line argument with a delimiter;
* - [AbstractSingleOption.multiple] to allow specifying the option several times.
*
* @param type The type describing how to parse an option value from a string,
* an instance of [ArgType], e.g. [ArgType.String] or [ArgType.Choice].
* @param fullName the full name of the option, can be omitted if the option name is inferred
* from the name of a property delegated to this option.
* @param shortName the short name of the option, `null` if the option cannot be specified in a short form.
* @param description the description of the option used when rendering the usage information.
* @param deprecatedWarning the deprecation message for the option.
* Specifying anything except `null` makes this option deprecated. The message is rendered in a help message and
* issued as a warning when the option is encountered when parsing command line arguments.
*/
fun <T : Any>option(type: ArgType<T>,
fullName: String? = null,
shortName: String ? = null,
description: String? = null,
deprecatedWarning: String? = null): SingleNullableOption<T> {
fun <T : Any> option(
type: ArgType<T>,
fullName: String? = null,
shortName: String ? = null,
description: String? = null,
deprecatedWarning: String? = null
): SingleNullableOption<T> {
val option = SingleNullableOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type,
fullName, shortName, description, deprecatedWarning = deprecatedWarning), CLIEntityWrapper())
option.owner.entity = option
@@ -176,38 +227,55 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
private fun inspectRequiredAndDefaultUsage() {
var previousArgument: ParsingValue<*, *>? = null
arguments.forEach { (_, currentArgument) ->
previousArgument?.let {
previousArgument?.let { previous ->
// Previous argument has default value.
it.descriptor.defaultValue?.let {
if (currentArgument.descriptor.defaultValue == null && currentArgument.descriptor.required) {
printError("Default value of argument ${previousArgument.descriptor.fullName} will be unused, " +
if (previous.descriptor.defaultValueSet) {
if (!currentArgument.descriptor.defaultValueSet && currentArgument.descriptor.required) {
error("Default value of argument ${previous.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) {
printError("Argument ${previousArgument.descriptor.fullName} will be always required, " +
if (!previous.descriptor.required) {
if (!currentArgument.descriptor.defaultValueSet && currentArgument.descriptor.required) {
error("Argument ${previous.descriptor.fullName} will be always required, " +
"because next argument ${currentArgument.descriptor.fullName} is always required.")
}
}
}
previousArgument = currentArgument
}
}
/**
* Add argument with single nullable value and get delegator to its value.
* Declares an argument and returns an object which can be used to access the argument value
* after all arguments are parsed or to delegate a property for accessing the argument value to.
*
* @param type argument type, one of [ArgType].
* @param fullName argument full name.
* @param description text description of argument.
* @param deprecatedWarning text message with information in case if argument is deprecated.
* By default, the argument supports only a single value, is required, and has no default value,
* therefore its value's type is `T`.
*
* You can alter the argument properties by chaining extensions for the argument type on the returned object:
* - [AbstractSingleArgument.default] to provide a default value that is used when the argument is not specified;
* - [SingleArgument.optional] to allow omitting the argument;
* - [AbstractSingleArgument.multiple] to require the argument to have exactly the number of values specified;
* - [AbstractSingleArgument.vararg] to allow specifying an unlimited number of values for the _last_ argument.
*
* @param type The type describing how to parse an option value from a string,
* an instance of [ArgType], e.g. [ArgType.String] or [ArgType.Choice].
* @param fullName the full name of the argument, can be omitted if the argument name is inferred
* from the name of a property delegated to this argument.
* @param description the description of the argument used when rendering the usage information.
* @param deprecatedWarning the deprecation message for the argument.
* Specifying anything except `null` makes this argument deprecated. The message is rendered in a help message and
* issued as a warning when the argument is encountered when parsing command line arguments.
*/
fun <T : Any>argument(type: ArgType<T>,
fullName: String? = null,
description: String? = null,
deprecatedWarning: String? = null) : SingleArgument<T> {
val argument = SingleArgument(ArgDescriptor(type, fullName, 1,
fun <T : Any> argument(
type: ArgType<T>,
fullName: String? = null,
description: String? = null,
deprecatedWarning: String? = null
) : SingleArgument<T, DefaultRequiredType.Required> {
val argument = SingleArgument<T, DefaultRequiredType.Required>(ArgDescriptor(type, fullName, 1,
description, deprecatedWarning = deprecatedWarning), CLIEntityWrapper())
argument.owner.entity = argument
declaredArguments.add(argument.owner)
@@ -215,16 +283,15 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
}
/**
* Add subcommands.
* Registers one or more subcommands.
*
* @param subcommandsList subcommands to add.
*/
@SinceKotlin("1.3")
@ExperimentalCli
fun subcommands(vararg subcommandsList: Subcommand) {
subcommandsList.forEach {
if (it.name in subcommands) {
printError("Subcommand with name ${it.name} was already defined.")
error("Subcommand with name ${it.name} was already defined.")
}
// Set same settings as main parser.
@@ -238,11 +305,11 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
}
/**
* Output error. Also adds help usage information for easy understanding of problem.
* Outputs an error message adding the usage information after it.
*
* @param message error message.
*/
fun printError(message: String): Nothing {
fun printError(message: String): Nothing {
error("$message\n${makeUsage()}")
}
@@ -292,21 +359,30 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
else null
/**
* Parse arguments.
* Parses the provided array of command line arguments.
* After a successful parsing, the options and arguments declared in this parser get their values and can be accessed
* with the properties delegated to them.
*
* @param args array with command line arguments.
* @param args the array with command line arguments.
*
* @return true if all arguments were parsed successfully, otherwise return false and print help message.
* @return an [ArgParserResult] if all arguments were parsed successfully.
* Otherwise, prints the usage information and terminates the program execution.
* @throws IllegalStateException in case of attempt of calling parsing several times.
*/
fun parse(args: Array<String>) = parse(args.asList())
fun parse(args: Array<String>): ArgParserResult = parse(args.asList())
protected fun parse(args: List<String>): ArgParserResult {
check(parsingState == null) { "Parsing of command line options can be called only once." }
// Add help option.
val helpDescriptor = if (useDefaultHelpShortName) OptionDescriptor<Boolean, Boolean>(optionFullFormPrefix,
optionShortFromPrefix, ArgType.Boolean,
"help", "h", "Usage info")
else OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix,
ArgType.Boolean, "help", description = "Usage info")
val helpDescriptor = if (useDefaultHelpShortName) OptionDescriptor<Boolean, Boolean>(
optionFullFormPrefix,
optionShortFromPrefix, ArgType.Boolean,
"help", "h", "Usage info"
)
else OptionDescriptor(
optionFullFormPrefix, optionShortFromPrefix,
ArgType.Boolean, "help", description = "Usage info"
)
val helpOption = SingleNullableOption(helpDescriptor, CLIEntityWrapper())
helpOption.owner.entity = helpOption
declaredOptions.add(helpOption.owner)
@@ -316,6 +392,10 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
argument(ArgType.String, "").vararg()
}
// Clean options and arguments maps.
options.clear()
arguments.clear()
// Map declared options and arguments to maps.
declaredOptions.forEachIndexed { index, option ->
val value = option.entity?.delegate as ParsingValue<*, *>
@@ -350,6 +430,12 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
// Make inspections for arguments.
inspectRequiredAndDefaultUsage()
listOf(arguments, options).forEach {
it.forEach { (_, value) ->
value.valueOrigin = ValueOrigin.UNSET
}
}
val argumentsQueue = ArgumentsQueue(arguments.map { it.value.descriptor as ArgDescriptor<*, *> })
var index = 0
@@ -363,8 +449,9 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
// Use parser for this subcommand.
subcommand.parse(args.slice(index + 1..args.size - 1))
subcommand.execute()
parsingState = ArgParserResult(name)
return ArgParserResult(name)
return parsingState!!
}
}
// Parse arguments from command line.
@@ -409,16 +496,19 @@ open class ArgParser(val programName: String, var useDefaultHelpShortName: Boole
if (value.isEmpty()) {
value.addDefaultValue()
}
if (value.valueOrigin != ValueOrigin.SET_BY_USER && value.descriptor.required) {
printError("Value for ${value.descriptor.textDescription} should be always provided in command line.")
}
}
} catch (exception: ParsingException) {
printError(exception.message!!)
}
return ArgParserResult(programName)
parsingState = ArgParserResult(programName)
return parsingState!!
}
/**
* Create message with usage description.
* Creates a message with the usage information.
*/
internal fun makeUsage(): String {
val result = StringBuilder()
@@ -23,7 +23,7 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
*
* @param value value
*/
abstract val conversion: (value: kotlin.String, name: kotlin.String)->T
abstract fun convert(value: kotlin.String, name: kotlin.String): T
/**
* Argument type for flags that can be only set/unset.
@@ -32,8 +32,8 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
override val description: kotlin.String
get() = ""
override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.Boolean
get() = { value, _ -> if (value == "false") false else true }
override fun convert(value: kotlin.String, name: kotlin.String): kotlin.Boolean =
value != "false"
}
/**
@@ -43,8 +43,7 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
override val description: kotlin.String
get() = "{ String }"
override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.String
get() = { value, _ -> value }
override fun convert(value: kotlin.String, name: kotlin.String): kotlin.String = value
}
/**
@@ -54,9 +53,9 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
override val description: kotlin.String
get() = "{ Int }"
override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.Int
get() = { value, name -> value.toIntOrNull()
?: throw ParsingException("Option $name is expected to be integer number. $value is provided.") }
override fun convert(value: kotlin.String, name: kotlin.String): kotlin.Int =
value.toIntOrNull()
?: throw ParsingException("Option $name is expected to be integer number. $value is provided.")
}
/**
@@ -66,9 +65,9 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
override val description: kotlin.String
get() = "{ Double }"
override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.Double
get() = { value, name -> value.toDoubleOrNull()
?: throw ParsingException("Option $name is expected to be double number. $value is provided.") }
override fun convert(value: kotlin.String, name: kotlin.String): kotlin.Double =
value.toDoubleOrNull()
?: throw ParsingException("Option $name is expected to be double number. $value is provided.")
}
/**
@@ -78,9 +77,9 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
override val description: kotlin.String
get() = "{ Value should be one of $values }"
override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.String
get() = { value, name -> if (value in values) value
else throw ParsingException("Option $name is expected to be one of $values. $value is provided.") }
override fun convert(value: kotlin.String, name: kotlin.String): kotlin.String =
if (value in values) value
else throw ParsingException("Option $name is expected to be one of $values. $value is provided.")
}
}
@@ -4,8 +4,6 @@
*/
package kotlinx.cli
import kotlin.reflect.KProperty
/**
* Parsing value of option/argument.
*/
@@ -18,8 +16,8 @@ internal abstract class ParsingValue<T: Any, TResult: Any>(val descriptor: Descr
/**
* Value origin.
*/
var valueOrigin = ArgParser.ValueOrigin.UNSET
protected set
var valueOrigin = ArgParser.ValueOrigin.UNDEFINED
internal set
/**
* Check if values of argument are empty.
@@ -76,9 +74,6 @@ internal abstract class ParsingValue<T: Any, TResult: Any>(val descriptor: Descr
* Set default value to option.
*/
fun addDefaultValue() {
if (!descriptor.defaultValueSet && descriptor.required) {
throw ParsingException("Please, provide value for ${descriptor.textDescription}. It should be always set.")
}
if (descriptor.defaultValueSet) {
parsedValue = descriptor.defaultValue!!
valueOrigin = ArgParser.ValueOrigin.SET_DEFAULT_VALUE
@@ -105,7 +100,7 @@ internal abstract class AbstractArgumentSingleValue<T: Any>(descriptor: Descript
override fun saveValue(stringValue: String) {
if (!valueIsInitialized()) {
parsedValue = descriptor.type.conversion(stringValue, descriptor.fullName!!)
parsedValue = descriptor.type.convert(stringValue, descriptor.fullName!!)
valueOrigin = ArgParser.ValueOrigin.SET_BY_USER
} else {
throw ParsingException("Try to provide more than one value $parsedValue and $stringValue for ${descriptor.fullName}.")
@@ -165,7 +160,7 @@ internal class ArgumentMultipleValues<T : Any>(descriptor: Descriptor<T, List<T>
set(value) = setDelegatedValue(value)
override fun saveValue(stringValue: String) {
addedValue.add(descriptor.type.conversion(stringValue, descriptor.fullName!!))
addedValue.add(descriptor.type.convert(stringValue, descriptor.fullName!!))
valueOrigin = ArgParser.ValueOrigin.SET_BY_USER
}
@@ -9,46 +9,98 @@ import kotlin.reflect.KProperty
internal data class CLIEntityWrapper(var entity: CLIEntity<*>? = null)
/**
* Command line entity.
* Base interface for all possible types of entities with default and required values.
* Provides limitations for API that is accessible for different options/arguments types.
* Allows to save the reason why option/argument can(or can't) be omitted in command line.
*
* @property owner parser which owns current entity.
* @see [SingleOption], [MultipleOption], [SingleArgument], [MultipleArgument].
*/
abstract class CLIEntity<TResult> internal constructor(internal val owner: CLIEntityWrapper) {
interface DefaultRequiredType {
/**
* Wrapper for element - read only property.
* Needed to close set of variable [cliElement].
* Type of an entity with default value.
*/
lateinit var delegate: ArgumentValueDelegate<TResult>
internal set
class Default : DefaultRequiredType
/**
* Value of entity.
* Type of an entity which value should always be provided in command line.
*/
class Required : DefaultRequiredType
/**
* Type of entity which is optional and has no default value.
*/
class None : DefaultRequiredType
}
/**
* The base class for a command line argument or an option.
*/
abstract class CLIEntity<TResult> internal constructor(val delegate: ArgumentValueDelegate<TResult>,
internal val owner: CLIEntityWrapper) {
/**
* The value of the option or argument parsed from command line.
*
* Accessing this property before it gets its value will result in an exception.
* You can use [valueOrigin] property to find out whether the property has been already set.
*
* @see ArgumentValueDelegate.value
*/
var value: TResult
get() = delegate.value
set(value) { delegate.value = value }
set(value) {
check((delegate as ParsingValue<*, *>).valueOrigin != ArgParser.ValueOrigin.UNDEFINED) {
"Resetting value of option/argument is only possible after parsing command line arguments." +
" ArgParser.parse(...) method should be called before"
}
delegate.value = value
}
/**
* Origin of argument value.
* The origin of the option/argument value.
*/
val valueOrigin: ArgParser.ValueOrigin
get() = (delegate as ParsingValue<*, *>).valueOrigin
private var delegateProvided = false
/**
* Returns the delegate object for property delegation and initializes it with the name of the delegated property.
*
* This operator makes it possible to delegate a property to this instance. It returns [delegate] object
* to be used as an actual delegate and uses the name of the delegated property to initialize the full name
* of the option/argument if it wasn't done during construction of that option/argument.
*
* @throws IllegalStateException in case of trying to use same delegate several times.
*/
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueDelegate<TResult> {
check(!delegateProvided) {
"There is already used delegate for ${(delegate as ParsingValue<*, *>).descriptor.fullName}."
}
(delegate as ParsingValue<*, *>).provideName(prop.name)
delegateProvided = true
return delegate
}
}
/**
* Argument instance.
* The base class for command line arguments.
*
* You can use [ArgParser.argument] function to declare an argument.
*/
abstract class Argument<TResult> internal constructor(owner: CLIEntityWrapper): CLIEntity<TResult>(owner)
abstract class Argument<TResult> internal constructor(delegate: ArgumentValueDelegate<TResult>,
owner: CLIEntityWrapper): CLIEntity<TResult>(delegate, owner)
/**
* Common single argument instance.
* The base class of an argument with a single value.
*
* A non-optional argument or an optional argument with a default value is represented with the [SingleArgument] inheritor.
* An optional argument having nullable value is represented with the [SingleNullableArgument] inheritor.
*/
abstract class AbstractSingleArgument<T: Any, TResult> internal constructor(owner: CLIEntityWrapper): Argument<TResult>(owner) {
// TODO: investigate if we can collapse two inheritors into the single base class and specialize extensions by TResult upper bound
abstract class AbstractSingleArgument<T: Any, TResult, DefaultRequired: DefaultRequiredType> internal constructor(
delegate: ArgumentValueDelegate<TResult>,
owner: CLIEntityWrapper):
Argument<TResult>(delegate, owner) {
/**
* Check descriptor for this kind of argument.
*/
@@ -60,51 +112,55 @@ abstract class AbstractSingleArgument<T: Any, TResult> internal constructor(owne
}
/**
* Argument with single non-nullable value.
* A non-optional argument or an optional argument with a default value.
*
* The [value] of such argument is non-null.
*/
class SingleArgument<T : Any> internal constructor(descriptor: ArgDescriptor<T, T>, owner: CLIEntityWrapper):
AbstractSingleArgument<T, T>(owner) {
class SingleArgument<T : Any, DefaultRequired: DefaultRequiredType> internal constructor(descriptor: ArgDescriptor<T, T>,
owner: CLIEntityWrapper):
AbstractSingleArgument<T, T, DefaultRequired>(ArgumentSingleValue(descriptor), owner) {
init {
checkDescriptor(descriptor)
delegate = ArgumentSingleValue(descriptor)
}
}
/**
* Argument with single nullable value.
* An optional argument with nullable [value].
*/
class SingleNullableArgument<T : Any> internal constructor(descriptor: ArgDescriptor<T, T>, owner: CLIEntityWrapper):
AbstractSingleArgument<T, T?>(owner){
AbstractSingleArgument<T, T?, DefaultRequiredType.None>(ArgumentSingleNullableValue(descriptor), owner) {
init {
checkDescriptor(descriptor)
delegate = ArgumentSingleNullableValue(descriptor)
}
}
/**
* Argument with multiple values.
* An argument that allows several values to be provided in command line string.
*
* The [value] property of such argument has type `List<T>`.
*/
class MultipleArgument<T : Any> internal constructor(descriptor: ArgDescriptor<T, List<T>>, owner: CLIEntityWrapper):
Argument<List<T>>(owner) {
class MultipleArgument<T : Any, DefaultRequired: DefaultRequiredType> internal constructor(
descriptor: ArgDescriptor<T, List<T>>, owner: CLIEntityWrapper):
Argument<List<T>>(ArgumentMultipleValues(descriptor), owner) {
init {
if (descriptor.number != null && descriptor.number < 2) {
failAssertion("Argument with multiple values can't be initialized with descriptor for single one.")
}
delegate = ArgumentMultipleValues(descriptor)
}
}
/**
* Allow argument have several values.
* Allows the argument to have several values specified in command line string.
*
* @param value number of arguments are expected.
* @param number the exact number of values expected for this argument, but at least 2.
*
* @throws IllegalArgumentException if number of values expected for this argument less than 2.
*/
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.")
}
fun <T : Any, TResult, DefaultRequired: DefaultRequiredType>
AbstractSingleArgument<T, TResult, DefaultRequired>.multiple(number: Int): MultipleArgument<T, DefaultRequired> {
require(number >= 2) { "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),
MultipleArgument<T, DefaultRequired>(ArgDescriptor(type, fullName, number, description, listOfNotNull(defaultValue),
required, deprecatedWarning), owner)
}
owner.entity = newArgument
@@ -112,11 +168,12 @@ fun <T : Any, TResult> AbstractSingleArgument<T, TResult>.multiple(value: Int):
}
/**
* Allow argument have several values.
* Allows the last argument to take all the trailing values in command line string.
*/
fun <T : Any, TResult> AbstractSingleArgument<T, TResult>.vararg(): MultipleArgument<T> {
fun <T : Any, TResult, DefaultRequired: DefaultRequiredType> AbstractSingleArgument<T, TResult, DefaultRequired>.vararg():
MultipleArgument<T, DefaultRequired> {
val newArgument = with((delegate as ParsingValue<T, T>).descriptor as ArgDescriptor) {
MultipleArgument(ArgDescriptor(type, fullName, null, description, listOfNotNull(defaultValue),
MultipleArgument<T, DefaultRequired>(ArgDescriptor(type, fullName, null, description, listOfNotNull(defaultValue),
required, deprecatedWarning), owner)
}
owner.entity = newArgument
@@ -124,29 +181,35 @@ fun <T : Any, TResult> AbstractSingleArgument<T, TResult>.vararg(): MultipleArgu
}
/**
* Set default value for argument.
* Specifies the default value for the argument, that will be used when no value is provided for the argument
* in command line string.
*
* @param value default value.
* Argument becomes optional, because value for it is set even if it isn't provided in command line.
*
* @param value the default value.
*/
fun <T: Any, TResult> AbstractSingleArgument<T, TResult>.default(value: T): SingleArgument<T> {
fun <T: Any> SingleNullableArgument<T>.default(value: T): SingleArgument<T, DefaultRequiredType.Default> {
val newArgument = with((delegate as ParsingValue<T, T>).descriptor as ArgDescriptor) {
SingleArgument(ArgDescriptor(type, fullName, number, description, value, required, deprecatedWarning), owner)
SingleArgument<T, DefaultRequiredType.Default>(ArgDescriptor(type, fullName, number, description, value,
false, deprecatedWarning), owner)
}
owner.entity = newArgument
return newArgument
}
/**
* Set default value for argument.
* Specifies the default value for the argument with multiple values, that will be used when no values are provided
* for the argument in command line string.
*
* @param value default value.
* Argument becomes optional, because value for it is set even if it isn't provided in command line.
*
* @param value the default value, must be a non-empty collection.
*/
fun <T: Any> MultipleArgument<T>.default(value: Collection<T>): MultipleArgument<T> {
if (value.isEmpty()) {
error("Default value for argument can't be empty collection.")
}
fun <T: Any> MultipleArgument<T, DefaultRequiredType.None>.default(value: Collection<T>):
MultipleArgument<T, DefaultRequiredType.Default> {
require (value.isNotEmpty()) { "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(),
MultipleArgument<T, DefaultRequiredType.Default>(ArgDescriptor(type, fullName, number, description, value.toList(),
required, deprecatedWarning), owner)
}
owner.entity = newArgument
@@ -154,9 +217,13 @@ fun <T: Any> MultipleArgument<T>.default(value: Collection<T>): MultipleArgument
}
/**
* Allow argument be unprovided in command line.
* Allows the argument to have no value specified in command line string.
*
* The value of the argument is `null` in case if no value was specified in command line string.
*
* Note that only trailing arguments can be optional, i.e. no required arguments can follow optional ones.
*/
fun <T: Any> SingleArgument<T>.optional(): SingleNullableArgument<T> {
fun <T: Any> SingleArgument<T, DefaultRequiredType.Required>.optional(): SingleNullableArgument<T> {
val newArgument = with((delegate as ParsingValue<T, T>).descriptor as ArgDescriptor) {
SingleNullableArgument(ArgDescriptor(type, fullName, number, description, defaultValue,
false, deprecatedWarning), owner)
@@ -166,15 +233,19 @@ fun <T: Any> SingleArgument<T>.optional(): SingleNullableArgument<T> {
}
/**
* Allow argument be unprovided in command line.
* Allows the argument with multiple values to have no values specified in command line string.
*
* The value of the argument is an empty list in case if no value was specified in command line string.
*
* Note that only trailing arguments can be optional: no required arguments can follow the optional ones.
*/
fun <T: Any> MultipleArgument<T>.optional(): MultipleArgument<T> {
fun <T: Any> MultipleArgument<T, DefaultRequiredType.Required>.optional(): MultipleArgument<T, DefaultRequiredType.None> {
val newArgument = with((delegate as ParsingValue<T, List<T>>).descriptor as ArgDescriptor) {
MultipleArgument(ArgDescriptor(type, fullName, number, description,
MultipleArgument<T, DefaultRequiredType.None>(ArgDescriptor(type, fullName, number, description,
defaultValue?.toList() ?: listOf(), false, deprecatedWarning), owner)
}
owner.entity = newArgument
return newArgument
}
fun failAssertion(message: String): Nothing = throw AssertionError(message)
internal fun failAssertion(message: String): Nothing = throw AssertionError(message)
@@ -11,7 +11,7 @@ package kotlinx.cli
* @property fullName option/argument full name.
* @property description text description of option/argument.
* @property defaultValue default value for option/argument.
* @property required if option/argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @property required if option/argument is required or not. If it's required and not provided in command line, error will be generated.
* @property deprecatedWarning text message with information in case if option is deprecated.
*/
internal abstract class Descriptor<T : Any, TResult>(val type: ArgType<T>,
@@ -36,7 +36,7 @@ internal abstract class Descriptor<T : Any, TResult>(val type: ArgType<T>,
*/
fun valueDescription(value: TResult?) = value?.let {
if (it is List<*> && it.isNotEmpty())
" [${it.joinToString { it.toString() }}]"
" [${it.joinToString()}]"
else if (it !is List<*>)
" [$it]"
else null
@@ -62,7 +62,7 @@ internal abstract class Descriptor<T : Any, TResult>(val type: ArgType<T>,
* @property shortName option short name.
* @property description text description of option.
* @property defaultValue default value for option.
* @property required if option is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @property required if option is required or not. If it's required and not provided in command line, error will be generated.
* @property multiple if option can be repeated several times in command line with different values. All values are stored.
* @property delimiter delimiter that separate option provided as one string to several values.
* @property deprecatedWarning text message with information in case if option is deprecated.
@@ -90,9 +90,9 @@ internal class OptionDescriptor<T : Any, TResult>(
result.append(" $optionFullFormPrefix$fullName")
shortName?.let { result.append(", $optionShortFromPrefix$it") }
valueDescription(defaultValue)?.let {
result.append("$it")
result.append(it)
}
description?.let {result.append(" -> ${it}")}
description?.let {result.append(" -> $it")}
if (required) result.append(" (always required)")
result.append(" ${type.description}")
deprecatedWarning?.let { result.append(" Warning: $it") }
@@ -140,9 +140,9 @@ internal class ArgDescriptor<T : Any, TResult>(
val result = StringBuilder()
result.append(" ${fullName}")
valueDescription(defaultValue)?.let {
result.append("$it")
result.append(it)
}
description?.let { result.append(" -> ${it}") }
description?.let { result.append(" -> $it") }
if (!required) result.append(" (optional)")
result.append(" ${type.description}")
deprecatedWarning?.let { result.append(" Warning: $it") }
@@ -32,5 +32,5 @@ import kotlin.annotation.AnnotationTarget.*
PROPERTY_SETTER,
TYPEALIAS
)
@SinceKotlin("1.3")
public annotation class ExperimentalCli
@@ -4,37 +4,49 @@
*/
package kotlinx.cli
import kotlin.reflect.KProperty
/**
* Base interface for all possible types of options with multiple values.
* Provides limitations for API that is accessible for options with several values.
* Allows to save the way of providing several values in command line.
*
* @see [MultipleOption]
*/
interface MultipleOptionType
interface MultipleOptionType {
/**
* Type of an option with multiple values allowed to be provided several times in command line.
*/
class Repeated : MultipleOptionType
/**
* Type of an option with multiple values allowed to be provided using delimiter in one command line value.
*/
class Delimited : MultipleOptionType
/**
* Type of an option with multiple values allowed to be provided several times in command line
* both with specifying several values and with delimiter.
*/
class RepeatedDelimited : MultipleOptionType
}
/**
* Type of option with multiple values that can be provided several times in command line.
* The base class for command line options.
*
* You can use [ArgParser.option] function to declare an option.
*/
class RepeatedOption: MultipleOptionType
abstract class Option<TResult> internal constructor(delegate: ArgumentValueDelegate<TResult>,
owner: CLIEntityWrapper) : CLIEntity<TResult>(delegate, owner)
/**
* Type of option with multiple values that are provided using delimiter.
* The base class of an option with a single value.
*
* A required option or an option with a default value is represented with the [SingleOption] inheritor.
* An option having nullable value is represented with the [SingleNullableOption] inheritor.
*/
class DelimitedOption: MultipleOptionType
/**
* Type of option with multiple values that can be both provided several times in command line and using delimiter.
*/
class RepeatedDelimitedOption: MultipleOptionType
/**
* Option instance.
*/
abstract class Option<TResult> internal constructor(owner: CLIEntityWrapper): CLIEntity<TResult>(owner)
/**
* Common single option instance.
*/
abstract class AbstractSingleOption<T: Any, TResult> internal constructor(owner: CLIEntityWrapper): Option<TResult>(owner) {
abstract class AbstractSingleOption<T : Any, TResult, DefaultRequired: DefaultRequiredType> internal constructor(
delegate: ArgumentValueDelegate<TResult>,
owner: CLIEntityWrapper) :
Option<TResult>(delegate, owner) {
/**
* Check descriptor for this kind of option.
*/
@@ -46,154 +58,208 @@ abstract class AbstractSingleOption<T: Any, TResult> internal constructor(owner:
}
/**
* Option wit single non-nullable value.
* A required option or an option with a default value.
*
* The [value] of such option is non-null.
*/
class SingleOption<T : Any> internal constructor(descriptor: OptionDescriptor<T, T>, owner: CLIEntityWrapper):
AbstractSingleOption<T, T>(owner) {
class SingleOption<T : Any, DefaultType: DefaultRequiredType> internal constructor(descriptor: OptionDescriptor<T, T>,
owner: CLIEntityWrapper) :
AbstractSingleOption<T, T, DefaultRequiredType>(ArgumentSingleValue(descriptor), owner) {
init {
checkDescriptor(descriptor)
delegate = ArgumentSingleValue(descriptor)
}
}
/**
* Option with single nullable value.
* An option with nullable [value].
*/
class SingleNullableOption<T : Any> internal constructor(descriptor: OptionDescriptor<T, T>, owner: CLIEntityWrapper):
AbstractSingleOption<T, T?>(owner) {
class SingleNullableOption<T : Any> internal constructor(descriptor: OptionDescriptor<T, T>, owner: CLIEntityWrapper) :
AbstractSingleOption<T, T?, DefaultRequiredType.None>(ArgumentSingleNullableValue(descriptor), owner) {
init {
checkDescriptor(descriptor)
delegate = ArgumentSingleNullableValue(descriptor)
}
}
/**
* Option with multiple values.
* An option that allows several values to be provided in command line string.
*
* The [value] property of such option has type `List<T>`.
*/
class MultipleOption<T : Any, OptionType: MultipleOptionType> internal constructor(descriptor: OptionDescriptor<T, List<T>>, owner: CLIEntityWrapper):
Option<List<T>>(owner) {
class MultipleOption<T : Any, OptionType : MultipleOptionType, DefaultType: DefaultRequiredType> internal constructor(
descriptor: OptionDescriptor<T, List<T>>,
owner: CLIEntityWrapper
) :
Option<List<T>>( ArgumentMultipleValues(descriptor), owner) {
init {
if (!descriptor.multiple && descriptor.delimiter == null) {
failAssertion("Option with multiple values can't be initialized with descriptor for single one.")
}
delegate = ArgumentMultipleValues(descriptor)
}
}
/**
* Allow option have several values.
* Allows the option to have several values specified in command line string.
* Number of values is unlimited.
*/
fun <T : Any, TResult> AbstractSingleOption<T, TResult>.multiple(): MultipleOption<T, RepeatedOption> {
fun <T : Any, TResult, DefaultType: DefaultRequiredType> AbstractSingleOption<T, TResult, DefaultType>.multiple():
MultipleOption<T, MultipleOptionType.Repeated, DefaultType> {
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
MultipleOption<T, RepeatedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
MultipleOption<T, MultipleOptionType.Repeated, DefaultType>(
OptionDescriptor(
optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, listOfNotNull(defaultValue),
required, true, delimiter, deprecatedWarning), owner)
required, true, delimiter, deprecatedWarning
), owner
)
}
owner.entity = newOption
return newOption
}
/**
* Allow option have several values.
* Allows the option to have several values specified in command line string.
* Number of values is unlimited.
*/
fun <T : Any> MultipleOption<T, DelimitedOption>.multiple(): MultipleOption<T, RepeatedDelimitedOption> {
fun <T : Any, DefaultType: DefaultRequiredType> MultipleOption<T, MultipleOptionType.Delimited, DefaultType>.multiple():
MultipleOption<T, MultipleOptionType.RepeatedDelimited, DefaultRequiredType> {
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
if (multiple) {
error("Try to use modifier multiple() twice on option ${fullName ?: ""}")
}
MultipleOption<T, RepeatedDelimitedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
MultipleOption<T, MultipleOptionType.RepeatedDelimited, DefaultRequiredType>(
OptionDescriptor(
optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, defaultValue?.toList() ?: listOf(),
required, true, delimiter, deprecatedWarning), owner)
required, true, delimiter, deprecatedWarning
), owner
)
}
owner.entity = newOption
return newOption
}
/**
* Set default option value.
* Specifies the default value for the option, that will be used when no value is provided for it
* in command line string.
*
* @param value default value.
* @param value the default value.
*/
fun <T: Any, TResult> AbstractSingleOption<T, TResult>.default(value: T): SingleOption<T> {
fun <T : Any> SingleNullableOption<T>.default(value: T): SingleOption<T, DefaultRequiredType.Default> {
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, value, required, multiple, delimiter, deprecatedWarning), owner)
SingleOption<T, DefaultRequiredType.Default>(
OptionDescriptor(
optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, value, required, multiple, delimiter, deprecatedWarning
), owner
)
}
owner.entity = newOption
return newOption
}
/**
* Set default option value.
* Specifies the default value for the option with multiple values, that will be used when no values are provided
* for it in command line string.
*
* @param value default value.
* @param value the default value, must be a non-empty collection.
* @throws IllegalArgumentException if provided default value is empty collection.
*/
fun <T: Any, OptionType: MultipleOptionType>
MultipleOption<T, OptionType>.default(value: Collection<T>): MultipleOption<T, OptionType> {
fun <T : Any, OptionType : MultipleOptionType>
MultipleOption<T, OptionType, DefaultRequiredType.None>.default(value: Collection<T>):
MultipleOption<T, OptionType, DefaultRequiredType.Default> {
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
if (value.isEmpty()) {
error("Default value for option can't be empty collection.")
}
MultipleOption<T, OptionType>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName,
require(value.isNotEmpty()) { "Default value for option can't be empty collection." }
MultipleOption<T, OptionType, DefaultRequiredType.Default>(
OptionDescriptor(
optionFullFormPrefix, optionShortFromPrefix, type, fullName,
shortName, description, value.toList(),
required, multiple, delimiter, deprecatedWarning), owner)
required, multiple, delimiter, deprecatedWarning
), owner
)
}
owner.entity = newOption
return newOption
}
/**
* Require option to be always provided in command line.
* Requires the option to be always provided in command line.
*/
fun <T: Any> SingleNullableOption<T>.required(): SingleOption<T> {
fun <T : Any> SingleNullableOption<T>.required(): SingleOption<T, DefaultRequiredType.Required> {
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName,
SingleOption<T, DefaultRequiredType.Required>(
OptionDescriptor(
optionFullFormPrefix, optionShortFromPrefix, type, fullName,
shortName, description, defaultValue,
true, multiple, delimiter, deprecatedWarning), owner)
true, multiple, delimiter, deprecatedWarning
), owner
)
}
owner.entity = newOption
return newOption
}
/**
* Require option to be always provided in command line.
* Requires the option to be always provided in command line.
*/
fun <T: Any, OptionType: MultipleOptionType>
MultipleOption<T, OptionType>.required(): MultipleOption<T, OptionType> {
fun <T : Any, OptionType : MultipleOptionType>
MultipleOption<T, OptionType, DefaultRequiredType.None>.required():
MultipleOption<T, OptionType, DefaultRequiredType.Required> {
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
MultipleOption<T, OptionType>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
MultipleOption<T, OptionType, DefaultRequiredType.Required>(
OptionDescriptor(
optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, defaultValue?.toList() ?: listOf(),
true, multiple, delimiter, deprecatedWarning), owner)
true, multiple, delimiter, deprecatedWarning
), owner
)
}
owner.entity = newOption
return newOption
}
/**
* Allow provide several options using [delimiter].
* Allows the option to have several values joined with [delimiter] specified in command line string.
* Number of values is unlimited.
*
* @param delimiterValue delimiter used to separate string value to option values.
* The value of the argument is an empty list in case if no value was specified in command line string.
*
* @param delimiterValue delimiter used to separate string value to option values list.
*/
fun <T : Any, TResult> AbstractSingleOption<T, TResult>.delimiter(delimiterValue: String): MultipleOption<T, DelimitedOption> {
fun <T : Any, DefaultRequired: DefaultRequiredType> AbstractSingleOption<T, *, DefaultRequired>.delimiter(
delimiterValue: String):
MultipleOption<T, MultipleOptionType.Delimited, DefaultRequired> {
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
MultipleOption<T, DelimitedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
MultipleOption<T, MultipleOptionType.Delimited, DefaultRequired>(
OptionDescriptor(
optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, listOfNotNull(defaultValue),
required, multiple, delimiterValue, deprecatedWarning), owner)
required, multiple, delimiterValue, deprecatedWarning
), owner
)
}
owner.entity = newOption
return newOption
}
/**
* Allow provide several options using [delimiter].
* Allows the option to have several values joined with [delimiter] specified in command line string.
* Number of values is unlimited.
*
* @param delimiterValue delimiter used to separate string value to option values.
* The value of the argument is an empty list in case if no value was specified in command line string.
*
* @param delimiterValue delimiter used to separate string value to option values list.
*/
fun <T : Any> MultipleOption<T, RepeatedOption>.delimiter(delimiterValue: String): MultipleOption<T, RepeatedDelimitedOption> {
fun <T : Any, DefaultRequired: DefaultRequiredType> MultipleOption<T, MultipleOptionType.Repeated, DefaultRequired>.delimiter(
delimiterValue: String):
MultipleOption<T, MultipleOptionType.RepeatedDelimited, DefaultRequired> {
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
MultipleOption<T, RepeatedDelimitedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
MultipleOption<T, MultipleOptionType.RepeatedDelimited, DefaultRequired>(
OptionDescriptor(
optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, defaultValue?.toList() ?: listOf(),
required, multiple, delimiterValue, deprecatedWarning), owner)
required, multiple, delimiterValue, deprecatedWarning
), owner
)
}
owner.entity = newOption
return newOption
@@ -28,6 +28,8 @@ class HelpTests {
val user by argParser.option(ArgType.String, shortName = "u", description = "User access information for authorization")
argParser.parse(arrayOf("main.txt"))
val helpOutput = argParser.makeUsage().trimIndent()
@Suppress("CanBeVal") // can't be val in order to build expectedOutput only in run time.
var epsDefault = 1.0
val expectedOutput = """
Usage: test options_list
Arguments:
@@ -35,7 +37,7 @@ Arguments:
compareToReport -> Report to compare to (optional) { String }
Options:
--output, -o -> Output file { String }
--eps, -e [1.0] -> Meaningful performance changes { Double }
--eps, -e [$epsDefault] -> Meaningful performance changes { Double }
--short, -s [false] -> Show short version of report
--renders, -r [text] -> Renders for showing information { Value should be one of [text, html, teamcity, statistics, metrics] }
--user, -u -> User access information for authorization { String }
@@ -5,8 +5,6 @@
package kotlinx.cli
import kotlinx.cli.ArgParser
import kotlinx.cli.ArgType
import kotlin.test.*
class OptionsTests {
@@ -32,7 +30,7 @@ class OptionsTests {
@Test
fun testJavaPrefix() {
val argParser = ArgParser("testParser", prefixStyle = ArgParser.OPTION_PREFIX_STYLE.JVM)
val argParser = ArgParser("testParser", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
val output by argParser.option(ArgType.String, "output", "o", "Output file")
val input by argParser.option(ArgType.String, "input", "i", "Input file")
argParser.parse(arrayOf("-output", "out.txt", "-i", "input.txt"))