diff --git a/backend/mirai-console/src/command/CommandExecuteResult.kt b/backend/mirai-console/src/command/CommandExecuteResult.kt index 106d1a8b6..72acb223b 100644 --- a/backend/mirai-console/src/command/CommandExecuteResult.kt +++ b/backend/mirai-console/src/command/CommandExecuteResult.kt @@ -12,7 +12,10 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.console.command.CommandExecuteResult.CommandExecuteStatus -import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors +import net.mamoe.mirai.console.command.descriptor.* +import net.mamoe.mirai.console.command.parse.CommandCall +import net.mamoe.mirai.console.command.parse.CommandValueArgument +import net.mamoe.mirai.console.command.resolve.ResolvedCommandCall import net.mamoe.mirai.console.util.ConsoleExperimentalApi import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.MessageChain @@ -37,6 +40,12 @@ public sealed class CommandExecuteResult { /** 尝试执行的指令 (如果匹配到) */ public abstract val command: Command? + /** 解析的 [CommandCall] (如果匹配到) */ + public abstract val call: CommandCall? + + /** 解析的 [ResolvedCommandCall] (如果匹配到) */ + public abstract val resolvedCall: ResolvedCommandCall? + /** 尝试执行的指令名 (如果匹配到) */ public abstract val commandName: String? @@ -49,10 +58,14 @@ public sealed class CommandExecuteResult { public class Success( /** 尝试执行的指令 */ public override val command: Command, + /** 解析的 [CommandCall] (如果匹配到) */ + public override val call: CommandCall, + /** 解析的 [ResolvedCommandCall] (如果匹配到) */ + public override val resolvedCall: ResolvedCommandCall, /** 尝试执行的指令名 */ public override val commandName: String, /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ - public override val args: MessageChain + public override val args: MessageChain, ) : CommandExecuteResult() { /** 指令执行时发生的错误, 总是 `null` */ public override val exception: Nothing? get() = null @@ -61,61 +74,82 @@ public sealed class CommandExecuteResult { public override val status: CommandExecuteStatus get() = CommandExecuteStatus.SUCCESSFUL } + /** 指令执行失败 */ + public abstract class Failure : CommandExecuteResult() + /** 执行执行时发生了一个非法参数错误 */ public class IllegalArgument( /** 指令执行时发生的错误 */ public override val exception: IllegalCommandArgumentException, /** 尝试执行的指令 */ public override val command: Command, + /** 解析的 [CommandCall] (如果匹配到) */ + public override val call: CommandCall, + /** 解析的 [ResolvedCommandCall] (如果匹配到) */ + public override val resolvedCall: ResolvedCommandCall, /** 尝试执行的指令名 */ public override val commandName: String, /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ - public override val args: MessageChain - ) : CommandExecuteResult() { + public override val args: MessageChain, + ) : Failure() { /** 指令最终执行状态, 总是 [CommandExecuteStatus.EXECUTION_EXCEPTION] */ public override val status: CommandExecuteStatus get() = CommandExecuteStatus.ILLEGAL_ARGUMENT } - /** 指令执行过程出现了错误 */ + /** 指令方法调用过程出现了错误 */ public class ExecutionFailed( /** 指令执行时发生的错误 */ public override val exception: Throwable, /** 尝试执行的指令 */ public override val command: Command, + /** 解析的 [CommandCall] (如果匹配到) */ + public override val call: CommandCall, + /** 解析的 [ResolvedCommandCall] (如果匹配到) */ + public override val resolvedCall: ResolvedCommandCall, /** 尝试执行的指令名 */ public override val commandName: String, /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ - public override val args: MessageChain - ) : CommandExecuteResult() { + public override val args: MessageChain, + ) : Failure() { /** 指令最终执行状态, 总是 [CommandExecuteStatus.EXECUTION_EXCEPTION] */ public override val status: CommandExecuteStatus get() = CommandExecuteStatus.EXECUTION_EXCEPTION } /** 没有匹配的指令 */ - public class UnresolvedCall( + public class UnresolvedCommand( /** 尝试执行的指令名 */ public override val commandName: String, - ) : CommandExecuteResult() { + ) : Failure() { /** 指令执行时发生的错误, 总是 `null` */ public override val exception: Nothing? get() = null /** 尝试执行的指令, 总是 `null` */ public override val command: Nothing? get() = null + /** 解析的 [CommandCall] (如果匹配到) */ + public override val call: CommandCall? get() = null + + /** 解析的 [ResolvedCommandCall] (如果匹配到) */ + public override val resolvedCall: ResolvedCommandCall? get() = null + /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ public override val args: Nothing? get() = null - /** 指令最终执行状态, 总是 [CommandExecuteStatus.COMMAND_NOT_FOUND] */ - public override val status: CommandExecuteStatus get() = CommandExecuteStatus.COMMAND_NOT_FOUND + /** 指令最终执行状态, 总是 [CommandExecuteStatus.UNRESOLVED_COMMAND] */ + public override val status: CommandExecuteStatus get() = CommandExecuteStatus.UNRESOLVED_COMMAND } /** 权限不足 */ public class PermissionDenied( /** 尝试执行的指令 */ public override val command: Command, + /** 解析的 [CommandCall] (如果匹配到) */ + public override val call: CommandCall, + /** 解析的 [ResolvedCommandCall] (如果匹配到) */ + public override val resolvedCall: ResolvedCommandCall, /** 尝试执行的指令名 */ - public override val commandName: String - ) : CommandExecuteResult() { + public override val commandName: String, + ) : Failure() { /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ public override val args: Nothing? get() = null @@ -126,6 +160,33 @@ public sealed class CommandExecuteResult { public override val status: CommandExecuteStatus get() = CommandExecuteStatus.PERMISSION_DENIED } + /** 没有匹配的指令 */ + public class UnmatchedSignature( + /** 尝试执行的指令名 */ + public override val commandName: String, + /** 尝试执行的指令 */ + public override val command: Command, + /** 尝试执行的指令 */ + @ExperimentalCommandDescriptors + @ConsoleExperimentalApi + public val failureReasons: List, + ) : Failure() { + /** 指令执行时发生的错误, 总是 `null` */ + public override val exception: Nothing? get() = null + + /** 解析的 [CommandCall] (如果匹配到) */ + public override val call: CommandCall? get() = null + + /** 解析的 [ResolvedCommandCall] (如果匹配到) */ + public override val resolvedCall: ResolvedCommandCall? get() = null + + /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ + public override val args: Nothing? get() = null + + /** 指令最终执行状态, 总是 [CommandExecuteStatus.UNMATCHED_SIGNATURE] */ + public override val status: CommandExecuteStatus get() = CommandExecuteStatus.UNMATCHED_SIGNATURE + } + /** * 指令的执行状态 */ @@ -137,15 +198,56 @@ public sealed class CommandExecuteResult { EXECUTION_EXCEPTION, /** 没有匹配的指令 */ - COMMAND_NOT_FOUND, + UNMATCHED_SIGNATURE, + + /** 没有匹配的指令 */ + UNRESOLVED_COMMAND, /** 权限不足 */ PERMISSION_DENIED, + /** 非法参数 */ ILLEGAL_ARGUMENT, } } +@ExperimentalCommandDescriptors +@ConsoleExperimentalApi +public class UnmatchedCommandSignature( + public val signature: CommandSignature, + public val failureReason: FailureReason, +) + +@ExperimentalCommandDescriptors +@ConsoleExperimentalApi +public sealed class FailureReason { + public class InapplicableReceiverArgument( + public override val parameter: CommandReceiverParameter<*>, + public val argument: CommandSender, + ) : InapplicableArgument() + + public class InapplicableValueArgument( + public override val parameter: CommandValueParameter<*>, + public val argument: CommandValueArgument, + ) : InapplicableArgument() + + public abstract class InapplicableArgument : FailureReason() { + public abstract val parameter: CommandParameter<*> + } + + public abstract class ArgumentLengthMismatch : FailureReason() + + public data class ResolutionAmbiguity( + /** + * Including [self][UnmatchedCommandSignature.signature]. + */ + public val allCandidates: List, + ) : FailureReason() + + public object TooManyArguments : ArgumentLengthMismatch() + public object NotEnoughArguments : ArgumentLengthMismatch() +} + @ExperimentalCommandDescriptors @Suppress("RemoveRedundantQualifierName") public typealias CommandExecuteStatus = CommandExecuteResult.CommandExecuteStatus @@ -164,59 +266,7 @@ public fun CommandExecuteResult.isSuccess(): Boolean { } /** - * 当 [this] 为 [CommandExecuteResult.IllegalArgument] 时返回 `true` - */ -@ExperimentalCommandDescriptors -@JvmSynthetic -public fun CommandExecuteResult.isIllegalArgument(): Boolean { - contract { - returns(true) implies (this@isIllegalArgument is CommandExecuteResult.IllegalArgument) - returns(false) implies (this@isIllegalArgument !is CommandExecuteResult.IllegalArgument) - } - return this is CommandExecuteResult.IllegalArgument -} - -/** - * 当 [this] 为 [CommandExecuteResult.ExecutionFailed] 时返回 `true` - */ -@ExperimentalCommandDescriptors -@JvmSynthetic -public fun CommandExecuteResult.isExecutionException(): Boolean { - contract { - returns(true) implies (this@isExecutionException is CommandExecuteResult.ExecutionFailed) - returns(false) implies (this@isExecutionException !is CommandExecuteResult.ExecutionFailed) - } - return this is CommandExecuteResult.ExecutionFailed -} - -/** - * 当 [this] 为 [CommandExecuteResult.PermissionDenied] 时返回 `true` - */ -@ExperimentalCommandDescriptors -@JvmSynthetic -public fun CommandExecuteResult.isPermissionDenied(): Boolean { - contract { - returns(true) implies (this@isPermissionDenied is CommandExecuteResult.PermissionDenied) - returns(false) implies (this@isPermissionDenied !is CommandExecuteResult.PermissionDenied) - } - return this is CommandExecuteResult.PermissionDenied -} - -/** - * 当 [this] 为 [CommandExecuteResult.UnresolvedCall] 时返回 `true` - */ -@ExperimentalCommandDescriptors -@JvmSynthetic -public fun CommandExecuteResult.isCommandNotFound(): Boolean { - contract { - returns(true) implies (this@isCommandNotFound is CommandExecuteResult.UnresolvedCall) - returns(false) implies (this@isCommandNotFound !is CommandExecuteResult.UnresolvedCall) - } - return this is CommandExecuteResult.UnresolvedCall -} - -/** - * 当 [this] 为 [CommandExecuteResult.ExecutionFailed], [CommandExecuteResult.IllegalArgument] 或 [CommandExecuteResult.UnresolvedCall] 时返回 `true` + * 当 [this] 为 [CommandExecuteResult.ExecutionFailed], [CommandExecuteResult.IllegalArgument] , [CommandExecuteResult.UnmatchedSignature] 或 [CommandExecuteResult.UnresolvedCommand] 时返回 `true` */ @ExperimentalCommandDescriptors @JvmSynthetic diff --git a/backend/mirai-console/src/command/descriptor/CommandParameter.kt b/backend/mirai-console/src/command/descriptor/CommandParameter.kt index 225615447..160d02752 100644 --- a/backend/mirai-console/src/command/descriptor/CommandParameter.kt +++ b/backend/mirai-console/src/command/descriptor/CommandParameter.kt @@ -75,15 +75,15 @@ public sealed class ArgumentAcceptance( ) { public object Direct : ArgumentAcceptance(Int.MAX_VALUE) - public class WithTypeConversion( + public data class WithTypeConversion( public val typeVariant: TypeVariant<*>, ) : ArgumentAcceptance(20) - public class WithContextualConversion( + public data class WithContextualConversion( public val parser: CommandValueArgumentParser<*>, ) : ArgumentAcceptance(10) - public class ResolutionAmbiguity( + public data class ResolutionAmbiguity( public val candidates: List>, ) : ArgumentAcceptance(0) @@ -101,7 +101,7 @@ public sealed class ArgumentAcceptance( } @ExperimentalCommandDescriptors -public class CommandReceiverParameter( +public data class CommandReceiverParameter( override val isOptional: Boolean, override val type: KType, ) : CommandParameter, AbstractCommandParameter() { diff --git a/backend/mirai-console/src/command/resolve/BuiltInCommandCallResolver.kt b/backend/mirai-console/src/command/resolve/BuiltInCommandCallResolver.kt index 39ab70db7..c45a10da8 100644 --- a/backend/mirai-console/src/command/resolve/BuiltInCommandCallResolver.kt +++ b/backend/mirai-console/src/command/resolve/BuiltInCommandCallResolver.kt @@ -9,9 +9,7 @@ package net.mamoe.mirai.console.command.resolve -import net.mamoe.mirai.console.command.Command -import net.mamoe.mirai.console.command.CommandManager -import net.mamoe.mirai.console.command.CommandSender +import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.descriptor.* import net.mamoe.mirai.console.command.descriptor.ArgumentAcceptance.Companion.isNotAcceptable import net.mamoe.mirai.console.command.parse.CommandCall @@ -35,13 +33,19 @@ public object BuiltInCommandCallResolver : CommandCallResolver { val valueArguments = call.valueArguments val context = callee.safeCast()?.context - val signature = resolveImpl(call.caller, callee, valueArguments, context) ?: return CommandResolveResult(null) + val errorSink = ErrorSink() + val signature = resolveImpl(call.caller, callee, valueArguments, context, errorSink) ?: kotlin.run { + return CommandResolveResult(errorSink.createFailure(call, callee)) + } - return CommandResolveResult(ResolvedCommandCallImpl(call.caller, - callee, - signature.signature, - signature.zippedArguments.map { it.second }, - context ?: EmptyCommandArgumentContext) + return CommandResolveResult( + ResolvedCommandCallImpl( + call.caller, + callee, + signature.signature, + signature.zippedArguments.map { it.second }, + context ?: EmptyCommandArgumentContext + ) ) } @@ -59,76 +63,121 @@ public object BuiltInCommandCallResolver : CommandCallResolver { val acceptance: ArgumentAcceptance, ) + private class ErrorSink { + private val unmatchedCommandSignatures = mutableListOf() + private val resolutionAmbiguities = mutableListOf() + + fun reportUnmatched(failure: UnmatchedCommandSignature) { + unmatchedCommandSignatures.add(failure) + } + + fun reportAmbiguity(resolutionAmbiguity: CommandSignature) { + resolutionAmbiguities.add(resolutionAmbiguity) + } + + fun createFailure(call: CommandCall, command: Command): CommandExecuteResult.Failure { + val failureReasons = unmatchedCommandSignatures.toMutableList() + val rA = FailureReason.ResolutionAmbiguity(resolutionAmbiguities) + failureReasons.addAll(resolutionAmbiguities.map { UnmatchedCommandSignature(it, rA) }) + return CommandExecuteResult.UnmatchedSignature(call.calleeName, command, unmatchedCommandSignatures) + } + + } + + private + fun CommandSignature.toResolveData( + caller: CommandSender, + valueArguments: List, + context: CommandArgumentContext?, + errorSink: ErrorSink, + ): ResolveData? { + val signature = this + val receiverParameter = signature.receiverParameter + if (receiverParameter?.type?.classifierAsKClass()?.isInstance(caller) == false) { + errorSink.reportUnmatched( + UnmatchedCommandSignature(signature, FailureReason.InapplicableReceiverArgument(receiverParameter, caller)) + )// not compatible receiver + return null + } + + val valueParameters = signature.valueParameters + + val zipped = valueParameters.zip(valueArguments).toMutableList() + + val remainingParameters = valueParameters.drop(zipped.size).toMutableList() + + if (remainingParameters.any { !it.isOptional && !it.isVararg }) { + errorSink.reportUnmatched(UnmatchedCommandSignature(signature, FailureReason.NotEnoughArguments))// not enough args. // vararg can be empty. + return null + } + + return if (zipped.isEmpty()) { + ResolveData( + signature = signature, + zippedArguments = emptyList(), + argumentAcceptances = emptyList(), + remainingParameters = remainingParameters, + ) + } else { + if (valueArguments.size > valueParameters.size && zipped.last().first.isVararg) { + // merge vararg arguments + val (varargParameter, _) + = zipped.removeLast() + + zipped.add(varargParameter to DefaultCommandValueArgument(valueArguments.drop(zipped.size).map { it.value }.asMessageChain())) + } else { + // add default empty vararg argument + val remainingVararg = remainingParameters.find { it.isVararg } + if (remainingVararg != null) { + zipped.add(remainingVararg to DefaultCommandValueArgument(EmptyMessageChain)) + remainingParameters.remove(remainingVararg) + } + } + + ResolveData( + signature = signature, + zippedArguments = zipped, + argumentAcceptances = zipped.mapIndexed { index, (parameter, argument) -> + val accepting = parameter.accepting(argument, context) + if (accepting.isNotAcceptable) { + errorSink.reportUnmatched(UnmatchedCommandSignature(signature, + FailureReason.InapplicableValueArgument(parameter, argument)))// argument type not assignable + return null + } + ArgumentAcceptanceWithIndex(index, accepting) + }, + remainingParameters = remainingParameters + ) + } + } + private fun resolveImpl( caller: CommandSender, callee: Command, valueArguments: List, context: CommandArgumentContext?, + errorSink: ErrorSink, ): ResolveData? { callee.overloads .mapNotNull l@{ signature -> - if (signature.receiverParameter?.type?.classifierAsKClass()?.isInstance(caller) == false) { - return@l null // not compatible receiver - } - - val valueParameters = signature.valueParameters - - val zipped = valueParameters.zip(valueArguments).toMutableList() - - val remainingParameters = valueParameters.drop(zipped.size).toMutableList() - - if (remainingParameters.any { !it.isOptional && !it.isVararg }) return@l null // not enough args. // vararg can be empty. - - if (zipped.isEmpty()) { - ResolveData( - signature = signature, - zippedArguments = emptyList(), - argumentAcceptances = emptyList(), - remainingParameters = remainingParameters, - ) - } else { - if (valueArguments.size > valueParameters.size && zipped.last().first.isVararg) { - // merge vararg arguments - val (varargParameter, _) - = zipped.removeLast() - - zipped.add(varargParameter to DefaultCommandValueArgument(valueArguments.drop(zipped.size).map { it.value }.asMessageChain())) - } else { - // add default empty vararg argument - val remainingVararg = remainingParameters.find { it.isVararg } - if (remainingVararg != null) { - zipped.add(remainingVararg to DefaultCommandValueArgument(EmptyMessageChain)) - remainingParameters.remove(remainingVararg) - } - } - - ResolveData( - signature = signature, - zippedArguments = zipped, - argumentAcceptances = zipped.mapIndexed { index, (parameter, argument) -> - val accepting = parameter.accepting(argument, context) - if (accepting.isNotAcceptable) { - return@l null // argument type not assignable - } - ArgumentAcceptanceWithIndex(index, accepting) - }, - remainingParameters = remainingParameters - ) - } + signature.toResolveData(caller, valueArguments, context, errorSink) } - .also { result -> result.singleOrNull()?.let { return it } } + .also { result -> result.takeSingleResolveData()?.let { return it } } .takeLongestMatches() .ifEmpty { return null } - .also { result -> result.singleOrNull()?.let { return it } } + .also { result -> result.takeSingleResolveData()?.let { return it } } // take single ArgumentAcceptance.Direct .also { list -> val candidates = list + .asSequence().filterIsInstance() .flatMap { phase -> phase.argumentAcceptances.filter { it.acceptance is ArgumentAcceptance.Direct }.map { phase to it } - } + }.toList() + candidates.singleOrNull()?.let { return it.first } // single Direct + if (candidates.distinctBy { it.second.index }.size != candidates.size) { // Resolution ambiguity /* @@ -142,13 +191,17 @@ public object BuiltInCommandCallResolver : CommandCallResolver { fun foo(a: AA, c: C) = 1 */ // The call is foo(AA(), C()) or foo(A(), CC()) - return null + + candidates.forEach { candidate -> errorSink.reportAmbiguity(candidate.first.signature) } + } } return null } + private fun Collection.takeSingleResolveData() = asSequence().filterIsInstance().singleOrNull() + /* diff --git a/backend/mirai-console/src/command/resolve/CommandCallResolver.kt b/backend/mirai-console/src/command/resolve/CommandCallResolver.kt index 798d3c1c1..72209e2d0 100644 --- a/backend/mirai-console/src/command/resolve/CommandCallResolver.kt +++ b/backend/mirai-console/src/command/resolve/CommandCallResolver.kt @@ -40,9 +40,9 @@ public class CommandResolveResult private constructor( callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE) callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE) } - call?.let(onSuccess) - failure?.let(onFailure) - null!! + call?.let(onSuccess)?.let { return it } + failure?.let(onFailure)?.let { return it } + throw kotlin.AssertionError() } public constructor(call: ResolvedCommandCall?) : this(call as Any?) diff --git a/backend/mirai-console/src/internal/command/CommandManagerImpl.kt b/backend/mirai-console/src/internal/command/CommandManagerImpl.kt index 8640664f1..1c978c3f0 100644 --- a/backend/mirai-console/src/internal/command/CommandManagerImpl.kt +++ b/backend/mirai-console/src/internal/command/CommandManagerImpl.kt @@ -17,6 +17,7 @@ import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.Command.Companion.allNames import net.mamoe.mirai.console.command.CommandManager.INSTANCE.findDuplicate import net.mamoe.mirai.console.command.CommandSender.Companion.toCommandSender +import net.mamoe.mirai.console.command.descriptor.CommandArgumentParserException import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors import net.mamoe.mirai.console.command.parse.CommandCallParser.Companion.parseCommandCall import net.mamoe.mirai.console.command.resolve.CommandCallResolver.Companion.resolve @@ -94,7 +95,9 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by MiraiCons sender.catchExecutionException(result.exception) intercept() } - is CommandExecuteResult.UnresolvedCall -> { + is CommandExecuteResult.UnmatchedSignature, + is CommandExecuteResult.UnresolvedCommand, + -> { // noop } } @@ -175,20 +178,25 @@ internal suspend fun executeCommandImpl( caller: CommandSender, checkPermission: Boolean, ): CommandExecuteResult { - val call = message.asMessageChain().parseCommandCall(caller) ?: return CommandExecuteResult.UnresolvedCall("") - val resolved = call.resolve() ?: return CommandExecuteResult.UnresolvedCall(call.calleeName) + val call = message.asMessageChain().parseCommandCall(caller) ?: return CommandExecuteResult.UnresolvedCommand("") + val resolved = call.resolve().fold( + onSuccess = { it }, + onFailure = { return it } + ) val command = resolved.callee if (checkPermission && !command.permission.testPermission(caller)) { - return CommandExecuteResult.PermissionDenied(command, call.calleeName) + return CommandExecuteResult.PermissionDenied(command, call, resolved, call.calleeName) } return try { resolved.calleeSignature.call(resolved) - CommandExecuteResult.Success(resolved.callee, call.calleeName, EmptyMessageChain) + CommandExecuteResult.Success(resolved.callee, call, resolved, call.calleeName, EmptyMessageChain) + } catch (e: CommandArgumentParserException) { + CommandExecuteResult.IllegalArgument(e, resolved.callee, call, resolved, call.calleeName, EmptyMessageChain) } catch (e: Throwable) { - CommandExecuteResult.ExecutionFailed(e, resolved.callee, call.calleeName, EmptyMessageChain) + CommandExecuteResult.ExecutionFailed(e, resolved.callee, call, resolved, call.calleeName, EmptyMessageChain) } } diff --git a/frontend/mirai-console-terminal/src/ConsoleThread.kt b/frontend/mirai-console-terminal/src/ConsoleThread.kt index 2e2e78fba..575b28be9 100644 --- a/frontend/mirai-console-terminal/src/ConsoleThread.kt +++ b/frontend/mirai-console-terminal/src/ConsoleThread.kt @@ -12,11 +12,14 @@ package net.mamoe.mirai.console.terminal import kotlinx.coroutines.* import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.command.* +import net.mamoe.mirai.console.command.CommandExecuteResult.* +import net.mamoe.mirai.console.command.CommandExecuteResult.CommandExecuteStatus.* import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors import net.mamoe.mirai.console.terminal.noconsole.NoConsole import net.mamoe.mirai.console.util.ConsoleInternalApi import net.mamoe.mirai.console.util.requestInput import net.mamoe.mirai.utils.DefaultLogger +import net.mamoe.mirai.utils.warning import org.jline.reader.EndOfFileException import org.jline.reader.UserInterruptException @@ -55,21 +58,28 @@ internal fun startupConsoleThread() { continue } // consoleLogger.debug("INPUT> $next") - val result = ConsoleCommandSender.executeCommand(next) - when (result.status) { - CommandExecuteStatus.SUCCESSFUL -> { + when (val result = ConsoleCommandSender.executeCommand(next)) { + is Success -> { } - CommandExecuteStatus.ILLEGAL_ARGUMENT -> { - result.exception?.message?.let { consoleLogger.warning(it) } + is IllegalArgument -> { + result.exception.message?.let { consoleLogger.warning(it) } ?: kotlin.run { + consoleLogger.warning(result.exception) + } } - CommandExecuteStatus.EXECUTION_EXCEPTION -> { - result.exception?.let(consoleLogger::error) + is ExecutionFailed -> { + consoleLogger.error(result.exception) } - CommandExecuteStatus.COMMAND_NOT_FOUND -> { - consoleLogger.warning("未知指令: ${result.commandName}, 输入 ? 获取帮助") + is UnresolvedCommand -> { + consoleLogger.warning { "未知指令: ${result.commandName}, 输入 ? 获取帮助" } } - CommandExecuteStatus.PERMISSION_DENIED -> { - consoleLogger.warning("Permission denied.") + is PermissionDenied -> { + consoleLogger.warning { "权限不足." } + } + is UnmatchedSignature -> { + consoleLogger.warning { "参数不匹配: " + result.failureReasons.joinToString("\n") { it.render() } } + } + is Failure -> { + consoleLogger.warning { result.toString() } } } } catch (e: InterruptedException) { @@ -88,3 +98,22 @@ internal fun startupConsoleThread() { } } } + +@OptIn(ExperimentalCommandDescriptors::class) +internal fun UnmatchedCommandSignature.render(): String { + return this.signature.toString() + " ${failureReason.render()}" +} + +@OptIn(ExperimentalCommandDescriptors::class) +internal fun FailureReason.render(): String { + return when (this) { + is FailureReason.InapplicableArgument -> "参数类型错误" + is FailureReason.TooManyArguments -> "参数过多" + is FailureReason.NotEnoughArguments -> "参数不足" + is FailureReason.ResolutionAmbiguity -> "调用歧义" + is FailureReason.ArgumentLengthMismatch -> { + // should not happen, render it anyway. + "参数长度不匹配" + } + } +} \ No newline at end of file