From d1ebe44f3efbc5e96e55676d957407a28ed4e55b Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 25 Oct 2020 14:27:27 +0800 Subject: [PATCH] Review CommandArgumentContext and command --- .../mirai/console/command/BuiltInCommands.kt | 8 +- .../mamoe/mirai/console/command/Command.kt | 3 - .../descriptor/CommandArgumentContext.kt | 38 ++++-- .../CommandArgumentParserBuiltins.kt | 68 +++++----- .../CommandArgumentParserException.kt | 3 +- .../descriptor/CommandValueArgumentParser.kt | 125 +++++++++--------- .../mirai/console/command/java/JCommand.kt | 25 ---- .../console/command/java/JCompositeCommand.kt | 2 - .../mirai/console/command/java/JRawCommand.kt | 2 - .../console/command/java/JSimpleCommand.kt | 2 - .../resolve/BuiltInCommandCallResolver.kt | 2 +- .../command/resolve/ResolvedCommandCall.kt | 1 + 12 files changed, 137 insertions(+), 142 deletions(-) delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCommand.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt index f37299e00..3bef2710d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt @@ -15,7 +15,11 @@ import kotlinx.coroutines.sync.withLock import net.mamoe.mirai.alsoLogin import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register -import net.mamoe.mirai.console.command.descriptor.* +import net.mamoe.mirai.console.command.descriptor.CommandArgumentParserException +import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser.Companion.map +import net.mamoe.mirai.console.command.descriptor.PermissionIdValueArgumentParser +import net.mamoe.mirai.console.command.descriptor.PermitteeIdValueArgumentParser +import net.mamoe.mirai.console.command.descriptor.buildCommandArgumentContext import net.mamoe.mirai.console.internal.command.CommandManagerImpl import net.mamoe.mirai.console.internal.command.CommandManagerImpl.allRegisteredCommands import net.mamoe.mirai.console.internal.util.runIgnoreException @@ -154,7 +158,7 @@ public object BuiltInCommands { Permission::class with PermissionIdValueArgumentParser.map { id -> kotlin.runCatching { id.findCorrespondingPermissionOrFail() - }.getOrElse { illegalArgument("指令不存在: $id", it) } + }.getOrElse { throw CommandArgumentParserException("指令不存在: $id", it) } } }, ), BuiltInCommandInternal { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt index 9d0036771..578cb9630 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt @@ -14,7 +14,6 @@ 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.ExperimentalCommandDescriptors -import net.mamoe.mirai.console.command.java.JCommand import net.mamoe.mirai.console.compiler.common.ResolveContext import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME import net.mamoe.mirai.console.permission.Permission @@ -31,8 +30,6 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi * @see SimpleCommand 简单的, 支持参数自动解析的指令 * * @see CommandArgumentContextAware - * - * @see JCommand 为 Java 用户添加协程帮助的 [Command] */ public interface Command { /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentContext.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentContext.kt index acc3d3d25..c662a2997 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentContext.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentContext.kt @@ -23,6 +23,8 @@ import net.mamoe.mirai.contact.* import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.MessageContent import net.mamoe.mirai.message.data.PlainText +import kotlin.contracts.InvocationKind.EXACTLY_ONCE +import kotlin.contracts.contract import kotlin.internal.LowPriorityInOverloadResolution import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf @@ -49,9 +51,17 @@ public interface CommandArgumentContext { public data class ParserPair( val klass: KClass, val parser: CommandValueArgumentParser, - ) + ) { + public companion object { + @JvmStatic + public fun ParserPair.toPair(): Pair, CommandValueArgumentParser> = klass to parser + } + } - public operator fun get(klass: KClass): CommandValueArgumentParser? + /** + * 获取一个 [kClass] 类型的解析器. + */ + public operator fun get(kClass: KClass): CommandValueArgumentParser? public fun toList(): List> @@ -59,7 +69,7 @@ public interface CommandArgumentContext { /** * For Java callers. * - * @see [EmptyCommandArgumentContext] + * @see EmptyCommandArgumentContext */ @JvmStatic public val EMPTY: CommandArgumentContext = EmptyCommandArgumentContext @@ -108,6 +118,9 @@ public interface CommandArgumentContextAware { public val context: CommandArgumentContext } +/** + * @see CommandArgumentContext.EMPTY + */ public object EmptyCommandArgumentContext : CommandArgumentContext by SimpleCommandArgumentContext(listOf()) /** @@ -117,8 +130,8 @@ public operator fun CommandArgumentContext.plus(replacer: CommandArgumentContext if (replacer == EmptyCommandArgumentContext) return this if (this == EmptyCommandArgumentContext) return replacer return object : CommandArgumentContext { - override fun get(klass: KClass): CommandValueArgumentParser? = - replacer[klass] ?: this@plus[klass] + override fun get(kClass: KClass): CommandValueArgumentParser? = + replacer[kClass] ?: this@plus[kClass] override fun toList(): List> = replacer.toList() + this@plus.toList() } @@ -132,9 +145,9 @@ public operator fun CommandArgumentContext.plus(replacer: List>): if (this == EmptyCommandArgumentContext) return SimpleCommandArgumentContext(replacer) return object : CommandArgumentContext { @Suppress("UNCHECKED_CAST") - override fun get(klass: KClass): CommandValueArgumentParser? = - replacer.firstOrNull { klass.isSubclassOf(it.klass) }?.parser as CommandValueArgumentParser? - ?: this@plus[klass] + override fun get(kClass: KClass): CommandValueArgumentParser? = + replacer.firstOrNull { kClass.isSubclassOf(it.klass) }?.parser as CommandValueArgumentParser? + ?: this@plus[kClass] override fun toList(): List> = replacer.toList() + this@plus.toList() } @@ -149,9 +162,9 @@ public operator fun CommandArgumentContext.plus(replacer: List>): public class SimpleCommandArgumentContext( public val list: List>, ) : CommandArgumentContext { - override fun get(klass: KClass): CommandValueArgumentParser? = - (this.list.firstOrNull { klass == it.klass }?.parser - ?: this.list.firstOrNull { klass.isSubclassOf(it.klass) }?.parser) as CommandValueArgumentParser? + override fun get(kClass: KClass): CommandValueArgumentParser? = + (this.list.firstOrNull { kClass == it.klass }?.parser + ?: this.list.firstOrNull { kClass.isSubclassOf(it.klass) }?.parser) as CommandValueArgumentParser? override fun toList(): List> = list } @@ -192,6 +205,9 @@ public class SimpleCommandArgumentContext( */ @JvmSynthetic public fun buildCommandArgumentContext(block: CommandArgumentContextBuilder.() -> Unit): CommandArgumentContext { + contract { + callsInPlace(block, EXACTLY_ONCE) + } return CommandArgumentContextBuilder().apply(block).build() } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentParserBuiltins.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentParserBuiltins.kt index a1fc279e3..20d0665b6 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentParserBuiltins.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentParserBuiltins.kt @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("EXPOSED_SUPER_CLASS") + package net.mamoe.mirai.console.command.descriptor import net.mamoe.mirai.Bot @@ -25,7 +27,7 @@ import net.mamoe.mirai.message.data.* /** * 使用 [String.toInt] 解析 */ -public object IntValueArgumentParser : InternalCommandValueArgumentParserExtensions { +public object IntValueArgumentParser : InternalCommandValueArgumentParserExtensions() { public override fun parse(raw: String, sender: CommandSender): Int = raw.toIntOrNull() ?: illegalArgument("无法解析 $raw 为整数") } @@ -33,7 +35,7 @@ public object IntValueArgumentParser : InternalCommandValueArgumentParserExtensi /** * 使用 [String.toLong] 解析 */ -public object LongValueArgumentParser : InternalCommandValueArgumentParserExtensions { +public object LongValueArgumentParser : InternalCommandValueArgumentParserExtensions() { public override fun parse(raw: String, sender: CommandSender): Long = raw.toLongOrNull() ?: illegalArgument("无法解析 $raw 为长整数") } @@ -41,7 +43,7 @@ public object LongValueArgumentParser : InternalCommandValueArgumentParserExtens /** * 使用 [String.toShort] 解析 */ -public object ShortValueArgumentParser : InternalCommandValueArgumentParserExtensions { +public object ShortValueArgumentParser : InternalCommandValueArgumentParserExtensions() { public override fun parse(raw: String, sender: CommandSender): Short = raw.toShortOrNull() ?: illegalArgument("无法解析 $raw 为短整数") } @@ -49,7 +51,7 @@ public object ShortValueArgumentParser : InternalCommandValueArgumentParserExten /** * 使用 [String.toByte] 解析 */ -public object ByteValueArgumentParser : InternalCommandValueArgumentParserExtensions { +public object ByteValueArgumentParser : InternalCommandValueArgumentParserExtensions() { public override fun parse(raw: String, sender: CommandSender): Byte = raw.toByteOrNull() ?: illegalArgument("无法解析 $raw 为字节") } @@ -57,7 +59,7 @@ public object ByteValueArgumentParser : InternalCommandValueArgumentParserExtens /** * 使用 [String.toDouble] 解析 */ -public object DoubleValueArgumentParser : InternalCommandValueArgumentParserExtensions { +public object DoubleValueArgumentParser : InternalCommandValueArgumentParserExtensions() { public override fun parse(raw: String, sender: CommandSender): Double = raw.toDoubleOrNull() ?: illegalArgument("无法解析 $raw 为小数") } @@ -65,7 +67,7 @@ public object DoubleValueArgumentParser : InternalCommandValueArgumentParserExte /** * 使用 [String.toFloat] 解析 */ -public object FloatValueArgumentParser : InternalCommandValueArgumentParserExtensions { +public object FloatValueArgumentParser : InternalCommandValueArgumentParserExtensions() { public override fun parse(raw: String, sender: CommandSender): Float = raw.toFloatOrNull() ?: illegalArgument("无法解析 $raw 为小数") } @@ -73,14 +75,14 @@ public object FloatValueArgumentParser : InternalCommandValueArgumentParserExten /** * 直接返回 [String], 或取用 [SingleMessage.contentToString] */ -public object StringValueArgumentParser : InternalCommandValueArgumentParserExtensions { +public object StringValueArgumentParser : InternalCommandValueArgumentParserExtensions() { public override fun parse(raw: String, sender: CommandSender): String = raw } /** * 解析 [String] 通过 [Image]. */ -public object ImageValueArgumentParser : InternalCommandValueArgumentParserExtensions { +public object ImageValueArgumentParser : InternalCommandValueArgumentParserExtensions() { public override fun parse(raw: String, sender: CommandSender): Image { return kotlin.runCatching { Image(raw) @@ -95,7 +97,7 @@ public object ImageValueArgumentParser : InternalCommandValueArgumentParserExten } } -public object PlainTextValueArgumentParser : InternalCommandValueArgumentParserExtensions { +public object PlainTextValueArgumentParser : InternalCommandValueArgumentParserExtensions<PlainText>() { public override fun parse(raw: String, sender: CommandSender): PlainText { return PlainText(raw) } @@ -109,7 +111,7 @@ public object PlainTextValueArgumentParser : InternalCommandValueArgumentParserE /** * 当字符串内容为(不区分大小写) "true", "yes", "enabled" */ -public object BooleanValueArgumentParser : InternalCommandValueArgumentParserExtensions<Boolean> { +public object BooleanValueArgumentParser : InternalCommandValueArgumentParserExtensions<Boolean>() { public override fun parse(raw: String, sender: CommandSender): Boolean = raw.trim().let { str -> str.equals("true", ignoreCase = true) || str.equals("yes", ignoreCase = true) @@ -121,7 +123,7 @@ public object BooleanValueArgumentParser : InternalCommandValueArgumentParserExt /** * 根据 [Bot.id] 解析一个登录后的 [Bot] */ -public object ExistingBotValueArgumentParser : InternalCommandValueArgumentParserExtensions<Bot> { +public object ExistingBotValueArgumentParser : InternalCommandValueArgumentParserExtensions<Bot>() { public override fun parse(raw: String, sender: CommandSender): Bot = if (raw == "~") sender.inferBotOrFail() else raw.findBotOrFail() @@ -136,7 +138,7 @@ public object ExistingBotValueArgumentParser : InternalCommandValueArgumentParse /** * 解析任意一个存在的好友. */ -public object ExistingFriendValueArgumentParser : InternalCommandValueArgumentParserExtensions<Friend> { +public object ExistingFriendValueArgumentParser : InternalCommandValueArgumentParserExtensions<Friend>() { private val syntax = """ - `botId.friendId` - `botId.friendNick` (模糊搜索, 寻找最优匹配) @@ -175,7 +177,7 @@ public object ExistingFriendValueArgumentParser : InternalCommandValueArgumentPa /** * 解析任意一个存在的群. */ -public object ExistingGroupValueArgumentParser : InternalCommandValueArgumentParserExtensions<Group> { +public object ExistingGroupValueArgumentParser : InternalCommandValueArgumentParserExtensions<Group>() { private val syntax = """ - `botId.groupId` - `~` (指代指令调用人自己所在群. 仅群聊天环境下) @@ -202,7 +204,7 @@ public object ExistingGroupValueArgumentParser : InternalCommandValueArgumentPar } } -public object ExistingUserValueArgumentParser : InternalCommandValueArgumentParserExtensions<User> { +public object ExistingUserValueArgumentParser : InternalCommandValueArgumentParserExtensions<User>() { private val syntax: String = """ - `botId.groupId.memberId` - `botId.groupId.memberCard` (模糊搜索, 寻找最优匹配) @@ -246,7 +248,7 @@ public object ExistingUserValueArgumentParser : InternalCommandValueArgumentPars } -public object ExistingContactValueArgumentParser : InternalCommandValueArgumentParserExtensions<Contact> { +public object ExistingContactValueArgumentParser : InternalCommandValueArgumentParserExtensions<Contact>() { private val syntax: String = """ - `botId.groupId.memberId` - `botId.groupId.memberCard` (模糊搜索, 寻找最优匹配) @@ -286,7 +288,7 @@ public object ExistingContactValueArgumentParser : InternalCommandValueArgumentP /** * 解析任意一个群成员. */ -public object ExistingMemberValueArgumentParser : InternalCommandValueArgumentParserExtensions<Member> { +public object ExistingMemberValueArgumentParser : InternalCommandValueArgumentParserExtensions<Member>() { private val syntax: String = """ - `botId.groupId.memberId` - `botId.groupId.memberCard` (模糊搜索, 寻找最优匹配) @@ -333,7 +335,7 @@ public object ExistingMemberValueArgumentParser : InternalCommandValueArgumentPa } } -public object PermissionIdValueArgumentParser : CommandValueArgumentParser<PermissionId> { +public object PermissionIdValueArgumentParser : InternalCommandValueArgumentParserExtensions<PermissionId>() { override fun parse(raw: String, sender: CommandSender): PermissionId { return kotlin.runCatching { PermissionId.parseFromString(raw) }.getOrElse { illegalArgument("无法解析 $raw 为 PermissionId") @@ -341,7 +343,7 @@ public object PermissionIdValueArgumentParser : CommandValueArgumentParser<Permi } } -public object PermitteeIdValueArgumentParser : CommandValueArgumentParser<PermitteeId> { +public object PermitteeIdValueArgumentParser : InternalCommandValueArgumentParserExtensions<PermitteeId>() { override fun parse(raw: String, sender: CommandSender): PermitteeId { return if (raw == "~") sender.permitteeId else kotlin.runCatching { AbstractPermitteeId.parseFromString(raw) }.getOrElse { @@ -363,26 +365,26 @@ public object RawContentValueArgumentParser : CommandValueArgumentParser<Message override fun parse(raw: MessageContent, sender: CommandSender): MessageContent = raw } -internal interface InternalCommandValueArgumentParserExtensions<T : Any> : CommandValueArgumentParser<T> { - fun String.parseToLongOrFail(): Long = toLongOrNull() ?: illegalArgument("无法解析 $this 为整数") +internal abstract class InternalCommandValueArgumentParserExtensions<T : Any> : AbstractCommandValueArgumentParser<T>() { + private fun String.parseToLongOrFail(): Long = toLongOrNull() ?: illegalArgument("无法解析 $this 为整数") - fun Long.findBotOrFail(): Bot = Bot.getInstanceOrNull(this) ?: illegalArgument("无法找到 Bot: $this") + protected fun Long.findBotOrFail(): Bot = Bot.getInstanceOrNull(this) ?: illegalArgument("无法找到 Bot: $this") - fun String.findBotOrFail(): Bot = + protected fun String.findBotOrFail(): Bot = Bot.getInstanceOrNull(this.parseToLongOrFail()) ?: illegalArgument("无法找到 Bot: $this") - fun Bot.findGroupOrFail(id: Long): Group = getGroupOrNull(id) ?: illegalArgument("无法找到群: $this") + protected fun Bot.findGroupOrFail(id: Long): Group = getGroupOrNull(id) ?: illegalArgument("无法找到群: $this") - fun Bot.findGroupOrFail(id: String): Group = + protected fun Bot.findGroupOrFail(id: String): Group = getGroupOrNull(id.parseToLongOrFail()) ?: illegalArgument("无法找到群: $this") - fun Bot.findFriendOrFail(id: String): Friend = + protected fun Bot.findFriendOrFail(id: String): Friend = getFriendOrNull(id.parseToLongOrFail()) ?: illegalArgument("无法找到好友: $this") - fun Bot.findMemberOrFail(id: String): Friend = + protected fun Bot.findMemberOrFail(id: String): Friend = getFriendOrNull(id.parseToLongOrFail()) ?: illegalArgument("无法找到群员: $this") - fun Group.findMemberOrFail(idOrCard: String): Member { + protected fun Group.findMemberOrFail(idOrCard: String): Member { if (idOrCard == "\$") return members.randomOrNull() ?: illegalArgument("当前语境下无法推断随机群员") idOrCard.toLongOrNull()?.let { getOrNull(it) }?.let { return it } this.members.singleOrNull { it.nameCardOrNick.contains(idOrCard) }?.let { return it } @@ -405,23 +407,21 @@ internal interface InternalCommandValueArgumentParserExtensions<T : Any> : Comma } } - fun CommandSender.inferBotOrFail(): Bot = + protected fun CommandSender.inferBotOrFail(): Bot = (this as? UserCommandSender)?.bot ?: Bot.botInstancesSequence.singleOrNull() ?: illegalArgument("当前语境下无法推断目标 Bot, 因为目前有多个 Bot 在线.") - fun CommandSender.inferGroupOrFail(): Group = + protected fun CommandSender.inferGroupOrFail(): Group = inferGroup() ?: illegalArgument("当前语境下无法推断目标群") - fun CommandSender.inferGroup(): Group? = (this as? GroupAwareCommandSender)?.group + protected fun CommandSender.inferGroup(): Group? = (this as? GroupAwareCommandSender)?.group - fun CommandSender.inferFriendOrFail(): Friend = + protected fun CommandSender.inferFriendOrFail(): Friend = (this as? FriendCommandSender)?.user ?: illegalArgument("当前语境下无法推断目标好友") } -internal fun Double.toDecimalPlace(n: Int): String { - return "%.${n}f".format(this) -} +internal fun Double.toDecimalPlace(n: Int): String = "%.${n}f".format(this) internal fun String.truncate(lengthLimit: Int, replacement: String = "..."): String = buildString { var lengthSum = 0 diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentParserException.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentParserException.kt index 25a59067d..b0bf45647 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentParserException.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandArgumentParserException.kt @@ -12,6 +12,7 @@ package net.mamoe.mirai.console.command.descriptor import net.mamoe.mirai.console.command.IllegalCommandArgumentException +import net.mamoe.mirai.console.command.descriptor.AbstractCommandValueArgumentParser.Companion.illegalArgument /** * 在解析参数时遇到的 _正常_ 错误. 如参数不符合规范等. @@ -20,7 +21,7 @@ import net.mamoe.mirai.console.command.IllegalCommandArgumentException * * @see IllegalCommandArgumentException * @see CommandValueArgumentParser - * @see CommandValueArgumentParser.illegalArgument + * @see AbstractCommandValueArgumentParser.illegalArgument */ public class CommandArgumentParserException : IllegalCommandArgumentException { public constructor() : super() diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandValueArgumentParser.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandValueArgumentParser.kt index ff82ed6db..75c373519 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandValueArgumentParser.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/descriptor/CommandValueArgumentParser.kt @@ -16,10 +16,9 @@ import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.console.command.CompositeCommand import net.mamoe.mirai.console.command.SimpleCommand +import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser.Companion.parse import net.mamoe.mirai.contact.* -import net.mamoe.mirai.message.data.MessageContent -import net.mamoe.mirai.message.data.SingleMessage -import net.mamoe.mirai.message.data.content +import net.mamoe.mirai.message.data.* import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -78,68 +77,76 @@ public interface CommandValueArgumentParser<out T : Any> { */ @Throws(CommandArgumentParserException::class) public fun parse(raw: MessageContent, sender: CommandSender): T = parse(raw.content, sender) + + public companion object { + /** + * 解析一个字符串或 [SingleMessage] 为 [T] 类型参数 + * + * @throws IllegalArgumentException 当 [raw] 既不是 [SingleMessage], 也不是 [String] 时抛出. + * + * @see CommandValueArgumentParser.parse + */ + @JvmStatic + @Throws(IllegalArgumentException::class) + public fun <T : Any> CommandValueArgumentParser<T>.parse(raw: Message, sender: CommandSender): T { + return when (raw) { + is PlainText -> parse(raw.content, sender) + is MessageContent -> parse(raw, sender) + else -> throw IllegalArgumentException("Illegal raw argument type: ${raw::class.qualifiedName}") + } + } + + /** + * 使用原 [this] 解析, 成功后使用 [mapper] 映射为另一个类型. + */ + @JvmStatic + public fun <Original : Any, Result : Any> CommandValueArgumentParser<Original>.map( + mapper: MappingCommandValueArgumentParser<Original, Result>.(Original) -> Result, + ): CommandValueArgumentParser<Result> = MappingCommandValueArgumentParser(this, mapper) + } } /** - * 使用原 [this] 解析, 成功后使用 [mapper] 映射为另一个类型. + * @see CommandValueArgumentParser 的基础实现. */ -public fun <T : Any, R : Any> CommandValueArgumentParser<T>.map( - mapper: CommandValueArgumentParser<R>.(T) -> R, -): CommandValueArgumentParser<R> = MappingCommandValueArgumentParser(this, mapper) +public abstract class AbstractCommandValueArgumentParser<T : Any> : CommandValueArgumentParser<T> { + public companion object { + /** + * 抛出一个 [CommandArgumentParserException] 的捷径 + * + * @throws CommandArgumentParserException + */ + @JvmStatic + @JvmSynthetic + @Throws(CommandArgumentParserException::class) + protected inline fun CommandValueArgumentParser<*>.illegalArgument(message: String, cause: Throwable? = null): Nothing = + throw CommandArgumentParserException(message, cause) -private class MappingCommandValueArgumentParser<T : Any, R : Any>( + /** + * 检查参数 [condition]. 当它为 `false` 时调用 [message] 并以其返回值作为消息, 抛出异常 [CommandArgumentParserException] + * + * @throws CommandArgumentParserException + */ + @JvmStatic + @Throws(CommandArgumentParserException::class) + @JvmSynthetic + protected inline fun CommandValueArgumentParser<*>.checkArgument( + condition: Boolean, + crossinline message: () -> String = { "Check failed." }, + ) { + contract { + returns() implies condition + callsInPlace(message, InvocationKind.AT_MOST_ONCE) + } + if (!condition) illegalArgument(message()) + } + } +} + +public class MappingCommandValueArgumentParser<T : Any, R : Any>( private val original: CommandValueArgumentParser<T>, - private val mapper: CommandValueArgumentParser<R>.(T) -> R, -) : CommandValueArgumentParser<R> { + private val mapper: MappingCommandValueArgumentParser<T, R>.(T) -> R, +) : AbstractCommandValueArgumentParser<R>() { override fun parse(raw: String, sender: CommandSender): R = mapper(original.parse(raw, sender)) override fun parse(raw: MessageContent, sender: CommandSender): R = mapper(original.parse(raw, sender)) -} - -/** - * 解析一个字符串或 [SingleMessage] 为 [T] 类型参数 - * - * @throws IllegalArgumentException 当 [raw] 既不是 [SingleMessage], 也不是 [String] 时抛出. - */ -@JvmSynthetic -@Throws(IllegalArgumentException::class) -public fun <T : Any> CommandValueArgumentParser<T>.parse(raw: Any, sender: CommandSender): T { - contract { - returns() implies (raw is String || raw is SingleMessage) - } - - return when (raw) { - is String -> parse(raw, sender) - is MessageContent -> parse(raw, sender) - else -> throw IllegalArgumentException("Illegal raw argument type: ${raw::class.qualifiedName}") - } -} - -/** - * 抛出一个 [CommandArgumentParserException] 的捷径 - * - * @throws CommandArgumentParserException - */ -@Suppress("unused") -@JvmSynthetic -@Throws(CommandArgumentParserException::class) -public inline fun CommandValueArgumentParser<*>.illegalArgument(message: String, cause: Throwable? = null): Nothing { - throw CommandArgumentParserException(message, cause) -} - -/** - * 检查参数 [condition]. 当它为 `false` 时调用 [message] 并以其返回值作为消息, 抛出异常 [CommandArgumentParserException] - * - * @throws CommandArgumentParserException - */ -@Throws(CommandArgumentParserException::class) -@JvmSynthetic -public inline fun CommandValueArgumentParser<*>.checkArgument( - condition: Boolean, - crossinline message: () -> String = { "Check failed." }, -) { - contract { - returns() implies condition - callsInPlace(message, InvocationKind.AT_MOST_ONCE) - } - if (!condition) illegalArgument(message()) } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCommand.kt deleted file mode 100644 index af4fbbe0e..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCommand.kt +++ /dev/null @@ -1,25 +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 - */ - -package net.mamoe.mirai.console.command.java - -import net.mamoe.mirai.console.command.Command -import net.mamoe.mirai.console.util.ConsoleExperimentalApi - -/** - * 为 Java 用户添加协程帮助的 [Command]. - * - * 注意, [JSimpleCommand], [JCompositeCommand], [JRawCommand] 都不实现这个接口. [JCommand] 只设计为 Java 使用者自己实现 [Command] 相关内容. - * - * @see Command - */ -@ConsoleExperimentalApi("Not yet supported") -public interface JCommand : Command { - // TODO: 2020/10/18 JCommand -} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt index 775542e99..b3828418d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt @@ -18,7 +18,6 @@ import net.mamoe.mirai.console.command.descriptor.buildCommandArgumentContext import net.mamoe.mirai.console.compiler.common.ResolveContext import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME import net.mamoe.mirai.console.permission.Permission -import net.mamoe.mirai.console.util.ConsoleExperimentalApi /** * 复合指令. 指令注册时候会通过反射构造指令解析器. @@ -70,7 +69,6 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi * * @see buildCommandArgumentContext */ -@ConsoleExperimentalApi("Not yet supported") public abstract class JCompositeCommand @JvmOverloads constructor( owner: CommandOwner, diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt index 539c409a1..70b62e680 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt @@ -18,7 +18,6 @@ import net.mamoe.mirai.console.compiler.common.ResolveContext import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME import net.mamoe.mirai.console.internal.command.createOrFindCommandPermission import net.mamoe.mirai.console.permission.Permission -import net.mamoe.mirai.console.util.ConsoleExperimentalApi /** * 供 Java 用户继承 @@ -46,7 +45,6 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi * * @see JRawCommand */ -@ConsoleExperimentalApi("Not yet supported") public abstract class JRawCommand @JvmOverloads constructor( /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt index bb56daeba..706a14df4 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt @@ -17,7 +17,6 @@ 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 import net.mamoe.mirai.console.permission.Permission -import net.mamoe.mirai.console.util.ConsoleExperimentalApi /** * Java 实现: @@ -43,7 +42,6 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi * @see SimpleCommand * @see [CommandManager.executeCommand] */ -@ConsoleExperimentalApi("Not yet supported") public abstract class JSimpleCommand( owner: CommandOwner, @ResolveContext(COMMAND_NAME) primaryName: String, diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/BuiltInCommandCallResolver.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/BuiltInCommandCallResolver.kt index 98a203118..5869352f6 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/BuiltInCommandCallResolver.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/BuiltInCommandCallResolver.kt @@ -77,7 +77,7 @@ public object BuiltInCommandCallResolver : CommandCallResolver { } else { if (valueArguments.size > valueParameters.size && zipped.last().first.isVararg) { // merge vararg arguments - val (varargParameter, varargFirstArgument) + val (varargParameter, _) = zipped.removeLast() zipped.add(varargParameter to DefaultCommandValueArgument(valueArguments.drop(zipped.size).map { it.value }.asMessageChain())) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/ResolvedCommandCall.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/ResolvedCommandCall.kt index 547dd736a..7b9a2db3b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/ResolvedCommandCall.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/ResolvedCommandCall.kt @@ -13,6 +13,7 @@ import net.mamoe.mirai.console.command.Command import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.console.command.CompositeCommand import net.mamoe.mirai.console.command.descriptor.* +import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser.Companion.parse import net.mamoe.mirai.console.command.parse.CommandCall import net.mamoe.mirai.console.command.parse.CommandValueArgument import net.mamoe.mirai.console.command.parse.mapToTypeOrNull