implement optional variable argument/option ranges, refactor exception handling
adapt required/optional argument handling implement bounded/unbounded variable argument handling
This commit is contained in:
@@ -31,7 +31,7 @@ class Context internal constructor(
|
||||
operator fun Command.Key.getValue(thisRef: Any?, property: KProperty<*>): String = commands[index].name
|
||||
|
||||
private val <T : Any> Argument<T>.index: Int
|
||||
get() = command.arguments.indexOf(this)
|
||||
get() = command.arguments.takeWhile { it != this }.sumOf { it.count }
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val <T : Any> Argument<T>.value: T?
|
||||
@@ -39,7 +39,7 @@ class Context internal constructor(
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val <T : Any> Argument.Variable<T>.values: List<T>
|
||||
get() = arguments.drop(index).map { it.value as T }
|
||||
get() = arguments.drop(index).take(count).map { it.value as T }
|
||||
|
||||
operator fun <T : Any> Argument.Required<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value!!
|
||||
operator fun <T : Any> Argument.OrElse<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value ?: other
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.infendro.cli.command.context
|
||||
|
||||
import com.infendro.cli.command.Argument
|
||||
import com.infendro.cli.command.Command
|
||||
import com.infendro.cli.command.Option
|
||||
import com.infendro.cli.command.context.Context.*
|
||||
import com.infendro.cli.exception.run.*
|
||||
|
||||
@@ -25,7 +23,7 @@ internal object Parser {
|
||||
val c = command.commands.firstOrNull { it.name == current || it.fallback }
|
||||
if (c == null) {
|
||||
when {
|
||||
command.arguments.isEmpty() -> throw UnknownCommandException(current)
|
||||
command.arguments.isEmpty() -> throw UnknownCommand(current)
|
||||
else -> break
|
||||
}
|
||||
}
|
||||
@@ -39,6 +37,7 @@ internal object Parser {
|
||||
// arguments and options
|
||||
var endOfOptions = false
|
||||
var argumentIndex = 0
|
||||
var argumentCount = 0
|
||||
while (i < args.size) {
|
||||
val dashes = args[i].takeWhile { it == '-' }.count()
|
||||
val current = args[i].drop(dashes)
|
||||
@@ -47,13 +46,16 @@ internal object Parser {
|
||||
// argument
|
||||
endOfOptions || dashes == 0 -> {
|
||||
val argument = command.arguments.getOrNull(argumentIndex)
|
||||
?: throw TooManyArgumentsException()
|
||||
?: throw UnexpectedArgument()
|
||||
|
||||
if (argument !is Argument.Variable)
|
||||
argumentIndex++
|
||||
|
||||
val value = argument.parser.parse(current) as Any
|
||||
val value = argument.parser.parse(current)
|
||||
arg += Arg(value)
|
||||
argumentCount++
|
||||
|
||||
if (argumentCount == argument.count) {
|
||||
argumentCount = 0
|
||||
argumentIndex++
|
||||
}
|
||||
}
|
||||
|
||||
// option
|
||||
@@ -88,12 +90,12 @@ internal object Parser {
|
||||
|
||||
for (name in names) {
|
||||
val option = command.options.firstOrNull { name in it.names }
|
||||
?: throw UnknownOptionException(name)
|
||||
?: throw UnknownOption(name)
|
||||
|
||||
val value = when {
|
||||
text != null -> option.parser.parse(text)
|
||||
option.flag -> option.fallback!!
|
||||
else -> throw MissingOptionValueException(name)
|
||||
else -> throw MissingOptionValue(option)
|
||||
}
|
||||
opt += Opt(name, value)
|
||||
}
|
||||
@@ -104,9 +106,9 @@ internal object Parser {
|
||||
when {
|
||||
args[i].contains('=') -> {
|
||||
val (option) = args[i].split('=', limit = 2)
|
||||
throw MalformedOptionException(option)
|
||||
throw MalformedOption(option)
|
||||
}
|
||||
else -> throw MalformedOptionException(args[i])
|
||||
else -> throw MalformedOption(args[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,17 +116,28 @@ internal object Parser {
|
||||
i++
|
||||
}
|
||||
|
||||
if (arg.size < command.arguments.count { it is Argument.Required })
|
||||
throw MissingArgumentException()
|
||||
|
||||
for (option in command.options) {
|
||||
val count = opt.count { it.name in option.names }
|
||||
when {
|
||||
option is Option.Required && count == 0 -> throw MissingOptionException(option.name)
|
||||
option !is Option.Variable && count > 1 -> throw DuplicateOptionException(option.name)
|
||||
}
|
||||
}
|
||||
validateArguments(command, arg)
|
||||
validateOptions(command, opt)
|
||||
|
||||
return Context(command, cmd, opt, arg)
|
||||
}
|
||||
|
||||
private fun validateArguments(command: Command, arg: List<Arg>) {
|
||||
val required = command.arguments.filter { it.required }
|
||||
var consumed = 0
|
||||
for (argument in required) {
|
||||
val needed = if (argument === required.last()) argument.min else argument.count
|
||||
val delta = arg.size - consumed
|
||||
if (delta < needed) throw InvalidArgumentCount(argument, delta)
|
||||
|
||||
consumed += needed
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateOptions(command: Command, opt: List<Opt>) {
|
||||
for (option in command.options) {
|
||||
val count = opt.count { it.name in option.names }
|
||||
if (count !in option.min..option.count) throw InvalidOptionCount(option, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user