improve exceptions
This commit is contained in:
@@ -7,11 +7,9 @@ import com.infendro.cli.command.context.ContextParser
|
|||||||
import com.infendro.cli.command.help.DefaultHelpRenderer
|
import com.infendro.cli.command.help.DefaultHelpRenderer
|
||||||
import com.infendro.cli.command.help.HelpRenderer
|
import com.infendro.cli.command.help.HelpRenderer
|
||||||
import com.infendro.cli.command.option.Option
|
import com.infendro.cli.command.option.Option
|
||||||
import com.infendro.cli.exception.build.*
|
import com.infendro.cli.error.build.*
|
||||||
import com.infendro.cli.parser.*
|
import com.infendro.cli.parser.*
|
||||||
import com.infendro.cli.util.Regex
|
import com.infendro.cli.util.Regex
|
||||||
import com.infendro.cli.util.Regex.ARGUMENT
|
|
||||||
import com.infendro.cli.util.Regex.OPTION
|
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
sealed class Command private constructor(
|
sealed class Command private constructor(
|
||||||
@@ -205,15 +203,11 @@ sealed class Command private constructor(
|
|||||||
argument.required && last.optional -> throw InvalidArgumentOrder()
|
argument.required && last.optional -> throw InvalidArgumentOrder()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!argument.name.matches(ARGUMENT)) throw InvalidArgument(argument.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateOption(option: Option<*>) {
|
private fun validateOption(option: Option<*>) {
|
||||||
for (name in option.names) {
|
for (name in option.names) {
|
||||||
when {
|
if (options.any { name in it.names }) throw DuplicateOption(name)
|
||||||
!name.matches(OPTION) -> throw InvalidOption(name)
|
|
||||||
options.any { name in it.names } -> throw DuplicateOption(name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +216,7 @@ sealed class Command private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun validateRenderer() {
|
private fun validateRenderer() {
|
||||||
if (_renderer != null) throw Exception() //TODO
|
if (_renderer != null) throw DuplicateRenderer()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validate() {
|
private fun validate() {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.infendro.cli.command.argument
|
package com.infendro.cli.command.argument
|
||||||
|
|
||||||
import com.infendro.cli.exception.build.InvalidRange
|
import com.infendro.cli.error.build.InvalidArgument
|
||||||
|
import com.infendro.cli.error.build.InvalidRange
|
||||||
import com.infendro.cli.parser.Parser
|
import com.infendro.cli.parser.Parser
|
||||||
|
import com.infendro.cli.util.Regex.ARGUMENT
|
||||||
|
|
||||||
sealed class Argument<T : Any>(
|
sealed class Argument<T : Any>(
|
||||||
val parser: Parser<T>,
|
val parser: Parser<T>,
|
||||||
@@ -10,7 +12,10 @@ sealed class Argument<T : Any>(
|
|||||||
val max: Int?,
|
val max: Int?,
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
if (min !in 0..count || max == 0) throw InvalidRange()
|
when {
|
||||||
|
!name.matches(ARGUMENT) -> throw InvalidArgument(name)
|
||||||
|
min !in 0..count || max == 0 -> throw InvalidRange()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val required: Boolean
|
val required: Boolean
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.infendro.cli.command.context
|
package com.infendro.cli.command.context
|
||||||
|
|
||||||
import com.infendro.cli.command.Command
|
import com.infendro.cli.command.Command
|
||||||
import com.infendro.cli.exception.run.*
|
import com.infendro.cli.error.run.*
|
||||||
import com.infendro.cli.parser.Parser
|
import com.infendro.cli.parser.Parser
|
||||||
|
|
||||||
internal class ContextParser(
|
internal class ContextParser(
|
||||||
@@ -19,13 +19,13 @@ internal class ContextParser(
|
|||||||
|
|
||||||
data class Failure(
|
data class Failure(
|
||||||
val command: Command,
|
val command: Command,
|
||||||
val error: RuntimeError,
|
val error: RunError,
|
||||||
) : Result()
|
) : Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun success(context: Context) = Result.Success(context)
|
private fun success(context: Context) = Result.Success(context)
|
||||||
private fun help(command: Command) = Result.Help(command)
|
private fun help(command: Command) = Result.Help(command)
|
||||||
private fun failure(command: Command, exception: RuntimeError) = Result.Failure(command, exception)
|
private fun failure(command: Command, exception: RunError) = Result.Failure(command, exception)
|
||||||
|
|
||||||
private var index = 0
|
private var index = 0
|
||||||
private val current: String
|
private val current: String
|
||||||
|
|||||||
@@ -75,7 +75,11 @@ object DefaultHelpRenderer : HelpRenderer {
|
|||||||
private fun StringBuilder.commands(commands: List<Command>) {
|
private fun StringBuilder.commands(commands: List<Command>) {
|
||||||
appendLine("Commands:")
|
appendLine("Commands:")
|
||||||
for (command in commands) {
|
for (command in commands) {
|
||||||
appendLine(" * ${command.name}")
|
val name = when {
|
||||||
|
command is Command.Fallback<*> -> "<${command.name}>"
|
||||||
|
else -> command.name
|
||||||
|
}
|
||||||
|
appendLine(" * $name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package com.infendro.cli.command.option
|
package com.infendro.cli.command.option
|
||||||
|
|
||||||
import com.infendro.cli.exception.build.InvalidRange
|
import com.infendro.cli.error.build.InvalidOption
|
||||||
import com.infendro.cli.exception.build.MissingOptionName
|
import com.infendro.cli.error.build.InvalidRange
|
||||||
|
import com.infendro.cli.error.build.MissingOptionName
|
||||||
import com.infendro.cli.parser.Parser
|
import com.infendro.cli.parser.Parser
|
||||||
|
import com.infendro.cli.util.Regex.OPTION
|
||||||
|
|
||||||
sealed class Option<T : Any>(
|
sealed class Option<T : Any>(
|
||||||
val parser: Parser<T>,
|
val parser: Parser<T>,
|
||||||
@@ -33,6 +35,7 @@ sealed class Option<T : Any>(
|
|||||||
init {
|
init {
|
||||||
when {
|
when {
|
||||||
names.isEmpty() -> throw MissingOptionName()
|
names.isEmpty() -> throw MissingOptionName()
|
||||||
|
names.any { !it.matches(OPTION) } -> throw InvalidOption(name)
|
||||||
min !in 0..count || max == 0 -> throw InvalidRange()
|
min !in 0..count || max == 0 -> throw InvalidRange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ object OptionFactory {
|
|||||||
fun long(vararg names: String) = option(LongParser, *names)
|
fun long(vararg names: String) = option(LongParser, *names)
|
||||||
fun float(vararg names: String) = option(FloatParser, *names)
|
fun float(vararg names: String) = option(FloatParser, *names)
|
||||||
fun double(vararg names: String) = option(DoubleParser, *names)
|
fun double(vararg names: String) = option(DoubleParser, *names)
|
||||||
fun boolean(vararg names: String) = option(BooleanParser, true, *names)
|
fun boolean(vararg names: String) = option(BooleanParser, fallback = true, *names)
|
||||||
|
|
||||||
inline fun <reified T : Enum<T>> enum(vararg names: String) = option(enumParser<T>(), *names)
|
inline fun <reified T : Enum<T>> enum(vararg names: String) = option(enumParser<T>(), *names)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
abstract class BuildException : Error()
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class DuplicateCommand(
|
||||||
|
val command: String,
|
||||||
|
) : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """duplicate command "$command""""
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class DuplicateExecute : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """duplicate execute block"""
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class DuplicateOption(
|
||||||
|
val name: String,
|
||||||
|
) : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """duplicate option "$name""""
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class DuplicateRenderer : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """duplicate renderer"""
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class InvalidArgument(
|
||||||
|
val name: String,
|
||||||
|
) : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """invalid argument name "$name""""
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class InvalidArgumentOrder : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """invalid argument order"""
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class InvalidCommand(
|
||||||
|
val name: String,
|
||||||
|
) : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """invalid command name "$name""""
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class InvalidCommandOrder : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """invalid command order"""
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class InvalidFallback : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """invalid fallback"""
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class InvalidOption(
|
||||||
|
val name: String,
|
||||||
|
) : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """invalid option name "$name""""
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class InvalidRange : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """invalid range"""
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.infendro.cli.error.build
|
||||||
|
|
||||||
|
class MissingOptionName : BuildException() {
|
||||||
|
override val message: String
|
||||||
|
get() = """missing option name"""
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package com.infendro.cli.exception.run
|
package com.infendro.cli.error.run
|
||||||
|
|
||||||
import com.infendro.cli.command.argument.Argument
|
import com.infendro.cli.command.argument.Argument
|
||||||
|
|
||||||
class InvalidArgumentCount(
|
class InvalidArgumentCount(
|
||||||
val argument: Argument<*>,
|
val argument: Argument<*>,
|
||||||
val count: Int,
|
val count: Int,
|
||||||
) : RuntimeError() {
|
) : RunError() {
|
||||||
override val message: String
|
override val message: String
|
||||||
get() {
|
get() {
|
||||||
val expected = when {
|
val expected = when {
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package com.infendro.cli.exception.run
|
package com.infendro.cli.error.run
|
||||||
|
|
||||||
import com.infendro.cli.command.option.Option
|
import com.infendro.cli.command.option.Option
|
||||||
|
|
||||||
class InvalidOptionCount(
|
class InvalidOptionCount(
|
||||||
val option: Option<*>,
|
val option: Option<*>,
|
||||||
val count: Int,
|
val count: Int,
|
||||||
) : RuntimeError() {
|
) : RunError() {
|
||||||
override val message: String
|
override val message: String
|
||||||
get() {
|
get() {
|
||||||
val expected = when {
|
val expected = when {
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package com.infendro.cli.exception.run
|
package com.infendro.cli.error.run
|
||||||
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class InvalidValue(
|
class InvalidValue(
|
||||||
val value: String,
|
val value: String,
|
||||||
val type: KClass<*>,
|
val type: KClass<*>,
|
||||||
) : RuntimeError() {
|
) : RunError() {
|
||||||
override val message: String
|
override val message: String
|
||||||
get() = """"$value" cannot be converted to ${type.simpleName ?: "Unknown"}"""
|
get() = """"$value" cannot be converted to ${type.simpleName ?: "Unknown"}"""
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.infendro.cli.exception.run
|
package com.infendro.cli.error.run
|
||||||
|
|
||||||
class MalformedOption(
|
class MalformedOption(
|
||||||
val option: String,
|
val option: String,
|
||||||
) : RuntimeError() {
|
) : RunError() {
|
||||||
override val message: String
|
override val message: String
|
||||||
get() = """malformed option "$option""""
|
get() = """malformed option "$option""""
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.infendro.cli.exception.run
|
package com.infendro.cli.error.run
|
||||||
|
|
||||||
import com.infendro.cli.command.option.Option
|
import com.infendro.cli.command.option.Option
|
||||||
|
|
||||||
class MissingOptionValue(
|
class MissingOptionValue(
|
||||||
val option: Option<*>,
|
val option: Option<*>,
|
||||||
) : RuntimeError() {
|
) : RunError() {
|
||||||
override val message: String
|
override val message: String
|
||||||
get() = """no value provided for option "${option.name}""""
|
get() = """no value provided for option "${option.name}""""
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.infendro.cli.error.run
|
||||||
|
|
||||||
|
abstract class RunError {
|
||||||
|
abstract val message: String
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.infendro.cli.error.run
|
||||||
|
|
||||||
|
class UnexpectedArgument : RunError() {
|
||||||
|
override val message: String
|
||||||
|
get() = """unexpected argument"""
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.infendro.cli.exception.run
|
package com.infendro.cli.error.run
|
||||||
|
|
||||||
class UnknownCommand(
|
class UnknownCommand(
|
||||||
val command: String,
|
val command: String,
|
||||||
) : RuntimeError() {
|
) : RunError() {
|
||||||
override val message: String
|
override val message: String
|
||||||
get() = """unknown command "$command""""
|
get() = """unknown command "$command""""
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.infendro.cli.exception.run
|
package com.infendro.cli.error.run
|
||||||
|
|
||||||
class UnknownOption(
|
class UnknownOption(
|
||||||
val option: String,
|
val option: String,
|
||||||
) : RuntimeError() {
|
) : RunError() {
|
||||||
override val message: String
|
override val message: String
|
||||||
get() = """unknown option "$option""""
|
get() = """unknown option "$option""""
|
||||||
}
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
abstract class CliException : Exception()
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
class DuplicateCommand(
|
|
||||||
val command: String,
|
|
||||||
) : CliException()
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
class DuplicateExecute : CliException()
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
class DuplicateOption(
|
|
||||||
val name: String,
|
|
||||||
) : CliException()
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
class InvalidArgument(
|
|
||||||
val name: String,
|
|
||||||
) : CliException()
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
class InvalidArgumentOrder : CliException()
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
class InvalidCommand(
|
|
||||||
val name: String,
|
|
||||||
) : CliException()
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
class InvalidCommandOrder : CliException()
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
class InvalidFallback : CliException()
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
class InvalidOption(
|
|
||||||
val name: String,
|
|
||||||
) : CliException()
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
class InvalidRange : CliException()
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
package com.infendro.cli.exception.build
|
|
||||||
|
|
||||||
class MissingOptionName : CliException()
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.infendro.cli.exception.run
|
|
||||||
|
|
||||||
abstract class RuntimeError {
|
|
||||||
abstract val message: String
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package com.infendro.cli.exception.run
|
|
||||||
|
|
||||||
class UnexpectedArgument : RuntimeError() {
|
|
||||||
override val message: String
|
|
||||||
get() = """unexpected argument"""
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.infendro.cli.parser
|
package com.infendro.cli.parser
|
||||||
|
|
||||||
import com.infendro.cli.exception.run.InvalidValue
|
import com.infendro.cli.error.run.InvalidValue
|
||||||
import com.infendro.cli.exception.run.RuntimeError
|
import com.infendro.cli.error.run.RunError
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
abstract class Parser<T : Any>(
|
abstract class Parser<T : Any>(
|
||||||
@@ -13,7 +13,7 @@ abstract class Parser<T : Any>(
|
|||||||
) : Result<T>()
|
) : Result<T>()
|
||||||
|
|
||||||
data class Failure<T : Any>(
|
data class Failure<T : Any>(
|
||||||
val error: RuntimeError,
|
val error: RunError,
|
||||||
) : Result<T>()
|
) : Result<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user