1.4.0 release #9

Merged
Infendro merged 20 commits from develop into main 2026-01-10 07:37:21 +00:00
2 changed files with 39 additions and 18 deletions
Showing only changes of commit 7001b00f5f - Show all commits

View File

@@ -23,8 +23,8 @@ sealed class Command private constructor(
if (!name.matches(Regex.COMMAND)) throw InvalidCommandName(name) if (!name.matches(Regex.COMMAND)) throw InvalidCommandName(name)
} }
fun run(args: Array<String>) { fun run(vararg args: String) {
when (val result = ContextParser(this, args).parse()) { when (val result = ContextParser(this, arrayOf(*args)).parse()) {
is ContextParser.Result.Success -> result.context.execute() is ContextParser.Result.Success -> result.context.execute()
is ContextParser.Result.Help -> { is ContextParser.Result.Help -> {
val command = result.path.last() val command = result.path.last()
@@ -144,7 +144,7 @@ sealed class Command private constructor(
val last = commands.last() val last = commands.last()
if (last is Fallback<*>) throw InvalidCommandOrder() if (last is Fallback<*>) throw InvalidCommandOrder()
} }
if (commands.any { it.name == command.name }) throw DuplicateCommand(command.name) if (commands.any { it.name == command.name }) throw DuplicateCommand(command)
} }
private fun validateArgument(argument: Argument<*>) { private fun validateArgument(argument: Argument<*>) {
@@ -155,12 +155,12 @@ sealed class Command private constructor(
argument.required && last.optional -> throw InvalidArgumentOrder() argument.required && last.optional -> throw InvalidArgumentOrder()
} }
} }
if (arguments.any { it.name == argument.name }) throw DuplicateArgument(argument.name) if (arguments.any { it.name == argument.name }) throw DuplicateArgument(argument)
} }
private fun validateOption(option: Option<*>) { private fun validateOption(option: Option<*>) {
for (name in option.names) { for (name in option.names) {
if (options.any { name in it.names }) throw DuplicateOption(name) if (options.any { name in it.names }) throw DuplicateOption(option)
} }
} }

View File

@@ -3,6 +3,7 @@ package com.infendro.cli.command.help
import com.infendro.cli.command.Command import com.infendro.cli.command.Command
import com.infendro.cli.command.argument.Argument import com.infendro.cli.command.argument.Argument
import com.infendro.cli.command.option.Option import com.infendro.cli.command.option.Option
import kotlin.reflect.KClass
object DefaultHelpRenderer : HelpRenderer { object DefaultHelpRenderer : HelpRenderer {
override fun render(path: List<Command>) = buildString { override fun render(path: List<Command>) = buildString {
@@ -40,11 +41,7 @@ object DefaultHelpRenderer : HelpRenderer {
argument.min == 1 && argument.max == 1 -> append("<${argument.name}>") argument.min == 1 && argument.max == 1 -> append("<${argument.name}>")
argument.min == 0 && argument.max == 1 -> append("[<${argument.name}>]") argument.min == 0 && argument.max == 1 -> append("[<${argument.name}>]")
else -> { else -> {
val range = when { val range = renderRange(argument.min, argument.max)
argument.unbounded -> "{${argument.min},}"
argument.min == argument.max -> "{${argument.min}}"
else -> "{${argument.min},${argument.max}}"
}
append("<${argument.name}>$range") append("<${argument.name}>$range")
} }
} }
@@ -64,11 +61,7 @@ object DefaultHelpRenderer : HelpRenderer {
option.min == 1 && option.max == 1 -> append("($text)") option.min == 1 && option.max == 1 -> append("($text)")
option.min == 0 && option.max == 1 -> append("[$text]") option.min == 0 && option.max == 1 -> append("[$text]")
else -> { else -> {
val range = when { val range = renderRange(option.min, option.max)
option.unbounded -> "{${option.min},}"
option.min == option.max -> "{${option.min}}"
else -> "{${option.min},${option.max}}"
}
append("($text)$range") append("($text)$range")
} }
} }
@@ -80,7 +73,11 @@ object DefaultHelpRenderer : HelpRenderer {
appendLine("Commands:") appendLine("Commands:")
for (command in commands) { for (command in commands) {
val name = when { val name = when {
command is Command.Fallback<*> -> "<${command.name}>" command is Command.Fallback<*> -> {
val name = command.name
val type = command.parser.type.name
"<$name> ($type)"
}
else -> command.name else -> command.name
} }
appendLine(" * $name") appendLine(" * $name")
@@ -90,7 +87,10 @@ object DefaultHelpRenderer : HelpRenderer {
private fun StringBuilder.arguments(arguments: List<Argument<*>>) { private fun StringBuilder.arguments(arguments: List<Argument<*>>) {
appendLine("Arguments:") appendLine("Arguments:")
for (argument in arguments) { for (argument in arguments) {
appendLine(" * ${argument.name}") val name = argument.name
val type = argument.parser.type.name
val range = renderRangeFull(argument.min, argument.max)
appendLine(" * $name ($type, $range)")
} }
} }
@@ -98,7 +98,9 @@ object DefaultHelpRenderer : HelpRenderer {
appendLine("Options:") appendLine("Options:")
for (option in options) { for (option in options) {
val name = option.names.joinToString(" | ") { it.withPrefix() } val name = option.names.joinToString(" | ") { it.withPrefix() }
appendLine(" * $name") val type = option.parser.type.name
val range = renderRangeFull(option.min, option.max)
appendLine(" * $name ($type, $range)")
} }
} }
@@ -106,4 +108,23 @@ object DefaultHelpRenderer : HelpRenderer {
length == 1 -> "-$this" length == 1 -> "-$this"
else -> "--$this" else -> "--$this"
} }
private val KClass<*>.name: String
get() = simpleName ?: "Unknown"
private fun renderRange(min: Int, max: Int?) = when {
max == null -> "{$min,}"
min == max -> "{$min}"
else -> "{$min,$max}"
}
private fun renderRangeFull(min: Int, max: Int?) = when {
min == 1 && max == 1 -> "required"
min == 0 && max == 1 -> "optional"
else -> when {
max == null -> "at least $min"
min == max -> "exactly $min"
else -> "between $min and $max"
}
}
} }