stabilize commands WIP

This commit is contained in:
Him188 2023-05-06 11:37:53 +01:00
parent b82e938b2c
commit 2f51293bd7
No known key found for this signature in database
GPG Key ID: BA439CDDCF652375
12 changed files with 95 additions and 107 deletions

View File

@ -21,7 +21,6 @@ import kotlin.contracts.contract
/**
* 指令的执行返回
*/
@ExperimentalCommandDescriptors
public sealed class CommandExecuteResult {
/** 指令执行时发生的错误 (如果有) */
public abstract val exception: Throwable?
@ -127,7 +126,6 @@ public sealed class CommandExecuteResult {
/** 解析的 [CommandCall] (如果匹配到) */
public override val call: CommandCall,
/** 尝试执行的指令 */
@ExperimentalCommandDescriptors
public val failureReasons: List<UnmatchedCommandSignature>,
) : Failure() {
/** 指令执行时发生的错误, 总是 `null` */
@ -138,8 +136,7 @@ public sealed class CommandExecuteResult {
}
}
@ExperimentalCommandDescriptors
public class UnmatchedCommandSignature(
public class UnmatchedCommandSignature @ExperimentalCommandDescriptors public constructor(
public val signature: CommandSignature,
public val failureReason: FailureReason,
)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2023 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -13,6 +13,7 @@ import net.mamoe.mirai.console.command.parse.CommandValueArgument
import net.mamoe.mirai.console.internal.data.castOrNull
import net.mamoe.mirai.console.internal.data.kClassQualifiedName
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.NotStableForInheritance
import kotlin.reflect.KType
import kotlin.reflect.typeOf
@ -30,7 +31,7 @@ import kotlin.reflect.typeOf
*
* @see CommandValueArgument.typeVariants
*/
@ExperimentalCommandDescriptors
@NotStableForInheritance
public interface TypeVariant<out OutType> {
/**
* The reified type of [OutType]
@ -43,25 +44,21 @@ public interface TypeVariant<out OutType> {
* @see CommandValueArgument.value
*/
public fun mapValue(valueArgument: Message): OutType
}
public companion object {
/**
* Creates a [TypeVariant] with reified [OutType].
*/
@OptIn(ExperimentalStdlibApi::class)
@JvmSynthetic
public inline operator fun <reified OutType> invoke(crossinline block: (valueParameter: Message) -> OutType): TypeVariant<OutType> {
return object : TypeVariant<OutType> {
override val outType: KType = typeOf<OutType>()
override fun mapValue(valueArgument: Message): OutType = block(valueArgument)
}
}
/**
* Creates a [TypeVariant] with reified [OutType].
* @since 2.15
*/
public inline fun <reified OutType> TypeVariant(crossinline mapValue: (valueParameter: Message) -> OutType): TypeVariant<OutType> {
return object : TypeVariant<OutType> {
override val outType: KType = typeOf<OutType>()
override fun mapValue(valueArgument: Message): OutType = mapValue(valueArgument)
}
}
@ExperimentalCommandDescriptors
public object MessageContentTypeVariant : TypeVariant<MessageContent> {
@OptIn(ExperimentalStdlibApi::class)
override val outType: KType = typeOf<MessageContent>()
override fun mapValue(valueArgument: Message): MessageContent =
valueArgument.castOrNull<MessageContent>()
@ -70,14 +67,12 @@ public object MessageContentTypeVariant : TypeVariant<MessageContent> {
@ExperimentalCommandDescriptors
public object MessageChainTypeVariant : TypeVariant<MessageChain> {
@OptIn(ExperimentalStdlibApi::class)
override val outType: KType = typeOf<MessageChain>()
override fun mapValue(valueArgument: Message): MessageChain = valueArgument.toMessageChain()
}
@ExperimentalCommandDescriptors
public object ContentStringTypeVariant : TypeVariant<String> {
@OptIn(ExperimentalStdlibApi::class)
override val outType: KType = typeOf<String>()
override fun mapValue(valueArgument: Message): String = valueArgument.content
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
* Copyright 2019-2023 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -18,6 +18,7 @@ import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.resolve.CommandCallResolver
import net.mamoe.mirai.console.command.resolve.ResolvedCommandCall
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.utils.NotStableForInheritance
/**
* Unresolved [CommandCall].
@ -31,7 +32,7 @@ import net.mamoe.mirai.message.data.MessageChain
*
* @see ResolvedCommandCall
*/
@ExperimentalCommandDescriptors
@NotStableForInheritance
public interface CommandCall {
/**
* The [CommandSender] responsible to this call.

View File

@ -10,7 +10,6 @@
package net.mamoe.mirai.console.command.parse
import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.resolve.CommandCallResolver
import net.mamoe.mirai.console.command.resolve.ResolvedCommandCall
import net.mamoe.mirai.console.extensions.CommandCallParserProvider
@ -18,18 +17,17 @@ import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
import net.mamoe.mirai.message.data.MessageChain
/**
* Lexical and syntactical parser for transforming a [MessageChain] into [CommandCall]
* 负责将 [MessageChain] 解析为 [CommandCall].
*
* @see CommandCallResolver The call resolver for [CommandCall] to become [ResolvedCommandCall]
* @see CommandCallParserProvider The extension point
*
* @see SpaceSeparatedCommandCallParser
*/
@ExperimentalCommandDescriptors
public interface CommandCallParser {
/**
* Lexically and syntactically parse a [message] into [CommandCall], but performs nothing about resolving a call.
* [message] 解析为 [CommandCall]
*
* @return `null` if unable to parse (i.e. due to syntax errors).
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2023 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -14,11 +14,11 @@ package net.mamoe.mirai.console.command.parse
import net.mamoe.mirai.console.command.descriptor.*
import net.mamoe.mirai.console.internal.data.castOrInternalError
import net.mamoe.mirai.console.internal.data.classifierAsKClass
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageContent
import net.mamoe.mirai.message.data.SingleMessage
import net.mamoe.mirai.utils.NotStableForInheritance
import kotlin.reflect.KType
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.typeOf
@ -27,20 +27,22 @@ import kotlin.reflect.typeOf
/**
* @see CommandValueArgument
*/
@ExperimentalCommandDescriptors
public interface CommandArgument
@NotStableForInheritance
public sealed interface CommandArgument
/**
* @see DefaultCommandValueArgument
*/
@ExperimentalCommandDescriptors
public interface CommandValueArgument : CommandArgument {
@NotStableForInheritance
public sealed interface CommandValueArgument : CommandArgument {
@ExperimentalCommandDescriptors
public val type: KType
/**
* [MessageContent] if single argument
* [MessageChain] is vararg
*/
@ExperimentalCommandDescriptors
public val value: Message
/**
@ -54,12 +56,9 @@ public interface CommandValueArgument : CommandArgument {
/**
* The [CommandValueArgument] that doesn't vary in type (remaining [MessageContent]).
*/
@ConsoleExperimentalApi
@ExperimentalCommandDescriptors
public data class DefaultCommandValueArgument(
public override val value: Message,
) : CommandValueArgument {
@OptIn(ExperimentalStdlibApi::class)
override val type: KType = typeOf<MessageContent>()
override val typeVariants: List<TypeVariant<*>> = listOf(
MessageContentTypeVariant,
@ -124,7 +123,6 @@ private fun KType.createArray(size: Int): Array<Any?> {
@OptIn(ExperimentalCommandDescriptors::class)
private fun CommandValueArgument.mapToTypeOrNullImpl(expectingType: KType, value: Message): Any? {
@OptIn(ExperimentalStdlibApi::class)
val result = typeVariants
.filter { it.outType.isSubtypeOf(expectingType) }
.ifEmpty {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
* Copyright 2019-2023 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -10,7 +10,6 @@
package net.mamoe.mirai.console.command.parse
import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.extensions.CommandCallParserProvider
import net.mamoe.mirai.console.internal.command.flattenCommandComponents
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
@ -19,12 +18,9 @@ import net.mamoe.mirai.message.data.MessageContent
import net.mamoe.mirai.message.data.content
@ConsoleExperimentalApi
@ExperimentalCommandDescriptors
public object SpaceSeparatedCommandCallParser : CommandCallParser {
@ConsoleExperimentalApi
@ExperimentalCommandDescriptors
public object Provider : CommandCallParserProvider {
internal object Provider : CommandCallParserProvider {
override val instance: CommandCallParser get() = SpaceSeparatedCommandCallParser
override val priority: Int get() = -1
}

View File

@ -7,21 +7,19 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("unused", "NOTHING_TO_INLINE")
@file:Suppress("unused")
package net.mamoe.mirai.console.command.resolve
import kotlinx.serialization.Serializable
import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.parse.CommandCall
import net.mamoe.mirai.console.command.parse.CommandCallParser
import net.mamoe.mirai.console.extensions.CommandCallInterceptorProvider
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
import net.mamoe.mirai.console.internal.util.UNREACHABLE_CLAUSE
import net.mamoe.mirai.console.util.safeCast
import net.mamoe.mirai.message.data.Message
import org.jetbrains.annotations.Contract
import net.mamoe.mirai.utils.NotStableForInheritance
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@ -29,7 +27,6 @@ import kotlin.contracts.contract
/**
* 指令解析和调用拦截器. 用于在指令各解析阶段拦截或转换调用.
*/
@ExperimentalCommandDescriptors
public interface CommandCallInterceptor {
/**
* 在指令[语法解析][CommandCallParser]前调用.
@ -70,9 +67,9 @@ public interface CommandCallInterceptor {
val intercepted = ext.instance.interceptBeforeCall(acc, caller)
intercepted?.fold(
onIntercepted = { return intercepted },
otherwise = { it }
onProceed = { it }
) ?: acc
}.let { InterceptResult(it) }
}.let { InterceptResult.proceed(it) }
}
/**
@ -85,9 +82,9 @@ public interface CommandCallInterceptor {
val intercepted = ext.instance.interceptCall(acc)
intercepted?.fold(
onIntercepted = { return intercepted },
otherwise = { it }
onProceed = { it }
) ?: acc
}.let { InterceptResult(it) }
}.let { InterceptResult.proceed(it) }
}
/**
@ -100,78 +97,95 @@ public interface CommandCallInterceptor {
val intercepted = ext.instance.interceptResolvedCall(acc)
intercepted?.fold(
onIntercepted = { return intercepted },
otherwise = { it }
onProceed = { it }
) ?: acc
}.let { InterceptResult(it) }
}.let { InterceptResult.proceed(it) }
}
}
}
/**
* [CommandCallInterceptor] 拦截结果
*/
@ExperimentalCommandDescriptors
public class InterceptResult<T> internal constructor(
private val _value: Any?,
@Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any?,
public class InterceptResult<T> private constructor(
@Suppress("PropertyName")
@PublishedApi
internal val _value: Any?,
) {
/**
* 构造一个 [InterceptResult], [value] 继续处理后续指令执行.
* 当未被拦截时返回未被拦截的值, 否则返回 `null`.
*/
public constructor(value: T) : this(value as Any?, null)
/**
* 构造一个 [InterceptResult], [原因][reason] 中断指令执行.
*/
public constructor(reason: InterceptedReason) : this(reason as Any?, null)
@get:Contract(pure = true)
public val value: T?
@Suppress("UNCHECKED_CAST")
get() {
val value = this._value
@Suppress("UNCHECKED_CAST")
return if (value is InterceptedReason) null else value as T
}
@get:Contract(pure = true)
/**
* 获取拦截原因, 当未被拦截时返回 `null`.
*/
public val reason: InterceptedReason?
get() = this._value.safeCast()
/**
* 当被拦截时返回 `true`, 否则返回 `false`.
* @since 2.15
*/
public val isIntercepted: Boolean get() = _value is InterceptedReason
public companion object {
/**
* 构造一个 [InterceptResult], [value] 继续处理后续指令执行.
*/
@JvmStatic
@JvmName("proceed")
public fun <T> proceed(value: T): InterceptResult<T> = InterceptResult(value)
/**
* 构造一个 [InterceptResult], [原因][reason] 中断指令执行.
*/
@JvmStatic
@JvmName("intercepted")
public fun <T> intercepted(reason: InterceptedReason): InterceptResult<T> = InterceptResult(reason)
}
}
@ExperimentalCommandDescriptors
public inline fun <T, R> InterceptResult<T>.fold(
onIntercepted: (reason: InterceptedReason) -> R,
otherwise: (call: T) -> R,
onProceed: (call: T) -> R,
): R {
contract {
callsInPlace(onIntercepted, InvocationKind.AT_MOST_ONCE)
callsInPlace(otherwise, InvocationKind.AT_MOST_ONCE)
callsInPlace(onProceed, InvocationKind.AT_MOST_ONCE)
}
val value = _value
if (value is InterceptedReason) {
return onIntercepted(value)
} else {
@Suppress("UNCHECKED_CAST")
return onProceed(value as T)
}
value?.let { return otherwise(it) }
reason?.let { return onIntercepted(it) }
UNREACHABLE_CLAUSE
}
@ExperimentalCommandDescriptors
public inline fun <T : R, R> InterceptResult<T>.getOrElse(onIntercepted: (reason: InterceptedReason) -> R): R {
contract { callsInPlace(onIntercepted, InvocationKind.AT_MOST_ONCE) }
reason?.let(onIntercepted)
return value!!
}
/**
* 创建一个 [InterceptedReason]
*
* @see InterceptedReason.create
* [InterceptResult] 为未被拦截时, 返回值. 当为被拦截时, 计算并返回 [onIntercepted].
*/
@ExperimentalCommandDescriptors
@JvmSynthetic
public inline fun InterceptedReason(message: String): InterceptedReason = InterceptedReason.create(message)
public inline fun <T : R, R> InterceptResult<T>.getOrElse(onIntercepted: (reason: InterceptedReason) -> R): R {
contract { callsInPlace(onIntercepted, InvocationKind.AT_MOST_ONCE) }
val value = _value
if (value is InterceptedReason) {
return onIntercepted(value)
}
@Suppress("UNCHECKED_CAST")
return value as R
}
/**
* 拦截原因
*/
@ExperimentalCommandDescriptors
@NotStableForInheritance
public interface InterceptedReason {
public val message: String
@ -179,10 +193,10 @@ public interface InterceptedReason {
/**
* 创建一个 [InterceptedReason]
*/
public fun create(message: String): InterceptedReason = InterceptedReasonData(message)
@JvmStatic
public fun simple(message: String): InterceptedReason = InterceptedReasonData(message)
}
}
@OptIn(ExperimentalCommandDescriptors::class)
@Serializable
private data class InterceptedReasonData(override val message: String) : InterceptedReason

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
* Copyright 2019-2023 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -14,7 +14,6 @@ import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.parse.CommandCall
import net.mamoe.mirai.console.extensions.CommandCallResolverProvider
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.console.util.safeCast
import org.jetbrains.annotations.Contract
import kotlin.contracts.InvocationKind
@ -67,14 +66,11 @@ public inline fun CommandResolveResult.getOrElse(
* @see CommandCallResolverProvider The provider to instances of this class
* @see BuiltInCommandCallResolver The builtin implementation
*/
@ExperimentalCommandDescriptors
public interface CommandCallResolver {
public fun resolve(call: CommandCall): CommandResolveResult
public companion object {
@JvmName("resolveCall")
@ConsoleExperimentalApi
@ExperimentalCommandDescriptors
public fun CommandCall.resolve(): CommandResolveResult {
return GlobalComponentStorage.getExtensions(CommandCallResolverProvider).first()
.extension.instance

View File

@ -31,7 +31,6 @@ import net.mamoe.mirai.message.data.MessageChain
*
* @see ResolvedCommandCallImpl
*/
@ExperimentalCommandDescriptors
public interface ResolvedCommandCall {
/**
* The [CommandSender] responsible to this call.
@ -58,7 +57,7 @@ public interface ResolvedCommandCall {
*
* **Default implementation details**: Lazy calculation.
*/
@ConsoleExperimentalApi
@ExperimentalCommandDescriptors
public val resolvedValueArguments: List<ResolvedCommandValueArgument<*>>
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
* Copyright 2019-2023 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -14,7 +14,6 @@ import net.mamoe.mirai.console.extensions.PermissionServiceProvider
import net.mamoe.mirai.console.extensions.PluginLoaderProvider
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin.Companion.onLoad
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.utils.DeprecatedSinceMirai
/**
@ -46,7 +45,6 @@ public interface Extension {
*
* @since 2.11
*/ // https://github.com/mamoe/mirai/issues/1860
@ConsoleExperimentalApi
public val priority: Int
get() = 0
}

View File

@ -9,16 +9,14 @@
package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.resolve.CommandCallInterceptor
import net.mamoe.mirai.console.extension.AbstractInstanceExtensionPoint
import net.mamoe.mirai.console.extension.InstanceExtension
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
@ExperimentalCommandDescriptors
public interface CommandCallInterceptorProvider : InstanceExtension<CommandCallInterceptor> {
@OptIn(ConsoleExperimentalApi::class)
@ExperimentalCommandDescriptors
public companion object ExtensionPoint :
AbstractInstanceExtensionPoint<CommandCallInterceptorProvider, CommandCallInterceptor>(
CommandCallInterceptorProvider::class

View File

@ -9,7 +9,6 @@
package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.parse.CommandCallParser
import net.mamoe.mirai.console.extension.AbstractInstanceExtensionPoint
import net.mamoe.mirai.console.extension.InstanceExtension
@ -18,10 +17,9 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi
/**
* The provider of [CommandCallParser]
*/
@ExperimentalCommandDescriptors
public interface CommandCallParserProvider : InstanceExtension<CommandCallParser> {
@OptIn(ConsoleExperimentalApi::class)
@ExperimentalCommandDescriptors
public companion object ExtensionPoint :
AbstractInstanceExtensionPoint<CommandCallParserProvider, CommandCallParser>(CommandCallParserProvider::class)
}