diff --git a/src/commonMain/kotlin/com/infendro/cli/Execution.kt b/src/commonMain/kotlin/com/infendro/cli/Execution.kt index b6eff1c..a4f8a5a 100644 --- a/src/commonMain/kotlin/com/infendro/cli/Execution.kt +++ b/src/commonMain/kotlin/com/infendro/cli/Execution.kt @@ -1,16 +1,30 @@ package com.infendro.cli import com.infendro.cli.argument.Argument.* +import com.infendro.cli.command.Command import kotlin.reflect.KProperty class Execution( - val arguments: List, + val commands: List, + val arguments: List, ) { - class Argument( + class Cmd( + val name: String, + ) + + class Arg( val name: String, val value: String?, ) + operator fun Command.Key.getValue( + thisRef: Any?, + property: KProperty<*>, + ): String { + return commands[index] + .name + } + operator fun Single.getValue( thisRef: Any?, property: KProperty<*>, diff --git a/src/commonMain/kotlin/com/infendro/cli/argument/Argument.kt b/src/commonMain/kotlin/com/infendro/cli/argument/Argument.kt index afc3792..4e0ce36 100644 --- a/src/commonMain/kotlin/com/infendro/cli/argument/Argument.kt +++ b/src/commonMain/kotlin/com/infendro/cli/argument/Argument.kt @@ -42,4 +42,10 @@ sealed class Argument { abstract val parser: Parser } + + interface Parser { + fun parse( + text: String?, + ): T + } } diff --git a/src/commonMain/kotlin/com/infendro/cli/argument/Boolean.kt b/src/commonMain/kotlin/com/infendro/cli/argument/Boolean.kt index 8db70b4..7343bad 100644 --- a/src/commonMain/kotlin/com/infendro/cli/argument/Boolean.kt +++ b/src/commonMain/kotlin/com/infendro/cli/argument/Boolean.kt @@ -1,8 +1,8 @@ package com.infendro.cli.argument -import com.infendro.cli.Command +import com.infendro.cli.command.Command -private object BooleanParser : Parser { +private object BooleanParser : Argument.Parser { override fun parse( text: String?, ): Boolean { diff --git a/src/commonMain/kotlin/com/infendro/cli/argument/Int.kt b/src/commonMain/kotlin/com/infendro/cli/argument/Int.kt index 113d0ca..962e3cd 100644 --- a/src/commonMain/kotlin/com/infendro/cli/argument/Int.kt +++ b/src/commonMain/kotlin/com/infendro/cli/argument/Int.kt @@ -1,8 +1,8 @@ package com.infendro.cli.argument -import com.infendro.cli.Command +import com.infendro.cli.command.Command -private object IntParser : Parser { +private object IntParser : Argument.Parser { override fun parse( text: String?, ): Int { diff --git a/src/commonMain/kotlin/com/infendro/cli/argument/Parser.kt b/src/commonMain/kotlin/com/infendro/cli/argument/Parser.kt deleted file mode 100644 index c93353e..0000000 --- a/src/commonMain/kotlin/com/infendro/cli/argument/Parser.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.infendro.cli.argument - -interface Parser { - fun parse( - text: String?, - ): T -} diff --git a/src/commonMain/kotlin/com/infendro/cli/argument/String.kt b/src/commonMain/kotlin/com/infendro/cli/argument/String.kt index 54c35f3..a2756e9 100644 --- a/src/commonMain/kotlin/com/infendro/cli/argument/String.kt +++ b/src/commonMain/kotlin/com/infendro/cli/argument/String.kt @@ -1,8 +1,8 @@ package com.infendro.cli.argument -import com.infendro.cli.Command +import com.infendro.cli.command.Command -private object StringParser : Parser { +private object StringParser : Argument.Parser { override fun parse( text: String?, ): String { diff --git a/src/commonMain/kotlin/com/infendro/cli/Command.kt b/src/commonMain/kotlin/com/infendro/cli/command/Command.kt similarity index 65% rename from src/commonMain/kotlin/com/infendro/cli/Command.kt rename to src/commonMain/kotlin/com/infendro/cli/command/Command.kt index c074e14..9159aa9 100644 --- a/src/commonMain/kotlin/com/infendro/cli/Command.kt +++ b/src/commonMain/kotlin/com/infendro/cli/command/Command.kt @@ -1,21 +1,26 @@ -package com.infendro.cli +package com.infendro.cli.command -import com.infendro.cli.Regex.ARGUMENT -import com.infendro.cli.Regex.ARGUMENT_NAME -import com.infendro.cli.Regex.COMMAND_NAME +import com.infendro.cli.Execution 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 +import com.infendro.cli.util.Regex.ARGUMENT +import com.infendro.cli.util.Regex.ARGUMENT_NAME +import com.infendro.cli.util.Regex.COMMAND_NAME class Command private constructor( - private val name: String, + private val index: Int, + private val name: String?, private val commands: List, private val arguments: List>, private val block: Execution.() -> Unit, ) { + private val nextIndex: Int + get() = index + 1 + fun run( args: Array, ) { @@ -24,11 +29,11 @@ class Command private constructor( .find { (_, value) -> value.startsWith("--") } ?.index ?: args.size - val c = args.slice(0.., args: List, + cmd: List, + arg: List, ) { - if (cmd.isEmpty()) { - val arguments = parseArguments(args) - execute(arguments) + if (index == cmd.lastIndex) { + Execution( + parseCmd(cmd), + parseArg(arg), + ).block() } else { - val command = commands.find { it.name == cmd[0] } - ?: throw UnknownCommandException(cmd[0]) - command.run(cmd.drop(1), args) + val command = commands.firstOrNull() { it.name == cmd[nextIndex] || it.name == null } + ?: throw UnknownCommandException(cmd[nextIndex]) + command.run(cmd, arg) } } - private fun execute( - arguments: List, - ) { - val execution = Execution(arguments) - execution.block() + private fun parseCmd( + cmd: List, + ): List { + return cmd.map { Execution.Cmd(it) } } - private fun parseArguments( - args: List, - ): List { - val result = mutableListOf() + private fun parseArg( + arg: List, + ): List { + val result = mutableListOf() - for (arg in args) { - val match = ARGUMENT.matchEntire(arg) - ?: throw InvalidArgumentException(arg) + for (argument in arg) { + val match = ARGUMENT.matchEntire(argument) + ?: throw InvalidArgumentException(argument) val name = match.groups["name"]!!.value val value = match.groups["value"]?.value @@ -79,7 +86,7 @@ class Command private constructor( if (arguments.none { it.name == name }) throw UnknownArgumentException(name) - result += Execution.Argument(name, value) + result += Execution.Arg(name, value) } arguments @@ -92,26 +99,31 @@ class Command private constructor( return result } - class Builder( - private val name: String = "", + class Builder internal constructor( + private val index: Int, + private val name: String?, ) { private val commands = mutableListOf() private val arguments = mutableListOf>() private lateinit var block: Execution.() -> Unit + private val nextIndex: Int + get() = index + 1 + fun command( - name: String, - block: Builder.() -> Unit, + name: String? = null, + block: Builder.(Key) -> Unit, ) { - if (!COMMAND_NAME.matches(name)) + if (name != null && !COMMAND_NAME.matches(name)) throw IllegalCommandNameException(name) if (commands.any { it.name == name }) throw DuplicateCommandException(name) - val builder = Builder(name) - builder.block() + val builder = Builder(nextIndex, name) + builder.block(Key(nextIndex)) val command = builder.build() + commands.add(command) } @@ -148,15 +160,19 @@ class Command private constructor( if (!this::block.isInitialized) throw NoExecuteException() - return Command(name, commands, arguments, block) + return Command(index, name, commands, arguments, block) } } + + class Key( + internal val index: Int, + ) } fun cli( block: Command.Builder.() -> Unit, ): Command { - val builder = Command.Builder() + val builder = Command.Builder(-1, null) builder.block() return builder.build() } diff --git a/src/commonMain/kotlin/com/infendro/cli/exception/build/DuplicateCommandException.kt b/src/commonMain/kotlin/com/infendro/cli/exception/build/DuplicateCommandException.kt index 4a21135..c83df29 100644 --- a/src/commonMain/kotlin/com/infendro/cli/exception/build/DuplicateCommandException.kt +++ b/src/commonMain/kotlin/com/infendro/cli/exception/build/DuplicateCommandException.kt @@ -1,5 +1,5 @@ package com.infendro.cli.exception.build class DuplicateCommandException( - val command: String, + val command: String?, ) : Exception() diff --git a/src/commonMain/kotlin/com/infendro/cli/Regex.kt b/src/commonMain/kotlin/com/infendro/cli/util/Regex.kt similarity index 88% rename from src/commonMain/kotlin/com/infendro/cli/Regex.kt rename to src/commonMain/kotlin/com/infendro/cli/util/Regex.kt index a57d67c..f626669 100644 --- a/src/commonMain/kotlin/com/infendro/cli/Regex.kt +++ b/src/commonMain/kotlin/com/infendro/cli/util/Regex.kt @@ -1,7 +1,7 @@ -package com.infendro.cli +package com.infendro.cli.util internal object Regex { val COMMAND_NAME = """[a-z]+(?:-[a-z]+)*""".toRegex() val ARGUMENT_NAME = """[a-z]+(?:[\-.][a-z]+)*""".toRegex() val ARGUMENT = """--(?[a-z]+(?:[\-.][a-z]+)*)(?:=(?.*))?""".toRegex() -} +} \ No newline at end of file