- arguments can have multiple names simultaneously - argument values can be passed using spaces (e.g., --arg value) - single-character arguments can be passed using a single dash (e.g., --a -> -a) - multiple single-character arguments can be combined (e.g., -a -b -> -ab) - simplify custom arguments using generic classes
105 lines
3.0 KiB
Kotlin
105 lines
3.0 KiB
Kotlin
package com.infendro.cli.command
|
|
|
|
import com.infendro.cli.argument.Argument
|
|
import com.infendro.cli.exception.CliException
|
|
import com.infendro.cli.exception.build.*
|
|
import com.infendro.cli.exception.run.UnknownCommandException
|
|
import com.infendro.cli.util.Regex.ARGUMENT_NAME
|
|
import com.infendro.cli.util.Regex.COMMAND_NAME
|
|
|
|
class Command private constructor(
|
|
val name: String?,
|
|
val commands: List<Command>,
|
|
val arguments: List<Argument<*>>,
|
|
private val block: Execution.() -> Unit,
|
|
) {
|
|
fun run(args: Array<String>) = try {
|
|
val index = args.withIndex()
|
|
.firstOrNull { (_, value) -> value.startsWith("-") }
|
|
?.index ?: args.size
|
|
|
|
val cmd = args.slice(0..<index)
|
|
val arg = args.slice(index..<args.size)
|
|
|
|
var command = this
|
|
for (c in cmd) {
|
|
command = command.commands.firstOrNull { it.name == c || it.name == null }
|
|
?: throw UnknownCommandException(c)
|
|
}
|
|
|
|
val execution = Execution.from(command, cmd, arg)
|
|
val execute = command.block
|
|
execution.execute()
|
|
} catch (e: CliException) {
|
|
println(e.message)
|
|
}
|
|
|
|
class Builder internal constructor(
|
|
private val index: Int,
|
|
private val name: String?,
|
|
) {
|
|
private val commands = mutableListOf<Command>()
|
|
private val arguments = mutableListOf<Argument<*>>()
|
|
private lateinit var block: Execution.() -> Unit
|
|
|
|
private val nextIndex: Int
|
|
get() = index + 1
|
|
|
|
fun command(name: String? = null, block: Builder.(Key) -> Unit) {
|
|
if (name != null && !name.matches(COMMAND_NAME))
|
|
throw IllegalCommandNameException(name)
|
|
|
|
if (commands.any { it.name == name })
|
|
throw DuplicateCommandException(name)
|
|
|
|
val builder = Builder(nextIndex, name)
|
|
val key = Key(nextIndex)
|
|
builder.block(key)
|
|
val command = builder.build()
|
|
|
|
commands.add(command)
|
|
}
|
|
|
|
fun argument(argument: Argument<*>) {
|
|
for (name in argument.names) {
|
|
if (!name.matches(ARGUMENT_NAME))
|
|
throw IllegalArgumentNameException(name)
|
|
|
|
if (arguments.any { name in it.names })
|
|
throw DuplicateArgumentException(name)
|
|
}
|
|
|
|
arguments.add(argument)
|
|
}
|
|
|
|
fun arguments(vararg arguments: Argument<*>) =
|
|
arguments.forEach(::argument)
|
|
|
|
fun execute(block: Execution.() -> Unit) {
|
|
if (this::block.isInitialized)
|
|
throw DuplicateExecuteException()
|
|
|
|
this.block = block
|
|
}
|
|
|
|
fun build(): Command {
|
|
if (!this::block.isInitialized)
|
|
throw NoExecuteException()
|
|
|
|
return Command(name, commands, arguments, block)
|
|
}
|
|
}
|
|
|
|
class Key internal constructor(
|
|
internal val index: Int,
|
|
)
|
|
}
|
|
|
|
fun cli(
|
|
block: Command.Builder.() -> Unit,
|
|
): Command {
|
|
val builder = Command.Builder(-1, null)
|
|
builder.block()
|
|
return builder.build()
|
|
}
|