mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
Call parse with errors
This commit is contained in:
parent
8da8615721
commit
64790d0114
@ -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<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,
|
||||
|
||||
/** 没有匹配的指令 */
|
||||
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<CommandSignature>,
|
||||
) : 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
|
||||
|
@ -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<TypeVariant<*>>,
|
||||
) : ArgumentAcceptance(0)
|
||||
|
||||
@ -101,7 +101,7 @@ public sealed class ArgumentAcceptance(
|
||||
}
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public class CommandReceiverParameter<T : CommandSender>(
|
||||
public data class CommandReceiverParameter<T : CommandSender>(
|
||||
override val isOptional: Boolean,
|
||||
override val type: KType,
|
||||
) : CommandParameter<T>, AbstractCommandParameter<T>() {
|
||||
|
@ -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<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,
|
||||
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<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(
|
||||
caller: CommandSender,
|
||||
callee: Command,
|
||||
valueArguments: List<CommandValueArgument>,
|
||||
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<ResolveData>()
|
||||
.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<Any>.takeSingleResolveData() = asSequence().filterIsInstance<ResolveData>().singleOrNull()
|
||||
|
||||
/*
|
||||
|
||||
|
||||
|
@ -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?)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
"参数长度不匹配"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user