1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-04-25 21:23:55 +08:00

Fix parser context

This commit is contained in:
Him188 2020-05-14 16:37:17 +08:00
parent 89ac4e863c
commit b857c8ad41
5 changed files with 95 additions and 31 deletions
backend/mirai-console/src
main/kotlin/net/mamoe/mirai/console/command
test/java/net/mamoe/mirai/console/command

View File

@ -97,7 +97,7 @@ class CommandArgs private constructor(
command.descriptor.params.asSequence().zip(rawArgs.asSequence()).map { (commandParam, any) ->
command.parserFor(commandParam)?.parse(any, sender)
?: error("ICould not find a parser for param named ${commandParam.name}")
?: error("Could not find a parser for param named ${commandParam.name}, typed ${commandParam.type.qualifiedName}")
}.toList().let { bakedArgs ->
return CommandArgs(bakedArgs, command)
}

View File

@ -29,9 +29,9 @@ class CommandDescriptor(
*/
description: String = "",
/**
* 指令参数解析器环境.
* 覆盖内建的指令参数解析器环境.
*/
val context: CommandParserContext = CommandParserContext.Builtins,
overrideContext: CommandParserContext = CommandParserContext.Empty,
/**
* 指令别名
*/
@ -44,6 +44,11 @@ class CommandDescriptor(
*/
val permission: CommandPermission = CommandPermission.Default
) {
/**
* 指令参数解析器环境.
*/
val context: CommandParserContext = CommandParserContext.Builtins + overrideContext
/**
* 指令别名
*/
@ -132,9 +137,12 @@ internal fun CommandFullName.checkFullName(errorHint: String): CommandFullName {
*/
@Suppress("FunctionName")
inline fun CommandDescriptor(
vararg fullName: Any,
/**
* 指令全名
*/
vararg fullNameComponents: Any,
block: CommandDescriptorBuilder.() -> Unit = {}
): CommandDescriptor = CommandDescriptorBuilder(*fullName).apply(block).build()
): CommandDescriptor = CommandDescriptorBuilder(*fullNameComponents).apply(block).build()
class CommandDescriptorBuilder(
vararg fullName: Any

View File

@ -12,19 +12,27 @@
package net.mamoe.mirai.console.command
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.command.AbstractCommandParserContext.ParserPair
import net.mamoe.mirai.console.command.CommandParserContext.ParserPair
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import kotlin.internal.LowPriorityInOverloadResolution
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
/**
* [KClass] [CommandArgParser] 的匹配
* @see AbstractCommandParserContext
* @see CustomCommandParserContext 自定义
*/
interface CommandParserContext {
operator fun <T : Any> get(klass: KClass<T>): CommandArgParser<T>?
data class ParserPair<T : Any>(
val klass: KClass<T>,
val parser: CommandArgParser<T>
)
operator fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>?
fun toList(): List<ParserPair<*>>
/**
* 内建的默认 [CommandArgParser]
@ -43,6 +51,8 @@ interface CommandParserContext {
Group::class with ExistGroupArgParser
Bot::class with ExistBotArgParser
})
object Empty : CommandParserContext by CustomCommandParserContext(listOf())
}
fun <T : Any> CommandParserContext.parserFor(param: CommandParam<T>): CommandArgParser<T>? =
@ -58,20 +68,38 @@ fun <T : Any> Command.parserFor(param: CommandParam<T>): CommandArgParser<T>? =
* 合并两个 [CommandParserContext], [replacer] 将会替换 [this] 中重复的 parser.
*/
operator fun CommandParserContext.plus(replacer: CommandParserContext): CommandParserContext {
if (replacer == CommandParserContext.Empty) return this
if (this == CommandParserContext.Empty) return replacer
return object : CommandParserContext {
override fun <T : Any> get(klass: KClass<T>): CommandArgParser<T>? = replacer[klass] ?: this@plus[klass]
override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? = replacer[klass] ?: this@plus[klass]
override fun toList(): List<ParserPair<*>> = replacer.toList() + this@plus.toList()
}
}
/**
* 合并 [this] [replacer], [replacer] 将会替换 [this] 中重复的 parser.
*/
operator fun CommandParserContext.plus(replacer: List<ParserPair<*>>): CommandParserContext {
if (replacer.isEmpty()) return this
if (this == CommandParserContext.Empty) return CustomCommandParserContext(replacer)
return object : CommandParserContext {
@Suppress("UNCHECKED_CAST")
override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? =
replacer.firstOrNull { klass.isSubclassOf(it.klass) }?.parser as CommandArgParser<T>? ?: this@plus[klass]
override fun toList(): List<ParserPair<*>> = replacer.toList() + this@plus.toList()
}
}
@Suppress("UNCHECKED_CAST")
open class AbstractCommandParserContext(val list: List<ParserPair<*>>) : CommandParserContext {
class ParserPair<T : Any>(
val klass: KClass<T>,
val parser: CommandArgParser<T>
)
open class CustomCommandParserContext(val list: List<ParserPair<*>>) : CommandParserContext {
override fun <T : Any> get(klass: KClass<T>): CommandArgParser<T>? =
this.list.firstOrNull { it.klass == klass }?.parser as CommandArgParser<T>?
override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? =
this.list.firstOrNull { klass.isSubclassOf(it.klass) }?.parser as CommandArgParser<T>?
override fun toList(): List<ParserPair<*>> {
return list
}
}
/**
@ -93,8 +121,7 @@ open class AbstractCommandParserContext(val list: List<ParserPair<*>>) : Command
@Suppress("FunctionName")
@JvmSynthetic
inline fun CommandParserContext(block: CommandParserContextBuilder.() -> Unit): CommandParserContext {
return AbstractCommandParserContext(
CommandParserContextBuilder().apply(block).distinctByReversed { it.klass })
return CustomCommandParserContext(CommandParserContextBuilder().apply(block).distinctByReversed { it.klass })
}
/**
@ -103,7 +130,7 @@ inline fun CommandParserContext(block: CommandParserContextBuilder.() -> Unit):
class CommandParserContextBuilder : MutableList<ParserPair<*>> by mutableListOf() {
@JvmName("add")
inline infix fun <T : Any> KClass<T>.with(parser: CommandArgParser<T>): ParserPair<*> =
ParserPair(this, parser)
ParserPair(this, parser).also { add(it) }
/**
* 添加一个指令解析器
@ -112,7 +139,7 @@ class CommandParserContextBuilder : MutableList<ParserPair<*>> by mutableListOf(
@LowPriorityInOverloadResolution
inline infix fun <T : Any> KClass<T>.with(
crossinline parser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T
): ParserPair<*> = ParserPair(this, CommandArgParser(parser))
): ParserPair<*> = ParserPair(this, CommandArgParser(parser)).also { add(it) }
/**
* 添加一个指令解析器
@ -120,16 +147,16 @@ class CommandParserContextBuilder : MutableList<ParserPair<*>> by mutableListOf(
@JvmSynthetic
inline infix fun <T : Any> KClass<T>.with(
crossinline parser: CommandArgParser<T>.(s: String) -> T
): ParserPair<*> = ParserPair(this, CommandArgParser { s: String, _: CommandSender -> parser(s) })
): ParserPair<*> = ParserPair(this, CommandArgParser { s: String, _: CommandSender -> parser(s) }).also { add(it) }
}
@PublishedApi
internal inline fun <T, K> Iterable<T>.distinctByReversed(selector: (T) -> K): List<T> {
internal inline fun <T, K> List<T>.distinctByReversed(selector: (T) -> K): List<T> {
val set = HashSet<K>()
val list = ArrayList<T>()
for (i in list.indices.reversed()) {
val element = list[i]
for (i in this.indices.reversed()) {
val element = this[i]
if (set.add(element.let(selector))) {
list.add(element)
}

View File

@ -24,7 +24,7 @@ interface CommandSender {
/**
* 立刻发送一条消息
*/
suspend fun sendMessage(messageChain: Message)
suspend fun sendMessage(message: Message)
suspend fun sendMessage(message: String)
@ -56,7 +56,7 @@ abstract class AbstractCommandSender : CommandSender {
* 控制台指令执行者. 代表由控制台执行指令
*/
object ConsoleCommandSender : AbstractCommandSender() {
override suspend fun sendMessage(messageChain: Message) {
override suspend fun sendMessage(message: Message) {
TODO()
// MiraiConsole.logger("[Command]", 0, messageChain.toString())
}
@ -86,8 +86,8 @@ interface BotAware {
*/
@Suppress("MemberVisibilityCanBePrivate")
open class ContactCommandSender(override val bot: Bot, val contact: Contact) : AbstractCommandSender(), BotAware {
override suspend fun sendMessage(messageChain: Message) {
contact.sendMessage(messageChain)
override suspend fun sendMessage(message: Message) {
contact.sendMessage(message)
}
override suspend fun sendMessage(message: String) {

View File

@ -11,10 +11,9 @@
package net.mamoe.mirai.console.command
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.console.plugins.PluginBase
import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.messageChainOf
import net.mamoe.mirai.message.data.toMessage
import net.mamoe.mirai.message.data.*
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@ -71,5 +70,35 @@ internal class TestCommands {
}.register()
)
}
inline fun withSender(block: CommandSender.() -> Unit): MessageChain {
val result = MessageChainBuilder()
val sender: CommandSender = object : CommandSender {
override suspend fun sendMessage(message: Message) {
result.add(message)
}
override suspend fun sendMessage(message: String) {
result.add(message)
}
override fun appendMessage(message: String) {
result.add(message)
}
}
sender.let(block)
return result.asMessageChain()
}
@Test
fun testExecute() = runBlocking {
TestCommand.register()
assertEquals(
"ok",
withSender {
execute("test", "ok", "extra")
}.contentToString()
)
}
}