mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
Review command:
Add `CommandReflector.validate` to check declaration clashes; Rename CommandSignatureVariant to CommandSignature; Add docs; Cleanup code;
This commit is contained in:
parent
d1ebe44f3e
commit
291035f978
@ -12,7 +12,7 @@
|
||||
package net.mamoe.mirai.console.command
|
||||
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandArgumentContextAware
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandSignatureVariant
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandSignature
|
||||
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
|
||||
import net.mamoe.mirai.console.compiler.common.ResolveContext
|
||||
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
|
||||
@ -52,7 +52,7 @@ public interface Command {
|
||||
*/
|
||||
@ConsoleExperimentalApi("Property name is experimental")
|
||||
@ExperimentalCommandDescriptors
|
||||
public val overloads: List<CommandSignatureVariant>
|
||||
public val overloads: List<CommandSignature>
|
||||
|
||||
/**
|
||||
* 用法说明, 用于发送给用户. [usage] 一般包含 [description].
|
||||
|
@ -95,8 +95,10 @@ public abstract class CompositeCommand(
|
||||
private val reflector by lazy { CommandReflector(this, CompositeCommandSubCommandAnnotationResolver) }
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public final override val overloads: List<CommandSignatureVariantFromKFunction> by lazy {
|
||||
reflector.findSubCommands()
|
||||
public final override val overloads: List<CommandSignatureFromKFunction> by lazy {
|
||||
reflector.findSubCommands().also {
|
||||
reflector.validate(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,8 +56,8 @@ public abstract class RawCommand(
|
||||
public override val permission: Permission by lazy { createOrFindCommandPermission(parentPermission) }
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
override val overloads: List<CommandSignatureVariant> = listOf(
|
||||
CommandSignatureVariantImpl(
|
||||
override val overloads: List<CommandSignature> = listOf(
|
||||
CommandSignatureImpl(
|
||||
receiverParameter = CommandReceiverParameter(false, typeOf0<CommandSender>()),
|
||||
valueParameters = listOf(AbstractCommandValueParameter.UserDefinedType.createRequired<Array<out Message>>("args", true))
|
||||
) { call ->
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package net.mamoe.mirai.console.command
|
||||
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
|
||||
import net.mamoe.mirai.console.command.descriptor.*
|
||||
import net.mamoe.mirai.console.command.java.JSimpleCommand
|
||||
import net.mamoe.mirai.console.compiler.common.ResolveContext
|
||||
@ -67,8 +66,9 @@ public abstract class SimpleCommand(
|
||||
private val reflector by lazy { CommandReflector(this, SimpleCommandSubCommandAnnotationResolver) }
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public final override val overloads: List<CommandSignatureVariantFromKFunction> by lazy {
|
||||
public final override val overloads: List<CommandSignatureFromKFunction> by lazy {
|
||||
reflector.findSubCommands().also {
|
||||
reflector.validate(it)
|
||||
if (it.isEmpty())
|
||||
throw IllegalCommandDeclarationException(this, "SimpleCommand must have at least one subcommand, whereas zero present.")
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package net.mamoe.mirai.console.command.descriptor
|
||||
|
||||
import net.mamoe.mirai.console.command.IllegalCommandArgumentException
|
||||
import net.mamoe.mirai.console.command.descriptor.AbstractCommandValueArgumentParser.Companion.illegalArgument
|
||||
|
||||
/**
|
||||
* 在解析参数时遇到的 _正常_ 错误. 如参数不符合规范等.
|
||||
*
|
||||
* [message] 将会发送给指令调用方.
|
||||
*
|
||||
* @see IllegalCommandArgumentException
|
||||
* @see CommandValueArgumentParser
|
||||
* @see AbstractCommandValueArgumentParser.illegalArgument
|
||||
*/
|
||||
public class CommandArgumentParserException : IllegalCommandArgumentException {
|
||||
public constructor() : super()
|
||||
public constructor(message: String?) : super(message)
|
||||
public constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||
public constructor(cause: Throwable?) : super(cause)
|
||||
}
|
@ -26,26 +26,46 @@ import kotlin.reflect.full.isSubtypeOf
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
/**
|
||||
* @see CommandSignatureVariantImpl
|
||||
* 指令签名. 表示指令定义的需要的参数.
|
||||
*
|
||||
* @see AbstractCommandSignature
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface CommandSignatureVariant {
|
||||
public interface CommandSignature {
|
||||
/**
|
||||
* 接收者参数, 为 [CommandSender] 子类
|
||||
*/
|
||||
@ConsoleExperimentalApi
|
||||
public val receiverParameter: CommandReceiverParameter<out CommandSender>?
|
||||
|
||||
/**
|
||||
* 形式 值参数.
|
||||
*/
|
||||
public val valueParameters: List<AbstractCommandValueParameter<*>>
|
||||
|
||||
/**
|
||||
* 调用这个指令.
|
||||
*/
|
||||
public suspend fun call(resolvedCommandCall: ResolvedCommandCall)
|
||||
}
|
||||
|
||||
/**
|
||||
* 来自 [KFunction] 反射得到的 [CommandSignature]
|
||||
*
|
||||
* @see CommandSignatureFromKFunctionImpl
|
||||
*/
|
||||
@ConsoleExperimentalApi
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface CommandSignatureVariantFromKFunction : CommandSignatureVariant {
|
||||
public interface CommandSignatureFromKFunction : CommandSignature {
|
||||
public val originFunction: KFunction<*>
|
||||
}
|
||||
|
||||
/**
|
||||
* @see CommandSignatureImpl
|
||||
* @see CommandSignatureFromKFunctionImpl
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public abstract class AbstractCommandSignatureVariant : CommandSignatureVariant {
|
||||
public abstract class AbstractCommandSignature : CommandSignature {
|
||||
override fun toString(): String {
|
||||
val receiverParameter = receiverParameter
|
||||
return if (receiverParameter == null) {
|
||||
@ -57,11 +77,11 @@ public abstract class AbstractCommandSignatureVariant : CommandSignatureVariant
|
||||
}
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public open class CommandSignatureVariantImpl(
|
||||
public open class CommandSignatureImpl(
|
||||
override val receiverParameter: CommandReceiverParameter<out CommandSender>?,
|
||||
override val valueParameters: List<AbstractCommandValueParameter<*>>,
|
||||
private val onCall: suspend CommandSignatureVariantImpl.(resolvedCommandCall: ResolvedCommandCall) -> Unit,
|
||||
) : CommandSignatureVariant, AbstractCommandSignatureVariant() {
|
||||
private val onCall: suspend CommandSignatureImpl.(resolvedCommandCall: ResolvedCommandCall) -> Unit,
|
||||
) : CommandSignature, AbstractCommandSignature() {
|
||||
override suspend fun call(resolvedCommandCall: ResolvedCommandCall) {
|
||||
return onCall(resolvedCommandCall)
|
||||
}
|
||||
@ -69,12 +89,12 @@ public open class CommandSignatureVariantImpl(
|
||||
|
||||
@ConsoleExperimentalApi
|
||||
@ExperimentalCommandDescriptors
|
||||
public open class CommandSignatureVariantFromKFunctionImpl(
|
||||
public open class CommandSignatureFromKFunctionImpl(
|
||||
override val receiverParameter: CommandReceiverParameter<out CommandSender>?,
|
||||
override val valueParameters: List<AbstractCommandValueParameter<*>>,
|
||||
override val originFunction: KFunction<*>,
|
||||
private val onCall: suspend CommandSignatureVariantFromKFunctionImpl.(resolvedCommandCall: ResolvedCommandCall) -> Unit,
|
||||
) : CommandSignatureVariantFromKFunction, AbstractCommandSignatureVariant() {
|
||||
private val onCall: suspend CommandSignatureFromKFunctionImpl.(resolvedCommandCall: ResolvedCommandCall) -> Unit,
|
||||
) : CommandSignatureFromKFunction, AbstractCommandSignature() {
|
||||
override suspend fun call(resolvedCommandCall: ResolvedCommandCall) {
|
||||
return onCall(resolvedCommandCall)
|
||||
}
|
||||
|
@ -11,7 +11,9 @@
|
||||
|
||||
package net.mamoe.mirai.console.command.descriptor
|
||||
|
||||
import net.mamoe.mirai.console.command.parse.CommandCall
|
||||
import net.mamoe.mirai.console.command.Command
|
||||
import net.mamoe.mirai.console.command.IllegalCommandArgumentException
|
||||
import net.mamoe.mirai.console.command.descriptor.AbstractCommandValueArgumentParser.Companion.illegalArgument
|
||||
import net.mamoe.mirai.console.command.parse.CommandValueArgument
|
||||
import net.mamoe.mirai.console.internal.data.classifierAsKClassOrNull
|
||||
import net.mamoe.mirai.console.internal.data.qualifiedNameOrTip
|
||||
@ -28,9 +30,10 @@ public open class NoValueArgumentMappingException(
|
||||
) : CommandResolutionException("Cannot find a CommandArgument mapping for ${forType.qualifiedName}")
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public open class UnresolvedCommandCallException(
|
||||
public val call: CommandCall,
|
||||
) : CommandResolutionException("Unresolved call: $call")
|
||||
public open class CommandDeclarationClashException(
|
||||
public val command: Command,
|
||||
public val signatures: List<CommandSignature>,
|
||||
) : CommandResolutionException("Command declaration clash: \n${signatures.joinToString("\n")}")
|
||||
|
||||
public open class CommandResolutionException : RuntimeException {
|
||||
public constructor() : super()
|
||||
@ -38,3 +41,19 @@ public open class CommandResolutionException : RuntimeException {
|
||||
public constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||
public constructor(cause: Throwable?) : super(cause)
|
||||
}
|
||||
|
||||
/**
|
||||
* 在解析参数时遇到的 _正常_ 错误. 如参数不符合规范等.
|
||||
*
|
||||
* [message] 将会发送给指令调用方.
|
||||
*
|
||||
* @see IllegalCommandArgumentException
|
||||
* @see CommandValueArgumentParser
|
||||
* @see AbstractCommandValueArgumentParser.illegalArgument
|
||||
*/
|
||||
public class CommandArgumentParserException : IllegalCommandArgumentException {
|
||||
public constructor() : super()
|
||||
public constructor(message: String?) : super(message)
|
||||
public constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||
public constructor(cause: Throwable?) : super(cause)
|
||||
}
|
@ -31,13 +31,13 @@ public object BuiltInCommandCallResolver : CommandCallResolver {
|
||||
|
||||
return ResolvedCommandCallImpl(call.caller,
|
||||
callee,
|
||||
signature.variant,
|
||||
signature.signature,
|
||||
signature.zippedArguments.map { it.second },
|
||||
context ?: EmptyCommandArgumentContext)
|
||||
}
|
||||
|
||||
private data class ResolveData(
|
||||
val variant: CommandSignatureVariant,
|
||||
val signature: CommandSignature,
|
||||
val zippedArguments: List<Pair<AbstractCommandValueParameter<*>, CommandValueArgument>>,
|
||||
val argumentAcceptances: List<ArgumentAcceptanceWithIndex>,
|
||||
val remainingParameters: List<AbstractCommandValueParameter<*>>,
|
||||
@ -69,7 +69,7 @@ public object BuiltInCommandCallResolver : CommandCallResolver {
|
||||
|
||||
if (zipped.isEmpty()) {
|
||||
ResolveData(
|
||||
variant = signature,
|
||||
signature = signature,
|
||||
zippedArguments = emptyList(),
|
||||
argumentAcceptances = emptyList(),
|
||||
remainingParameters = remainingParameters,
|
||||
@ -91,7 +91,7 @@ public object BuiltInCommandCallResolver : CommandCallResolver {
|
||||
}
|
||||
|
||||
ResolveData(
|
||||
variant = signature,
|
||||
signature = signature,
|
||||
zippedArguments = zipped,
|
||||
argumentAcceptances = zipped.mapIndexed { index, (parameter, argument) ->
|
||||
val accepting = parameter.accepting(argument, context)
|
||||
@ -163,7 +163,7 @@ fun main() {
|
||||
private fun List<ResolveData>.takeLongestMatches(): Collection<ResolveData> {
|
||||
if (isEmpty()) return emptyList()
|
||||
return associateWith {
|
||||
it.variant.valueParameters.size - it.remainingOptionalCount * 1.001 // slightly lower priority with optional defaults.
|
||||
it.signature.valueParameters.size - it.remainingOptionalCount * 1.001 // slightly lower priority with optional defaults.
|
||||
}.let { m ->
|
||||
val maxMatch = m.values.maxByOrNull { it }
|
||||
m.filter { it.value == maxMatch }.keys
|
||||
|
@ -37,9 +37,9 @@ public interface ResolvedCommandCall {
|
||||
public val callee: Command
|
||||
|
||||
/**
|
||||
* The callee [CommandSignatureVariant], specifically a sub command from [CompositeCommand]
|
||||
* The callee [CommandSignature], specifically a sub command from [CompositeCommand]
|
||||
*/
|
||||
public val calleeSignature: CommandSignatureVariant
|
||||
public val calleeSignature: CommandSignature
|
||||
|
||||
/**
|
||||
* Original arguments
|
||||
@ -47,7 +47,7 @@ public interface ResolvedCommandCall {
|
||||
public val rawValueArguments: List<CommandValueArgument>
|
||||
|
||||
/**
|
||||
* Resolved value arguments arranged mapping the [CommandSignatureVariant.valueParameters] by index.
|
||||
* Resolved value arguments arranged mapping the [CommandSignature.valueParameters] by index.
|
||||
*
|
||||
* **Implementation details**: Lazy calculation.
|
||||
*/
|
||||
@ -73,7 +73,7 @@ public suspend inline fun ResolvedCommandCall.call() {
|
||||
public class ResolvedCommandCallImpl(
|
||||
override val caller: CommandSender,
|
||||
override val callee: Command,
|
||||
override val calleeSignature: CommandSignatureVariant,
|
||||
override val calleeSignature: CommandSignature,
|
||||
override val rawValueArguments: List<CommandValueArgument>,
|
||||
private val context: CommandArgumentContext,
|
||||
) : ResolvedCommandCall {
|
||||
|
@ -11,6 +11,7 @@ import net.mamoe.mirai.message.data.SingleMessage
|
||||
import net.mamoe.mirai.message.data.buildMessageChain
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.KVisibility
|
||||
import kotlin.reflect.full.*
|
||||
|
||||
@ -133,7 +134,7 @@ internal class CommandReflector(
|
||||
// if (isAbstract) illegalDeclaration("Command function cannot be abstract")
|
||||
}
|
||||
|
||||
fun generateUsage(overloads: Iterable<CommandSignatureVariantFromKFunction>): String {
|
||||
fun generateUsage(overloads: Iterable<CommandSignatureFromKFunction>): String {
|
||||
return overloads.joinToString("\n") { subcommand ->
|
||||
buildString {
|
||||
if (command.prefixOptional) {
|
||||
@ -173,17 +174,45 @@ internal class CommandReflector(
|
||||
}
|
||||
}
|
||||
|
||||
fun validate(variants: List<CommandSignatureVariantFromKFunctionImpl>) {
|
||||
fun validate(signatures: List<CommandSignatureFromKFunctionImpl>) {
|
||||
|
||||
data class ErasedParameters(
|
||||
val name: String,
|
||||
val x: String,
|
||||
data class ErasedParameterInfo(
|
||||
val index: Int,
|
||||
val name: String?,
|
||||
val type: KType, // ignore nullability
|
||||
val additional: String?,
|
||||
)
|
||||
variants
|
||||
|
||||
data class ErasedVariantInfo(
|
||||
val receiver: ErasedParameterInfo?,
|
||||
val valueParameters: List<ErasedParameterInfo>,
|
||||
)
|
||||
|
||||
fun CommandParameter<*>.toErasedParameterInfo(index: Int): ErasedParameterInfo {
|
||||
return ErasedParameterInfo(index,
|
||||
this.name,
|
||||
this.type.withNullability(false),
|
||||
if (this is AbstractCommandValueParameter.StringConstant) this.expectingValue else null)
|
||||
}
|
||||
|
||||
val candidates = signatures.map { variant ->
|
||||
variant to ErasedVariantInfo(
|
||||
variant.receiverParameter?.toErasedParameterInfo(0),
|
||||
variant.valueParameters.mapIndexed { index, parameter -> parameter.toErasedParameterInfo(index) }
|
||||
)
|
||||
}
|
||||
|
||||
val groups = candidates.groupBy { it.second }
|
||||
|
||||
val clashes = groups.entries.find { (_, value) ->
|
||||
value.size > 1
|
||||
} ?: return
|
||||
|
||||
throw CommandDeclarationClashException(command, clashes.value.map { it.first })
|
||||
}
|
||||
|
||||
@Throws(IllegalCommandDeclarationException::class)
|
||||
fun findSubCommands(): List<CommandSignatureVariantFromKFunctionImpl> {
|
||||
fun findSubCommands(): List<CommandSignatureFromKFunctionImpl> {
|
||||
return command::class.functions // exclude static later
|
||||
.asSequence()
|
||||
.filter { it.isSubCommandFunction() }
|
||||
@ -198,7 +227,7 @@ internal class CommandReflector(
|
||||
val functionValueParameters =
|
||||
function.valueParameters.associateBy { it.toUserDefinedCommandParameter() }
|
||||
|
||||
CommandSignatureVariantFromKFunctionImpl(
|
||||
CommandSignatureFromKFunctionImpl(
|
||||
receiverParameter = function.extensionReceiverParameter?.toCommandReceiverParameter(),
|
||||
valueParameters = functionNameAsValueParameter + functionValueParameters.keys,
|
||||
originFunction = function
|
||||
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("unused", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
|
||||
package net.mamoe.mirai.console.internal.command
|
||||
|
||||
import net.mamoe.mirai.console.command.CompositeCommand
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.KType
|
||||
|
||||
/*
|
||||
internal fun Parameter.toCommandParam(): CommandParameter<*> {
|
||||
val name = getAnnotation(CompositeCommand.Name::class.java)
|
||||
return CommandParameter(
|
||||
name?.value ?: this.name
|
||||
?: throw IllegalArgumentException("Cannot construct CommandParam from a unnamed param"),
|
||||
this.type.kotlin,
|
||||
null
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* 指令形式参数.
|
||||
*/
|
||||
internal data class CommandParameter<T : Any>(
|
||||
/**
|
||||
* 参数名. 不允许重复.
|
||||
*/
|
||||
val name: String,
|
||||
/**
|
||||
* 参数类型. 将从 [CompositeCommand.context] 中寻找 [CommandValueArgumentParser] 解析.
|
||||
*/
|
||||
val type: KType, // exact type
|
||||
val parameter: KParameter, // source parameter
|
||||
) {
|
||||
constructor(name: String, type: KType, parameter: KParameter, parser: CommandValueArgumentParser<T>) : this(
|
||||
name, type, parameter
|
||||
) {
|
||||
this._overrideParser = parser
|
||||
}
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@JvmField
|
||||
internal var _overrideParser: CommandValueArgumentParser<T>? = null
|
||||
|
||||
|
||||
/**
|
||||
* 覆盖的 [CommandValueArgumentParser].
|
||||
*
|
||||
* 如果非 `null`, 将不会从 [CommandArgumentContext] 寻找 [CommandValueArgumentParser]
|
||||
*/
|
||||
val overrideParser: CommandValueArgumentParser<T>? get() = _overrideParser
|
||||
}
|
||||
|
@ -18,13 +18,6 @@ import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
internal infix fun Array<String>.matchesBeginning(list: List<Any>): Boolean {
|
||||
this.forEachIndexed { index, any ->
|
||||
if (list[index] != any) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
internal infix fun Array<out String>.intersectsIgnoringCase(other: Array<out String>): Boolean {
|
||||
val max = this.size.coerceAtMost(other.size)
|
||||
for (i in 0 until max) {
|
||||
|
Loading…
Reference in New Issue
Block a user