linear.kt 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /*
  2. * Copyright 2019-2020 Mamoe Technologies and contributors.
  3. *
  4. * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
  5. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
  6. *
  7. * https://github.com/mamoe/mirai/blob/master/LICENSE
  8. */
  9. @file:Suppress("unused")
  10. package net.mamoe.mirai.event
  11. import kotlinx.coroutines.*
  12. import kotlin.coroutines.CoroutineContext
  13. import kotlin.coroutines.EmptyCoroutineContext
  14. import kotlin.reflect.KClass
  15. /**
  16. * 挂起当前协程, 监听事件 [E], 并尝试从这个事件中**同步**一个值, 在超时时抛出 [TimeoutCancellationException]
  17. *
  18. * @param timeoutMillis 超时. 单位为毫秒.
  19. * @param mapper 过滤转换器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
  20. *
  21. * @see asyncFromEvent 本函数的异步版本
  22. * @see subscribe 普通地监听一个事件
  23. * @see nextEvent 挂起当前协程, 并获取下一个事件实例
  24. *
  25. * @see syncFromEventOrNull 本函数的在超时后返回 `null` 的版本
  26. *
  27. * @throws TimeoutCancellationException 在超时后抛出.
  28. * @throws Throwable 当 [mapper] 抛出任何异常时, 本函数会抛出该异常
  29. */
  30. @JvmSynthetic
  31. public suspend inline fun <reified E : Event, R : Any> syncFromEvent(
  32. timeoutMillis: Long = -1,
  33. priority: Listener.EventPriority = EventPriority.MONITOR,
  34. crossinline mapper: suspend E.(E) -> R?
  35. ): R {
  36. require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" }
  37. return if (timeoutMillis == -1L) {
  38. coroutineScope {
  39. syncFromEventImpl<E, R>(E::class, this, priority, mapper)
  40. }
  41. } else {
  42. withTimeout(timeoutMillis) {
  43. syncFromEventImpl<E, R>(E::class, this, priority, mapper)
  44. }
  45. }
  46. }
  47. /**
  48. * 挂起当前协程, 监听这个事件, 并尝试从这个事件中获取一个值, 在超时时返回 `null`
  49. *
  50. * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
  51. * @param mapper 过滤转换器. 返回非 null 则代表得到了需要的值.
  52. *
  53. * @return 超时返回 `null`, 否则返回 [mapper] 返回的第一个非 `null` 值.
  54. *
  55. * @see asyncFromEvent 本函数的异步版本
  56. * @see subscribe 普通地监听一个事件
  57. * @see nextEvent 挂起当前协程, 并获取下一个事件实例
  58. *
  59. * @throws Throwable 当 [mapper] 抛出任何异常时, 本函数会抛出该异常
  60. */
  61. @JvmSynthetic
  62. public suspend inline fun <reified E : Event, R : Any> syncFromEventOrNull(
  63. timeoutMillis: Long,
  64. priority: Listener.EventPriority = EventPriority.MONITOR,
  65. crossinline mapper: suspend E.(E) -> R?
  66. ): R? {
  67. require(timeoutMillis > 0) { "timeoutMillis must be > 0" }
  68. return withTimeoutOrNull(timeoutMillis) {
  69. syncFromEventImpl<E, R>(E::class, this, priority, mapper)
  70. }
  71. }
  72. /**
  73. * 异步监听这个事件, 并尝试从这个事件中获取一个值.
  74. *
  75. * 若 [mapper] 抛出的异常将会被传递给 [Deferred.await] 抛出.
  76. *
  77. * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
  78. * @param coroutineContext 额外的 [CoroutineContext]
  79. * @param mapper 过滤转换器. 返回非 `null` 则代表得到了需要的值. [syncFromEvent] 会返回这个值
  80. *
  81. * @see syncFromEvent
  82. * @see asyncFromEvent
  83. * @see subscribe 普通地监听一个事件
  84. * @see nextEvent 挂起当前协程, 并获取下一个事件实例
  85. */
  86. @JvmSynthetic
  87. @Suppress("DeferredIsResult")
  88. public inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEventOrNull(
  89. timeoutMillis: Long,
  90. coroutineContext: CoroutineContext = EmptyCoroutineContext,
  91. priority: Listener.EventPriority = EventPriority.MONITOR,
  92. crossinline mapper: suspend E.(E) -> R?
  93. ): Deferred<R?> {
  94. require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" }
  95. return this.async(coroutineContext) {
  96. syncFromEventOrNull(timeoutMillis, priority, mapper)
  97. }
  98. }
  99. /**
  100. * 异步监听这个事件, 并尝试从这个事件中获取一个值.
  101. *
  102. * 若 [mapper] 抛出的异常将会被传递给 [Deferred.await] 抛出.
  103. *
  104. * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
  105. * @param coroutineContext 额外的 [CoroutineContext]
  106. * @param mapper 过滤转换器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
  107. *
  108. * @see syncFromEvent
  109. * @see asyncFromEventOrNull
  110. * @see subscribe 普通地监听一个事件
  111. * @see nextEvent 挂起当前协程, 并获取下一个事件实例
  112. */
  113. @JvmSynthetic
  114. @Suppress("DeferredIsResult")
  115. public inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEvent(
  116. timeoutMillis: Long = -1,
  117. coroutineContext: CoroutineContext = EmptyCoroutineContext,
  118. priority: Listener.EventPriority = EventPriority.MONITOR,
  119. crossinline mapper: suspend E.(E) -> R?
  120. ): Deferred<R> {
  121. require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" }
  122. return this.async(coroutineContext) {
  123. syncFromEvent(timeoutMillis, priority, mapper)
  124. }
  125. }
  126. //////////////
  127. //// internal
  128. //////////////
  129. @JvmSynthetic
  130. @PublishedApi
  131. internal suspend inline fun <E : Event, R> syncFromEventImpl(
  132. eventClass: KClass<E>,
  133. coroutineScope: CoroutineScope,
  134. priority: Listener.EventPriority,
  135. crossinline mapper: suspend E.(E) -> R?
  136. ): R = suspendCancellableCoroutine { cont ->
  137. coroutineScope.globalEventChannel().subscribe(eventClass, priority = priority) {
  138. try {
  139. cont.resumeWith(kotlin.runCatching {
  140. mapper.invoke(this, it) ?: return@subscribe ListeningStatus.LISTENING
  141. })
  142. } catch (e: Exception) {
  143. }
  144. return@subscribe ListeningStatus.STOPPED
  145. }
  146. }