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() {
/** 指令执行时发生的错误, 总是 `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
public fun CommandExecuteResult.isCommandNotFound(): Boolean {
contract {
returns(true) implies (this@isCommandNotFound is CommandExecuteResult.CommandNotFound)
returns(false) implies (this@isCommandNotFound !is CommandExecuteResult.CommandNotFound)
returns(true) implies (this@isCommandNotFound is CommandExecuteResult.UnresolvedCall)
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
public fun CommandExecuteResult.isFailure(): Boolean {

View File

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

View File

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

View File

@ -81,7 +81,7 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by MiraiCons
sender.catchExecutionException(result.exception)
intercept()
}
is CommandExecuteResult.CommandNotFound -> {
is CommandExecuteResult.UnresolvedCall -> {
// noop
}
}
@ -140,11 +140,11 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by MiraiCons
override fun Command.unregister(): Boolean = modifyLock.withLock {
if (this.prefixOptional) {
this.allNames.forEach {
optionalPrefixCommandMap.remove(it)
optionalPrefixCommandMap.remove(it.toLowerCase())
}
}
this.allNames.forEach {
requiredPrefixCommandMap.remove(it)
requiredPrefixCommandMap.remove(it.toLowerCase())
}
_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.descriptor.*
import net.mamoe.mirai.console.internal.command.hasAnnotation
import net.mamoe.mirai.console.permission.Permission
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.PlainText
@ -22,10 +23,7 @@ import kotlin.reflect.KAnnotatedElement
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.full.callSuspendBy
import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.*
internal object CompositeCommandSubCommandAnnotationResolver :
AbstractReflectionCommand.SubCommandAnnotationResolver {
@ -322,7 +320,9 @@ internal fun AbstractReflectionCommand.createSubCommand(
subDescription, // overridePermission?.value
permission,//overridePermission?.value?.let { PermissionService.INSTANCE[PermissionId.parseFromString(it)] } ?: permission,
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})" }

View File

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