aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/commands/dsl.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/commands/dsl.kt')
-rw-r--r--src/main/kotlin/commands/dsl.kt118
1 files changed, 118 insertions, 0 deletions
diff --git a/src/main/kotlin/commands/dsl.kt b/src/main/kotlin/commands/dsl.kt
new file mode 100644
index 0000000..d1f0d8c
--- /dev/null
+++ b/src/main/kotlin/commands/dsl.kt
@@ -0,0 +1,118 @@
+
+
+package moe.nea.firmament.commands
+
+import com.mojang.brigadier.arguments.ArgumentType
+import com.mojang.brigadier.builder.ArgumentBuilder
+import com.mojang.brigadier.builder.RequiredArgumentBuilder
+import com.mojang.brigadier.context.CommandContext
+import com.mojang.brigadier.suggestion.SuggestionProvider
+import kotlinx.coroutines.launch
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.util.MinecraftDispatcher
+import moe.nea.firmament.util.iterate
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
+import java.lang.reflect.ParameterizedType
+import java.lang.reflect.Type
+import java.lang.reflect.TypeVariable
+
+
+typealias DefaultSource = FabricClientCommandSource
+
+
+inline val <T : CommandContext<*>> T.context get() = this
+operator fun <T : Any, C : CommandContext<*>> C.get(arg: TypeSafeArg<T>): T {
+ return arg.get(this)
+}
+
+fun literal(
+ name: String,
+ block: CaseInsensitiveLiteralCommandNode.Builder<DefaultSource>.() -> Unit
+): CaseInsensitiveLiteralCommandNode.Builder<DefaultSource> =
+ CaseInsensitiveLiteralCommandNode.Builder<DefaultSource>(name).also(block)
+
+
+private fun normalizeGeneric(argument: Type): Class<*> {
+ return when (argument) {
+ is Class<*> -> argument
+ is TypeVariable<*> -> normalizeGeneric(argument.bounds[0])
+ is ParameterizedType -> normalizeGeneric(argument.rawType)
+ else -> Any::class.java
+ }
+}
+
+data class TypeSafeArg<T : Any>(val name: String, val argument: ArgumentType<T>) {
+ val argClass by lazy {
+ argument.javaClass
+ .iterate<Class<in ArgumentType<T>>> {
+ it.superclass
+ }
+ .flatMap {
+ it.genericInterfaces.toList()
+ }
+ .filterIsInstance<ParameterizedType>()
+ .find { it.rawType == ArgumentType::class.java }!!
+ .let { normalizeGeneric(it.actualTypeArguments[0]) }
+ }
+
+ @JvmName("getWithThis")
+ fun <S> CommandContext<S>.get(): T =
+ get(this)
+
+
+ fun <S> get(ctx: CommandContext<S>): T {
+ try {
+ return ctx.getArgument(name, argClass) as T
+ } catch (e: Exception) {
+ if (ctx.child != null) {
+ return get(ctx.child)
+ }
+ throw e
+ }
+ }
+}
+
+
+fun <T : Any> argument(
+ name: String,
+ argument: ArgumentType<T>,
+ block: RequiredArgumentBuilder<DefaultSource, T>.(TypeSafeArg<T>) -> Unit
+): RequiredArgumentBuilder<DefaultSource, T> =
+ RequiredArgumentBuilder.argument<DefaultSource, T>(name, argument).also { block(it, TypeSafeArg(name, argument)) }
+
+fun <T : ArgumentBuilder<DefaultSource, T>, AT : Any> T.thenArgument(
+ name: String,
+ argument: ArgumentType<AT>,
+ block: RequiredArgumentBuilder<DefaultSource, AT>.(TypeSafeArg<AT>) -> Unit
+): T = then(argument(name, argument, block))
+
+fun <T : RequiredArgumentBuilder<DefaultSource, String>> T.suggestsList(provider: CommandContext<DefaultSource>.() -> Iterable<String>) {
+ suggests(SuggestionProvider<DefaultSource> { context, builder ->
+ provider(context)
+ .asSequence()
+ .filter { it.startsWith(builder.remaining, ignoreCase = true) }
+ .forEach {
+ builder.suggest(it)
+ }
+ builder.buildFuture()
+ })
+}
+
+fun <T : ArgumentBuilder<DefaultSource, T>> T.thenLiteral(
+ name: String,
+ block: CaseInsensitiveLiteralCommandNode.Builder<DefaultSource>.() -> Unit
+): T =
+ then(literal(name, block))
+
+fun <T : ArgumentBuilder<DefaultSource, T>> T.then(node: ArgumentBuilder<DefaultSource, *>, block: T.() -> Unit): T =
+ then(node).also(block)
+
+fun <T : ArgumentBuilder<DefaultSource, T>> T.thenExecute(block: suspend CommandContext<DefaultSource>.() -> Unit): T =
+ executes {
+ Firmament.coroutineScope.launch(MinecraftDispatcher) {
+ block(it)
+ }
+ 1
+ }
+
+