Fix command call

This commit is contained in:
Him188 2020-10-23 13:40:16 +08:00
parent 4c30e3d9d7
commit 084e2c5c55
6 changed files with 56 additions and 41 deletions

View File

@ -90,9 +90,9 @@ public sealed class CommandExecuteResult {
} }
/** 没有匹配的指令 */ /** 没有匹配的指令 */
public class CommandNotFound( public class UnresolvedCall(
/** 尝试执行的指令名 */ /** 尝试执行的指令名 */
public override val commandName: String public override val commandName: String,
) : CommandExecuteResult() { ) : CommandExecuteResult() {
/** 指令执行时发生的错误, 总是 `null` */ /** 指令执行时发生的错误, 总是 `null` */
public override val exception: Nothing? get() = null public override val exception: Nothing? get() = null
@ -196,19 +196,19 @@ public fun CommandExecuteResult.isPermissionDenied(): Boolean {
} }
/** /**
* [this] [CommandExecuteResult.CommandNotFound] 时返回 `true` * [this] [CommandExecuteResult.UnresolvedCall] 时返回 `true`
*/ */
@JvmSynthetic @JvmSynthetic
public fun CommandExecuteResult.isCommandNotFound(): Boolean { public fun CommandExecuteResult.isCommandNotFound(): Boolean {
contract { contract {
returns(true) implies (this@isCommandNotFound is CommandExecuteResult.CommandNotFound) returns(true) implies (this@isCommandNotFound is CommandExecuteResult.UnresolvedCall)
returns(false) implies (this@isCommandNotFound !is CommandExecuteResult.CommandNotFound) returns(false) implies (this@isCommandNotFound !is CommandExecuteResult.UnresolvedCall)
} }
return this is CommandExecuteResult.CommandNotFound return this is CommandExecuteResult.UnresolvedCall
} }
/** /**
* [this] [CommandExecuteResult.ExecutionFailed], [CommandExecuteResult.IllegalArgument] [CommandExecuteResult.CommandNotFound] 时返回 `true` * [this] [CommandExecuteResult.ExecutionFailed], [CommandExecuteResult.IllegalArgument] [CommandExecuteResult.UnresolvedCall] 时返回 `true`
*/ */
@JvmSynthetic @JvmSynthetic
public fun CommandExecuteResult.isFailure(): Boolean { public fun CommandExecuteResult.isFailure(): Boolean {

View File

@ -228,8 +228,8 @@ internal suspend fun executeCommandImpl(
caller: CommandSender, caller: CommandSender,
checkPermission: Boolean, checkPermission: Boolean,
): CommandExecuteResult = with(receiver) { ): CommandExecuteResult = with(receiver) {
val call = message.asMessageChain().parseCommandCall(caller) ?: return CommandExecuteResult.CommandNotFound("") val call = message.asMessageChain().parseCommandCall(caller) ?: return CommandExecuteResult.UnresolvedCall("")
val resolved = call.resolve() ?: return CommandExecuteResult.CommandNotFound(call.calleeName) val resolved = call.resolve() ?: return CommandExecuteResult.UnresolvedCall(call.calleeName)
val command = resolved.callee val command = resolved.callee

View File

@ -62,12 +62,16 @@ public abstract class SimpleCommand(
CommandArgumentContextAware { CommandArgumentContextAware {
@ExperimentalCommandDescriptors @ExperimentalCommandDescriptors
override val overloads: List<CommandSignatureVariant> = listOf( override val overloads: List<CommandSignatureVariant> by lazy {
CommandSignatureVariantImpl(listOf(CommandValueParameter.UserDefinedType.createRequired<MessageChain>("args", true))) { call -> CommandSignatureVariantImpl(
valueParameters = subCommands.single().params.map {
CommandValueParameter.UserDefinedType(it.name, null, isOptional = false, isVararg = false, type = it.type)
}
) { call ->
val sender = call.caller val sender = call.caller
subCommands.single().onCommand(sender, call.resolvedValueArguments) subCommands.single().onCommand(sender, call.resolvedValueArguments)
} }.let { listOf(it) }
) }
/** /**
* 自动根据带有 [Handler] 注解的函数签名生成 [usage]. 也可以被覆盖. * 自动根据带有 [Handler] 注解的函数签名生成 [usage]. 也可以被覆盖.

View File

@ -81,7 +81,7 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by MiraiCons
sender.catchExecutionException(result.exception) sender.catchExecutionException(result.exception)
intercept() intercept()
} }
is CommandExecuteResult.CommandNotFound -> { is CommandExecuteResult.UnresolvedCall -> {
// noop // noop
} }
} }
@ -140,11 +140,11 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by MiraiCons
override fun Command.unregister(): Boolean = modifyLock.withLock { override fun Command.unregister(): Boolean = modifyLock.withLock {
if (this.prefixOptional) { if (this.prefixOptional) {
this.allNames.forEach { this.allNames.forEach {
optionalPrefixCommandMap.remove(it) optionalPrefixCommandMap.remove(it.toLowerCase())
} }
} }
this.allNames.forEach { this.allNames.forEach {
requiredPrefixCommandMap.remove(it) requiredPrefixCommandMap.remove(it.toLowerCase())
} }
_registeredCommands.remove(this) _registeredCommands.remove(this)
} }

View File

@ -13,6 +13,7 @@ package net.mamoe.mirai.console.internal.command
import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.descriptor.* import net.mamoe.mirai.console.command.descriptor.*
import net.mamoe.mirai.console.internal.command.hasAnnotation
import net.mamoe.mirai.console.permission.Permission import net.mamoe.mirai.console.permission.Permission
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.PlainText
@ -22,10 +23,7 @@ import kotlin.reflect.KAnnotatedElement
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KFunction import kotlin.reflect.KFunction
import kotlin.reflect.KParameter import kotlin.reflect.KParameter
import kotlin.reflect.full.callSuspendBy import kotlin.reflect.full.*
import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.isSubclassOf
internal object CompositeCommandSubCommandAnnotationResolver : internal object CompositeCommandSubCommandAnnotationResolver :
AbstractReflectionCommand.SubCommandAnnotationResolver { AbstractReflectionCommand.SubCommandAnnotationResolver {
@ -322,7 +320,9 @@ internal fun AbstractReflectionCommand.createSubCommand(
subDescription, // overridePermission?.value subDescription, // overridePermission?.value
permission,//overridePermission?.value?.let { PermissionService.INSTANCE[PermissionId.parseFromString(it)] } ?: permission, permission,//overridePermission?.value?.let { PermissionService.INSTANCE[PermissionId.parseFromString(it)] } ?: permission,
onCommand = { _: CommandSender, args -> onCommand = { _: CommandSender, args ->
val result = function.callSuspendBy(parameters.zip(args).toMap()) val p = parameters.zip(args).toMap(LinkedHashMap())
if (notStatic) p[function.instanceParameter!!] = this@createSubCommand
val result = function.callSuspendBy(p)
checkNotNull(result) { "sub command return value is null (at ${this::class.qualifiedName}.${function.name})" } checkNotNull(result) { "sub command return value is null (at ${this::class.qualifiedName}.${function.name})" }

View File

@ -73,6 +73,7 @@ internal class TestCommand {
fun testRegister() { fun testRegister() {
try { try {
ConsoleCommandOwner.unregisterAllCommands() // builtins ConsoleCommandOwner.unregisterAllCommands() // builtins
TestSimpleCommand.unregister()
assertTrue(TestCompositeCommand.register()) assertTrue(TestCompositeCommand.register())
assertFalse(TestCompositeCommand.register()) assertFalse(TestCompositeCommand.register())
@ -80,7 +81,9 @@ internal class TestCommand {
assertEquals(1, ConsoleCommandOwner.registeredCommands.size) assertEquals(1, ConsoleCommandOwner.registeredCommands.size)
assertEquals(1, CommandManagerImpl._registeredCommands.size) assertEquals(1, CommandManagerImpl._registeredCommands.size)
assertEquals(2, CommandManagerImpl.requiredPrefixCommandMap.size) assertEquals(2,
CommandManagerImpl.requiredPrefixCommandMap.size,
CommandManagerImpl.requiredPrefixCommandMap.entries.joinToString { it.toString() })
} finally { } finally {
TestCompositeCommand.unregister() TestCompositeCommand.unregister()
} }
@ -107,24 +110,28 @@ internal class TestCommand {
@Test @Test
fun testSimpleArgsSplitting() = runBlocking { fun testSimpleArgsSplitting() = runBlocking {
assertEquals(arrayOf("test", "ttt", "tt").joinToString(), withTesting<MessageChain> { TestSimpleCommand.withRegistration {
assertSuccess(TestSimpleCommand.execute(sender, PlainText("test ttt tt"))) assertEquals(arrayOf("test", "ttt", "tt").joinToString(), withTesting<MessageChain> {
}.joinToString()) assertSuccess(TestSimpleCommand.execute(sender, PlainText("test ttt tt")))
}.joinToString())
}
} }
val image = Image("/f8f1ab55-bf8e-4236-b55e-955848d7069f") val image = Image("/f8f1ab55-bf8e-4236-b55e-955848d7069f")
@Test @Test
fun `PlainText and Image args splitting`() = runBlocking { fun `PlainText and Image args splitting`() = runBlocking {
val result = withTesting<MessageChain> { TestSimpleCommand.withRegistration {
assertSuccess(TestSimpleCommand.execute(sender, buildMessageChain { val result = withTesting<MessageChain> {
+"test" assertSuccess(TestSimpleCommand.execute(sender, buildMessageChain {
+image +"test"
+"tt" +image
})) +"tt"
}))
}
assertEquals<Any>(arrayOf("test", image, "tt").joinToString(), result.toTypedArray().joinToString())
assertSame(image, result[1])
} }
assertEquals<Any>(arrayOf("test", image, "tt").joinToString(), result.toTypedArray().joinToString())
assertSame(image, result[1])
} }
@Test @Test
@ -147,9 +154,11 @@ internal class TestCommand {
@Test @Test
fun `composite command executing`() = runBlocking { fun `composite command executing`() = runBlocking {
assertEquals(1, withTesting { TestCompositeCommand.withRegistration {
assertSuccess(TestCompositeCommand.execute(sender, "mute 1")) assertEquals(1, withTesting {
}) assertSuccess(TestCompositeCommand.execute(sender, "mute 1"))
})
}
} }
@Test @Test
@ -187,7 +196,7 @@ internal class TestCommand {
fun `composite sub command parsing`() { fun `composite sub command parsing`() {
runBlocking { runBlocking {
class MyClass( class MyClass(
val value: Int val value: Int,
) )
val composite = object : CompositeCommand( val composite = object : CompositeCommand(
@ -215,10 +224,12 @@ internal class TestCommand {
composite.withRegistration { composite.withRegistration {
assertEquals(333, withTesting<MyClass> { execute(sender, "mute 333") }.value) assertEquals(333, withTesting<MyClass> { execute(sender, "mute 333") }.value)
assertEquals(2, withTesting<MyClass> { assertEquals(2, withTesting<MyClass> {
execute(sender, buildMessageChain { assertSuccess(
+"mute" execute(sender, buildMessageChain {
+image +"mute"
}) +image
})
)
}.value) }.value)
} }
} }