package com.infendro.cli.command import com.infendro.cli.argument.Argument import com.infendro.cli.exception.run.DuplicateArgumentException import com.infendro.cli.exception.run.MalformedArgumentException import com.infendro.cli.exception.run.MissingArgumentException import com.infendro.cli.exception.run.UnknownArgumentException import kotlin.reflect.KProperty class Execution private constructor( val commands: List, val arguments: List, ) { companion object { fun from(command: Command, cmd: List, arg: List): Execution { val commands = Cmd.from(cmd) val arguments = Arg.from(command, arg) return Execution(commands, arguments) } } class Cmd( val name: String, ) { companion object { fun from(cmd: List): List = cmd.map { Cmd(it) } } } class Arg( val name: String, val value: Any, ) { companion object { fun from(command: Command, arg: List): List = buildList { var i = 0 while (i < arg.size) { val dashes = arg[i].takeWhile { it == '-' }.count() val trimmed = arg[i].substring(dashes) if (dashes !in 1..2 || trimmed.isEmpty()) throw MalformedArgumentException(arg[i]) val (name, value) = when { trimmed.contains('=') -> { val (name, value) = trimmed.split('=', limit = 2) name to value } else -> { val next = arg.getOrNull(i + 1) if (next != null && !next.startsWith('-')) { i++ trimmed to next } else { trimmed to null } } } val names = when { dashes == 1 -> name.map { "$it" } else -> listOf(name) } for (name in names) { val argument = command.arguments.firstOrNull { name in it.names } ?: throw UnknownArgumentException(name) val value = argument.parser.parse(value) as Any add(Arg(name, value)) } i++ } for (argument in command.arguments) { val count = count { it.name in argument.names } when { argument.required && count == 0 -> throw MissingArgumentException(argument.name) count > 1 -> throw DuplicateArgumentException(argument.name) } } } } } operator fun Command.Key.getValue(thisRef: Any?, property: KProperty<*>): String = commands[index].name @Suppress("UNCHECKED_CAST") private fun Argument.getValue(): T? = arguments.firstOrNull { it.name in names }?.value as? T operator fun Argument.Single.getValue(thisRef: Any?, property: KProperty<*>): T = getValue()!! operator fun Argument.SingleOrElse.getValue(thisRef: Any?, property: KProperty<*>): T = getValue() ?: other operator fun Argument.SingleOrNull.getValue(thisRef: Any?, property: KProperty<*>): T? = getValue() }