Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用 View 替代 EventProcessingContext.results #504

Merged
merged 3 commits into from
Nov 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import love.forte.simbot.Attribute
import love.forte.simbot.ExperimentalSimbotApi
import love.forte.simbot.MutableAttributeMap
import love.forte.simbot.attribute
import love.forte.simbot.event.EventProcessingResult.Empty.resultsView
import love.forte.simbot.utils.view.IndexAccessView
import org.jetbrains.annotations.UnmodifiableView
import kotlin.coroutines.CoroutineContext

Expand All @@ -46,12 +48,19 @@ public interface EventProcessingContext : CoroutineContext.Element, InstantScope
public val event: Event

/**
* 已经执行过的所有监听函数的结果。
*
* 已经执行过的所有监听函数的结果视图的二次列表收集。
* 此列表仅由事件处理器内部操作,是一个对外不可变视图。
*
* 请使用 [resultsView], 此api会在适当时机被删除。
* @see resultsView
*/
public val results: @UnmodifiableView List<EventResult>
@Deprecated("Use 'resultsView'", replaceWith = ReplaceWith("resultsView"))
public val results: @UnmodifiableView List<EventResult> get() = resultsView.toList()

/**
* 本次流程下执行后得到的所有响应结果的视图。按照顺序计入。
*/
public val resultsView: IndexAccessView<EventResult>

/**
* 当前事件所处环境中所能够提供的消息序列化模块信息。
Expand Down Expand Up @@ -138,7 +147,6 @@ public interface InstantScopeContext : ScopeContext
*/
public interface EventListenerProcessingContext : EventProcessingContext {
override val event: Event
override val results: List<EventResult>
override fun <T : Any> getAttribute(attribute: Attribute<T>): T?

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import love.forte.plugin.suspendtrans.annotation.JvmAsync
import love.forte.plugin.suspendtrans.annotation.JvmBlocking
import love.forte.simbot.SimbotIllegalStateException
import love.forte.simbot.event.EventProcessingResult.Empty
import org.jetbrains.annotations.UnmodifiableView
import love.forte.simbot.utils.view.IndexAccessView
import love.forte.simbot.utils.view.emptyView


/**
Expand Down Expand Up @@ -103,18 +104,29 @@ public suspend inline fun <reified E : Event> EventProcessor.pushIfProcessable(b
public interface EventProcessingResult {

/**
* 本次流程下执行后得到的所有响应结果。按照顺序计入。
* 本次流程下执行后得到的所有响应结果的视图的二次收集结果。
* 请使用 [resultsView], 此api会在适当时机被删除。
* @see resultsView
*/
public val results: @UnmodifiableView List<EventResult>

@Deprecated("Use 'resultsView'", replaceWith = ReplaceWith("resultsView"))
public val results: List<EventResult> get() = resultsView.toList()


/**
* 本次流程下执行后得到的所有响应结果的视图。按照顺序计入。
*/
public val resultsView: IndexAccessView<EventResult>

/**
* [EventProcessingResult] 的特殊无效实现,一般使用在例如全局拦截器进行拦截的时候。
*
* [Empty] 的 [results][Empty.results] 永远得到空列表。
*/
public companion object Empty : EventProcessingResult {
override val results: List<EventResult> get() = emptyList()
@Suppress("OVERRIDE_DEPRECATION")
override val results: List<EventResult>
get() = emptyList()

override val resultsView: IndexAccessView<EventResult>
get() = emptyView()

}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public interface EventResult {
* 得到一个无效的特殊默认值。
*
* [invalid] 与 [defaults] 得到结果的区别在于,[Invalid] 代表的是“无效的”,
* 因此此结果不会被记录到 [EventProcessingResult.results] 中。
* 因此此结果不会被记录到 [EventProcessingResult.resultsView] 中。
*
* 当一个监听事件的结果为 [Invalid], 则代表它“没有真正地执行成功”,或者可以简单的理解为”忽略结果“。
*
Expand Down Expand Up @@ -162,7 +162,7 @@ public interface EventResult {
* 代表着 **无效** 的 [EventResult] 实例,是一个具有[特殊意义][SpecialEventResult]的类型: [事件处理器][EventProcessor] 不应对此结果进行保留或处理。
*
* [Invalid] 与其他的 [EventResult] 得到结果的区别在于,[Invalid] 代表的是“无效的”,
* 因此此结果不会被记录到 [EventProcessingResult.results] 中。
* 因此此结果不会被记录到 [EventProcessingResult.resultsView] 中。
*
* 当一个监听事件的结果为 [Invalid], 则代表它“没有真正地执行成功”,或可以简单的理解为“忽略结果”。
*/
Expand Down Expand Up @@ -298,7 +298,7 @@ public abstract class ReactivelyCollectableEventResult : SpecialEventResult() {

/**
* 当响应式结果 [content] 被收集完毕后通过 [collected] 提供其收集的结果 [collectedContent],
* 并作为一个新的 [EventResult] 结果提供给 [EventProcessingResult.results].
* 并作为一个新的 [EventResult] 结果提供给 [EventProcessingResult.resultsView].
*
* [collected] 的结果不会再被二次收集, 因此假若 [collectedContent] 仍然为响应式类型, 则它们将会被忽略并直接作为结果返回.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2022-2022 ForteScarlet <ForteScarlet@163.com>
*
* 本文件是 simply-robot (或称 simple-robot 3.x 、simbot 3.x ) 的一部分。
*
* simply-robot 是自由软件:你可以再分发之和/或依照由自由软件基金会发布的 GNU 通用公共许可证修改之,无论是版本 3 许可证,还是(按你的决定)任何以后版都可以。
*
* 发布 simply-robot 是希望它能有用,但是并无保障;甚至连可销售和符合某个特定的目的都不保证。请参看 GNU 通用公共许可证,了解详情。
*
* 你应该随程序获得一份 GNU 通用公共许可证的复本。如果没有,请看:
* https://www.gnu.org/licenses
* https://www.gnu.org/licenses/gpl-3.0-standalone.html
* https://www.gnu.org/licenses/lgpl-3.0-standalone.html
*
*/

package love.forte.simbot.utils.view


/**
* 一个“视图”。试图的主要应用是对使用者提供一个只读的集合类型,类似于 [Collection]。
* 但是它不直接实现 [Collection] 或相关接口,而是提供一些较为基础的、与 [Collection] 相似的方法。
*
* 因此它不论面向什么平台(比如JVM平台)都是一种更安全的只读列表。
*
* @author ForteScarlet
*/
public interface View<out T> : Iterable<T> {
/**
* 得到当前视图的迭代器。
*/
override fun iterator(): Iterator<T>

/**
* 获取当前视图中的元素数量。
*/
public val size: Int

/**
* 判断当前视图是否为空。
*/
public fun isEmpty(): Boolean

/**
* 判断当前视图中是否包含指定元素。
*/
public operator fun contains(element: @UnsafeVariance T): Boolean
}

/**
* 代表为一个可以通过索引值访问任意元素的 [View] 类型。
*/
public interface IndexAccessView<out T> : View<T> {

/**
* 获取指定索引上的元素。索引值以0为始。
* @throws IndexOutOfBoundsException 当访问的索引超出界限时
*
*/
public operator fun get(index: Int): T

/*
toCollection() ?
asCollection() ?
*/
}

/**
* 判断当前视图是否不为空。
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun <T> View<T>.isNotEmpty(): Boolean = !isEmpty()


Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2022-2022 ForteScarlet <ForteScarlet@163.com>
*
* 本文件是 simply-robot (或称 simple-robot 3.x 、simbot 3.x ) 的一部分。
*
* simply-robot 是自由软件:你可以再分发之和/或依照由自由软件基金会发布的 GNU 通用公共许可证修改之,无论是版本 3 许可证,还是(按你的决定)任何以后版都可以。
*
* 发布 simply-robot 是希望它能有用,但是并无保障;甚至连可销售和符合某个特定的目的都不保证。请参看 GNU 通用公共许可证,了解详情。
*
* 你应该随程序获得一份 GNU 通用公共许可证的复本。如果没有,请看:
* https://www.gnu.org/licenses
* https://www.gnu.org/licenses/gpl-3.0-standalone.html
* https://www.gnu.org/licenses/lgpl-3.0-standalone.html
*
*/

@file:JvmName("Views")

package love.forte.simbot.utils.view


/**
* 构建一个当前 [List] 对应的 [View]。
*/
public fun <T> List<T>.asView(): IndexAccessView<T> {
if (this === emptyList<T>()) {
return EmptyView
}

return ListView(this)
}


private class ListView<out T>(private val list: List<T>) : IndexAccessView<T>, RandomAccess {
override fun iterator(): Iterator<T> = list.iterator()

override val size: Int
get() = list.size

override fun isEmpty(): Boolean = list.isEmpty()

override fun contains(element: @UnsafeVariance T): Boolean = element in list

override fun get(index: Int): T = list[index]
}

/**
* 构建一个当前 [Collection] 对应的 [View]。
* 如果当前集合类型为 [List], 则相当于 [List.asView]。
*/
public fun <T> Collection<T>.asView(): View<T> {
return if (this is List) asView() else CollectionView(this)
}


private class CollectionView<out T>(private val collection: Collection<T>) : View<T> {
override fun iterator(): Iterator<T> = collection.iterator()

override val size: Int
get() = collection.size

override fun isEmpty(): Boolean = collection.isEmpty()

override fun contains(element: @UnsafeVariance T): Boolean = element in collection
}

/**
* 构建一个当前 [Iterable] 对应的 [View]。
* 如果当前类型为 [Collection], 则相当于 [Collection.asView]。
*
* 如果当前 [Iterable] 不属于集合类型或列表类型,
* 那么得到的 [View] 中大多数操作都可能是直接依托于 [Iterable.iterator] 方法的,
* 例如 [View.size], 每次获取都会进行一次遍历与计算。
*
*/
public fun <T> Iterable<T>.asView(): View<T> {
return when (this) {
is List -> this.asView()
is Collection -> this.asView()
else -> IterableView(this)
}
}


private class IterableView<out T>(private val iterable: Iterable<T>) : View<T> {
override fun iterator(): Iterator<T> = iterable.iterator()

override val size: Int
get() = iterable.count()

override fun isEmpty(): Boolean = iterable.iterator().hasNext()

override fun contains(element: @UnsafeVariance T): Boolean = iterable.any { it == element }
}

/**
* 得到一个永远不会有内容的 [View]。
*/
public fun <T> emptyView(): IndexAccessView<T> = EmptyView

/**
* 得到一个永远不会存在内容的 [View] 实现。
*/
private object EmptyView : IndexAccessView<Nothing>, RandomAccess {
override fun iterator(): Iterator<Nothing> = EmptyIterator

private object EmptyIterator : Iterator<Nothing> {
override fun hasNext(): Boolean = false
override fun next(): Nothing = throw NoSuchElementException()
}

override val size: Int
get() = 0

override fun isEmpty(): Boolean = true

override fun contains(element: Nothing): Boolean = false

override fun get(index: Int): Nothing = throw IndexOutOfBoundsException("Empty view doesn't contain element at index $index.")
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ public interface EventProcessingContextResolver<C : EventProcessingContext> {
public suspend fun resolveEventToContext(event: Event, listenerSize: Int): C?

/**
* 向提供的上下文 [C] 的 [EventProcessingContext.results] 中追加一个 [EventResult].
* 向提供的上下文 [C] 的 [EventProcessingContext.resultsView] 中追加一个 [EventResult].
*
* [SimpleEventListenerManager] 会对所有得到的结果进行尝试推送,包括 [EventResult.Invalid],
* 但是建议不会真正的添加 [EventResult.Invalid].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import love.forte.simbot.core.scope.SimpleScope
import love.forte.simbot.event.*
import love.forte.simbot.event.EventListener
import love.forte.simbot.logger.LoggerFactory
import love.forte.simbot.utils.ListView
import love.forte.simbot.utils.view
import love.forte.simbot.utils.view.IndexAccessView
import love.forte.simbot.utils.view.asView
import org.slf4j.Logger
import java.lang.reflect.InvocationTargetException
import java.util.*
Expand Down Expand Up @@ -332,7 +332,7 @@ internal class SimpleEventListenerManagerImpl internal constructor(
}

// resolve to processing result
SimpleEventProcessingResult(context.results)
SimpleEventProcessingResult(context.resultsView)
}
}.getOrElse {
currentBot.logger.error("Event process failed.", it)
Expand Down Expand Up @@ -495,7 +495,7 @@ private interface ListenerInvokerContainer {
}


private data class SimpleEventProcessingResult(override val results: List<EventResult>) : EventProcessingResult
private data class SimpleEventProcessingResult(override val resultsView: IndexAccessView<EventResult>) : EventProcessingResult


/**
Expand Down Expand Up @@ -533,14 +533,11 @@ internal class SimpleEventProcessingContext(

private val _results = ArrayList<EventResult>(resultInitSize.coerceAtLeast(1))

private var resultView: ListView<EventResult>? = null
private var _resultsView: IndexAccessView<EventResult>? = null

override val results: List<EventResult>
get() {
// sync is not necessary, probably.
return resultView ?: _results.view().also {
resultView = it
}
override val resultsView: IndexAccessView<EventResult>
get() = _resultsView ?: _results.asView().also {
_resultsView = it
}

internal fun addResult(result: EventResult) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class EventAsyncProcessingTest {
})

val resultContent = runBlocking {
val results = manager.push(TestEvent(bot)).results
val results = manager.push(TestEvent(bot)).resultsView
assertEquals(4, results.size, "result size")
results.joinToString(" ") {
when (val c = it.content) {
Expand Down Expand Up @@ -103,7 +103,7 @@ class EventAsyncProcessingTest {
})

val resultContent = runBlocking {
val results = manager.push(TestEvent(bot)).results
val results = manager.push(TestEvent(bot)).resultsView
assertEquals(2, results.size, "result size")
results.joinToString(" ") {
it as AsyncEventResult
Expand Down