package com.infendro.cli import com.infendro.cli.Regex.ARGUMENT import com.infendro.cli.Regex.ARGUMENT_NAME import com.infendro.cli.Regex.COMMAND_NAME import com.infendro.cli.argument.Argument import com.infendro.cli.exception.build.* import com.infendro.cli.exception.run.ArgumentCountException import com.infendro.cli.exception.run.InvalidArgumentException import com.infendro.cli.exception.run.UnknownArgumentException import com.infendro.cli.exception.run.UnknownCommandException class Command private constructor( private val name: String, private val commands: List, private val arguments: List>, private val block: Execution.() -> Unit, ) { fun run( args: Array, ) { val index = args .withIndex() .find { (_, value) -> value.startsWith("--") } ?.index ?: args.size val c = args.slice(0.. println("""argument "${e.argument}" must appear ${e.min} times""") e.max == Int.MAX_VALUE -> println("""argument "${e.argument}" must appear at least ${e.min} times""") else -> println("""argument "${e.argument}" must appear between ${e.min} and ${e.max} times""") } } } private fun run( cmd: List, args: List, ) { if (cmd.isEmpty()) { val arguments = parseArguments(args) execute(arguments) } else { val command = commands.find { it.name == cmd[0] } ?: throw UnknownCommandException(cmd[0]) command.run(cmd.drop(1), args) } } private fun execute( arguments: List, ) { val execution = Execution(arguments) execution.block() } private fun parseArguments( args: List, ): List { val result = mutableListOf() for (arg in args) { val match = ARGUMENT.matchEntire(arg) ?: throw InvalidArgumentException(arg) val name = match.groups["name"]!!.value val value = match.groups["value"]?.value if (arguments.none { it.name == name }) throw UnknownArgumentException(name) result += Execution.Argument(name, value) } arguments .forEach { argument -> val count = result.count { it.name == argument.name } if (count !in argument.min..argument.max) throw ArgumentCountException(argument.name, argument.min, argument.max) } return result } class Builder( private val name: String = "", ) { private val commands = mutableListOf() private val arguments = mutableListOf>() private lateinit var block: Execution.() -> Unit fun command( name: String, block: Builder.() -> Unit, ) { if (!COMMAND_NAME.matches(name)) throw IllegalCommandNameException(name) if (commands.any { it.name == name }) throw DuplicateCommandException(name) val builder = Builder(name) builder.block() val command = builder.build() commands.add(command) } fun argument( argument: Argument<*>, ) { if (!ARGUMENT_NAME.matches(argument.name)) throw IllegalArgumentNameException(argument.name) if (arguments.any { it.name == argument.name }) throw DuplicateArgumentException(argument.name) arguments.add(argument) } fun arguments( vararg arguments: Argument<*>, ) { for (argument in arguments) { argument(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) } } } fun cli( block: Command.Builder.() -> Unit, ): Command { val builder = Command.Builder() builder.block() return builder.build() }