Call parse with errors

This commit is contained in:
Him188 2020-11-16 09:40:54 +08:00
parent 8da8615721
commit 64790d0114
6 changed files with 292 additions and 152 deletions

View File

@ -12,7 +12,10 @@
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.CommandExecuteResult.CommandExecuteStatus 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.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
@ -37,6 +40,12 @@ public sealed class CommandExecuteResult {
/** 尝试执行的指令 (如果匹配到) */ /** 尝试执行的指令 (如果匹配到) */
public abstract val command: Command? public abstract val command: Command?
/** 解析的 [CommandCall] (如果匹配到) */
public abstract val call: CommandCall?
/** 解析的 [ResolvedCommandCall] (如果匹配到) */
public abstract val resolvedCall: ResolvedCommandCall?
/** 尝试执行的指令名 (如果匹配到) */ /** 尝试执行的指令名 (如果匹配到) */
public abstract val commandName: String? public abstract val commandName: String?
@ -49,10 +58,14 @@ public sealed class CommandExecuteResult {
public class Success( public class Success(
/** 尝试执行的指令 */ /** 尝试执行的指令 */
public override val command: Command, public override val command: Command,
/** 解析的 [CommandCall] (如果匹配到) */
public override val call: CommandCall,
/** 解析的 [ResolvedCommandCall] (如果匹配到) */
public override val resolvedCall: ResolvedCommandCall,
/** 尝试执行的指令名 */ /** 尝试执行的指令名 */
public override val commandName: String, public override val commandName: String,
/** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */
public override val args: MessageChain public override val args: MessageChain,
) : CommandExecuteResult() { ) : CommandExecuteResult() {
/** 指令执行时发生的错误, 总是 `null` */ /** 指令执行时发生的错误, 总是 `null` */
public override val exception: Nothing? get() = 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 override val status: CommandExecuteStatus get() = CommandExecuteStatus.SUCCESSFUL
} }
/** 指令执行失败 */
public abstract class Failure : CommandExecuteResult()
/** 执行执行时发生了一个非法参数错误 */ /** 执行执行时发生了一个非法参数错误 */
public class IllegalArgument( public class IllegalArgument(
/** 指令执行时发生的错误 */ /** 指令执行时发生的错误 */
public override val exception: IllegalCommandArgumentException, public override val exception: IllegalCommandArgumentException,
/** 尝试执行的指令 */ /** 尝试执行的指令 */
public override val command: Command, public override val command: Command,
/** 解析的 [CommandCall] (如果匹配到) */
public override val call: CommandCall,
/** 解析的 [ResolvedCommandCall] (如果匹配到) */
public override val resolvedCall: ResolvedCommandCall,
/** 尝试执行的指令名 */ /** 尝试执行的指令名 */
public override val commandName: String, public override val commandName: String,
/** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */
public override val args: MessageChain public override val args: MessageChain,
) : CommandExecuteResult() { ) : Failure() {
/** 指令最终执行状态, 总是 [CommandExecuteStatus.EXECUTION_EXCEPTION] */ /** 指令最终执行状态, 总是 [CommandExecuteStatus.EXECUTION_EXCEPTION] */
public override val status: CommandExecuteStatus get() = CommandExecuteStatus.ILLEGAL_ARGUMENT public override val status: CommandExecuteStatus get() = CommandExecuteStatus.ILLEGAL_ARGUMENT
} }
/** 指令执行过程出现了错误 */ /** 指令方法调用过程出现了错误 */
public class ExecutionFailed( public class ExecutionFailed(
/** 指令执行时发生的错误 */ /** 指令执行时发生的错误 */
public override val exception: Throwable, public override val exception: Throwable,
/** 尝试执行的指令 */ /** 尝试执行的指令 */
public override val command: Command, public override val command: Command,
/** 解析的 [CommandCall] (如果匹配到) */
public override val call: CommandCall,
/** 解析的 [ResolvedCommandCall] (如果匹配到) */
public override val resolvedCall: ResolvedCommandCall,
/** 尝试执行的指令名 */ /** 尝试执行的指令名 */
public override val commandName: String, public override val commandName: String,
/** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */
public override val args: MessageChain public override val args: MessageChain,
) : CommandExecuteResult() { ) : Failure() {
/** 指令最终执行状态, 总是 [CommandExecuteStatus.EXECUTION_EXCEPTION] */ /** 指令最终执行状态, 总是 [CommandExecuteStatus.EXECUTION_EXCEPTION] */
public override val status: CommandExecuteStatus get() = CommandExecuteStatus.EXECUTION_EXCEPTION public override val status: CommandExecuteStatus get() = CommandExecuteStatus.EXECUTION_EXCEPTION
} }
/** 没有匹配的指令 */ /** 没有匹配的指令 */
public class UnresolvedCall( public class UnresolvedCommand(
/** 尝试执行的指令名 */ /** 尝试执行的指令名 */
public override val commandName: String, public override val commandName: String,
) : CommandExecuteResult() { ) : Failure() {
/** 指令执行时发生的错误, 总是 `null` */ /** 指令执行时发生的错误, 总是 `null` */
public override val exception: Nothing? get() = null public override val exception: Nothing? get() = null
/** 尝试执行的指令, 总是 `null` */ /** 尝试执行的指令, 总是 `null` */
public override val command: Nothing? get() = 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] */ /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */
public override val args: Nothing? get() = null public override val args: Nothing? get() = null
/** 指令最终执行状态, 总是 [CommandExecuteStatus.COMMAND_NOT_FOUND] */ /** 指令最终执行状态, 总是 [CommandExecuteStatus.UNRESOLVED_COMMAND] */
public override val status: CommandExecuteStatus get() = CommandExecuteStatus.COMMAND_NOT_FOUND public override val status: CommandExecuteStatus get() = CommandExecuteStatus.UNRESOLVED_COMMAND
} }
/** 权限不足 */ /** 权限不足 */
public class PermissionDenied( public class PermissionDenied(
/** 尝试执行的指令 */ /** 尝试执行的指令 */
public override val command: Command, public override val command: Command,
/** 解析的 [CommandCall] (如果匹配到) */
public override val call: CommandCall,
/** 解析的 [ResolvedCommandCall] (如果匹配到) */
public override val resolvedCall: ResolvedCommandCall,
/** 尝试执行的指令名 */ /** 尝试执行的指令名 */
public override val commandName: String public override val commandName: String,
) : CommandExecuteResult() { ) : Failure() {
/** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */
public override val args: Nothing? get() = null 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 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<UnmatchedCommandSignature>,
) : 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, EXECUTION_EXCEPTION,
/** 没有匹配的指令 */ /** 没有匹配的指令 */
COMMAND_NOT_FOUND, UNMATCHED_SIGNATURE,
/** 没有匹配的指令 */
UNRESOLVED_COMMAND,
/** 权限不足 */ /** 权限不足 */
PERMISSION_DENIED, PERMISSION_DENIED,
/** 非法参数 */ /** 非法参数 */
ILLEGAL_ARGUMENT, 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<CommandSignature>,
) : FailureReason()
public object TooManyArguments : ArgumentLengthMismatch()
public object NotEnoughArguments : ArgumentLengthMismatch()
}
@ExperimentalCommandDescriptors @ExperimentalCommandDescriptors
@Suppress("RemoveRedundantQualifierName") @Suppress("RemoveRedundantQualifierName")
public typealias CommandExecuteStatus = CommandExecuteResult.CommandExecuteStatus public typealias CommandExecuteStatus = CommandExecuteResult.CommandExecuteStatus
@ -164,59 +266,7 @@ public fun CommandExecuteResult.isSuccess(): Boolean {
} }
/** /**
* [this] [CommandExecuteResult.IllegalArgument] 时返回 `true` * [this] [CommandExecuteResult.ExecutionFailed], [CommandExecuteResult.IllegalArgument] , [CommandExecuteResult.UnmatchedSignature] [CommandExecuteResult.UnresolvedCommand] 时返回 `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`
*/ */
@ExperimentalCommandDescriptors @ExperimentalCommandDescriptors
@JvmSynthetic @JvmSynthetic

View File

@ -75,15 +75,15 @@ public sealed class ArgumentAcceptance(
) { ) {
public object Direct : ArgumentAcceptance(Int.MAX_VALUE) public object Direct : ArgumentAcceptance(Int.MAX_VALUE)
public class WithTypeConversion( public data class WithTypeConversion(
public val typeVariant: TypeVariant<*>, public val typeVariant: TypeVariant<*>,
) : ArgumentAcceptance(20) ) : ArgumentAcceptance(20)
public class WithContextualConversion( public data class WithContextualConversion(
public val parser: CommandValueArgumentParser<*>, public val parser: CommandValueArgumentParser<*>,
) : ArgumentAcceptance(10) ) : ArgumentAcceptance(10)
public class ResolutionAmbiguity( public data class ResolutionAmbiguity(
public val candidates: List<TypeVariant<*>>, public val candidates: List<TypeVariant<*>>,
) : ArgumentAcceptance(0) ) : ArgumentAcceptance(0)
@ -101,7 +101,7 @@ public sealed class ArgumentAcceptance(
} }
@ExperimentalCommandDescriptors @ExperimentalCommandDescriptors
public class CommandReceiverParameter<T : CommandSender>( public data class CommandReceiverParameter<T : CommandSender>(
override val isOptional: Boolean, override val isOptional: Boolean,
override val type: KType, override val type: KType,
) : CommandParameter<T>, AbstractCommandParameter<T>() { ) : CommandParameter<T>, AbstractCommandParameter<T>() {

View File

@ -9,9 +9,7 @@
package net.mamoe.mirai.console.command.resolve package net.mamoe.mirai.console.command.resolve
import net.mamoe.mirai.console.command.Command import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.descriptor.* import net.mamoe.mirai.console.command.descriptor.*
import net.mamoe.mirai.console.command.descriptor.ArgumentAcceptance.Companion.isNotAcceptable import net.mamoe.mirai.console.command.descriptor.ArgumentAcceptance.Companion.isNotAcceptable
import net.mamoe.mirai.console.command.parse.CommandCall import net.mamoe.mirai.console.command.parse.CommandCall
@ -35,13 +33,19 @@ public object BuiltInCommandCallResolver : CommandCallResolver {
val valueArguments = call.valueArguments val valueArguments = call.valueArguments
val context = callee.safeCast<CommandArgumentContextAware>()?.context val context = callee.safeCast<CommandArgumentContextAware>()?.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, return CommandResolveResult(
callee, ResolvedCommandCallImpl(
signature.signature, call.caller,
signature.zippedArguments.map { it.second }, callee,
context ?: EmptyCommandArgumentContext) signature.signature,
signature.zippedArguments.map { it.second },
context ?: EmptyCommandArgumentContext
)
) )
} }
@ -59,76 +63,121 @@ public object BuiltInCommandCallResolver : CommandCallResolver {
val acceptance: ArgumentAcceptance, val acceptance: ArgumentAcceptance,
) )
private class ErrorSink {
private val unmatchedCommandSignatures = mutableListOf<UnmatchedCommandSignature>()
private val resolutionAmbiguities = mutableListOf<CommandSignature>()
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<CommandValueArgument>,
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( private fun resolveImpl(
caller: CommandSender, caller: CommandSender,
callee: Command, callee: Command,
valueArguments: List<CommandValueArgument>, valueArguments: List<CommandValueArgument>,
context: CommandArgumentContext?, context: CommandArgumentContext?,
errorSink: ErrorSink,
): ResolveData? { ): ResolveData? {
callee.overloads callee.overloads
.mapNotNull l@{ signature -> .mapNotNull l@{ signature ->
if (signature.receiverParameter?.type?.classifierAsKClass()?.isInstance(caller) == false) { signature.toResolveData(caller, valueArguments, context, errorSink)
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
)
}
} }
.also { result -> result.singleOrNull()?.let { return it } } .also { result -> result.takeSingleResolveData()?.let { return it } }
.takeLongestMatches() .takeLongestMatches()
.ifEmpty { return null } .ifEmpty { return null }
.also { result -> result.singleOrNull()?.let { return it } } .also { result -> result.takeSingleResolveData()?.let { return it } }
// take single ArgumentAcceptance.Direct // take single ArgumentAcceptance.Direct
.also { list -> .also { list ->
val candidates = list val candidates = list
.asSequence().filterIsInstance<ResolveData>()
.flatMap { phase -> .flatMap { phase ->
phase.argumentAcceptances.filter { it.acceptance is ArgumentAcceptance.Direct }.map { phase to it } phase.argumentAcceptances.filter { it.acceptance is ArgumentAcceptance.Direct }.map { phase to it }
} }.toList()
candidates.singleOrNull()?.let { return it.first } // single Direct candidates.singleOrNull()?.let { return it.first } // single Direct
if (candidates.distinctBy { it.second.index }.size != candidates.size) { if (candidates.distinctBy { it.second.index }.size != candidates.size) {
// Resolution ambiguity // Resolution ambiguity
/* /*
@ -142,13 +191,17 @@ public object BuiltInCommandCallResolver : CommandCallResolver {
fun foo(a: AA, c: C) = 1 fun foo(a: AA, c: C) = 1
*/ */
// The call is foo(AA(), C()) or foo(A(), CC()) // The call is foo(AA(), C()) or foo(A(), CC())
return null
candidates.forEach { candidate -> errorSink.reportAmbiguity(candidate.first.signature) }
} }
} }
return null return null
} }
private fun Collection<Any>.takeSingleResolveData() = asSequence().filterIsInstance<ResolveData>().singleOrNull()
/* /*

View File

@ -40,9 +40,9 @@ public class CommandResolveResult private constructor(
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE) callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE) callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
} }
call?.let(onSuccess) call?.let(onSuccess)?.let { return it }
failure?.let(onFailure) failure?.let(onFailure)?.let { return it }
null!! throw kotlin.AssertionError()
} }
public constructor(call: ResolvedCommandCall?) : this(call as Any?) public constructor(call: ResolvedCommandCall?) : this(call as Any?)

View File

@ -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.Command.Companion.allNames
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.findDuplicate import net.mamoe.mirai.console.command.CommandManager.INSTANCE.findDuplicate
import net.mamoe.mirai.console.command.CommandSender.Companion.toCommandSender 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.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.parse.CommandCallParser.Companion.parseCommandCall import net.mamoe.mirai.console.command.parse.CommandCallParser.Companion.parseCommandCall
import net.mamoe.mirai.console.command.resolve.CommandCallResolver.Companion.resolve 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) sender.catchExecutionException(result.exception)
intercept() intercept()
} }
is CommandExecuteResult.UnresolvedCall -> { is CommandExecuteResult.UnmatchedSignature,
is CommandExecuteResult.UnresolvedCommand,
-> {
// noop // noop
} }
} }
@ -175,20 +178,25 @@ internal suspend fun executeCommandImpl(
caller: CommandSender, caller: CommandSender,
checkPermission: Boolean, checkPermission: Boolean,
): CommandExecuteResult { ): CommandExecuteResult {
val call = message.asMessageChain().parseCommandCall(caller) ?: return CommandExecuteResult.UnresolvedCall("") val call = message.asMessageChain().parseCommandCall(caller) ?: return CommandExecuteResult.UnresolvedCommand("")
val resolved = call.resolve() ?: return CommandExecuteResult.UnresolvedCall(call.calleeName) val resolved = call.resolve().fold(
onSuccess = { it },
onFailure = { return it }
)
val command = resolved.callee val command = resolved.callee
if (checkPermission && !command.permission.testPermission(caller)) { if (checkPermission && !command.permission.testPermission(caller)) {
return CommandExecuteResult.PermissionDenied(command, call.calleeName) return CommandExecuteResult.PermissionDenied(command, call, resolved, call.calleeName)
} }
return try { return try {
resolved.calleeSignature.call(resolved) 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) { } catch (e: Throwable) {
CommandExecuteResult.ExecutionFailed(e, resolved.callee, call.calleeName, EmptyMessageChain) CommandExecuteResult.ExecutionFailed(e, resolved.callee, call, resolved, call.calleeName, EmptyMessageChain)
} }
} }

View File

@ -12,11 +12,14 @@ package net.mamoe.mirai.console.terminal
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.command.* 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.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.terminal.noconsole.NoConsole import net.mamoe.mirai.console.terminal.noconsole.NoConsole
import net.mamoe.mirai.console.util.ConsoleInternalApi import net.mamoe.mirai.console.util.ConsoleInternalApi
import net.mamoe.mirai.console.util.requestInput import net.mamoe.mirai.console.util.requestInput
import net.mamoe.mirai.utils.DefaultLogger import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.warning
import org.jline.reader.EndOfFileException import org.jline.reader.EndOfFileException
import org.jline.reader.UserInterruptException import org.jline.reader.UserInterruptException
@ -55,21 +58,28 @@ internal fun startupConsoleThread() {
continue continue
} }
// consoleLogger.debug("INPUT> $next") // consoleLogger.debug("INPUT> $next")
val result = ConsoleCommandSender.executeCommand(next) when (val result = ConsoleCommandSender.executeCommand(next)) {
when (result.status) { is Success -> {
CommandExecuteStatus.SUCCESSFUL -> {
} }
CommandExecuteStatus.ILLEGAL_ARGUMENT -> { is IllegalArgument -> {
result.exception?.message?.let { consoleLogger.warning(it) } result.exception.message?.let { consoleLogger.warning(it) } ?: kotlin.run {
consoleLogger.warning(result.exception)
}
} }
CommandExecuteStatus.EXECUTION_EXCEPTION -> { is ExecutionFailed -> {
result.exception?.let(consoleLogger::error) consoleLogger.error(result.exception)
} }
CommandExecuteStatus.COMMAND_NOT_FOUND -> { is UnresolvedCommand -> {
consoleLogger.warning("未知指令: ${result.commandName}, 输入 ? 获取帮助") consoleLogger.warning { "未知指令: ${result.commandName}, 输入 ? 获取帮助" }
} }
CommandExecuteStatus.PERMISSION_DENIED -> { is PermissionDenied -> {
consoleLogger.warning("Permission denied.") consoleLogger.warning { "权限不足." }
}
is UnmatchedSignature -> {
consoleLogger.warning { "参数不匹配: " + result.failureReasons.joinToString("\n") { it.render() } }
}
is Failure -> {
consoleLogger.warning { result.toString() }
} }
} }
} catch (e: InterruptedException) { } 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.
"参数长度不匹配"
}
}
}