StandardUtils.kt 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. * Copyright 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:JvmMultifileClass
  10. @file:JvmName("MiraiUtils")
  11. package net.mamoe.mirai.utils
  12. import java.util.*
  13. import kotlin.contracts.InvocationKind
  14. import kotlin.contracts.contract
  15. import kotlin.reflect.KClass
  16. public inline fun <reified T> Any?.cast(): T {
  17. contract { returns() implies (this@cast is T) }
  18. return this as T
  19. }
  20. public inline fun <reified T> Any?.safeCast(): T? {
  21. contract { returnsNotNull() implies (this@safeCast is T) }
  22. return this as? T
  23. }
  24. public inline fun <reified T> Any?.castOrNull(): T? {
  25. contract { returnsNotNull() implies (this@castOrNull is T) }
  26. return this as? T
  27. }
  28. @Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
  29. public inline fun <T> Any?.uncheckedCast(): T = this as T
  30. public inline fun <reified R> Iterable<*>.firstIsInstanceOrNull(): R? {
  31. for (it in this) {
  32. if (it is R) return it
  33. }
  34. return null
  35. }
  36. @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
  37. @kotlin.internal.InlineOnly
  38. @kotlin.internal.LowPriorityInOverloadResolution
  39. public inline fun <R, T : R> Result<T>.recoverCatchingSuppressed(transform: (exception: Throwable) -> R): Result<R> {
  40. return when (val exception = exceptionOrNull()) {
  41. null -> this
  42. else -> {
  43. try {
  44. Result.success(transform(exception))
  45. } catch (e: Throwable) {
  46. e.addSuppressed(exception)
  47. Result.failure(e)
  48. }
  49. }
  50. }
  51. }
  52. @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
  53. @kotlin.internal.InlineOnly
  54. @kotlin.internal.LowPriorityInOverloadResolution
  55. public inline fun <R> retryCatching(
  56. n: Int,
  57. except: KClass<out Throwable>? = null,
  58. block: (count: Int, lastException: Throwable?) -> R
  59. ): Result<R> {
  60. require(n >= 0) {
  61. "param n for retryCatching must not be negative"
  62. }
  63. var exception: Throwable? = null
  64. repeat(n) {
  65. try {
  66. return Result.success(block(it, exception))
  67. } catch (e: Throwable) {
  68. if (except?.isInstance(e) == true) {
  69. return Result.failure(e)
  70. }
  71. exception?.addSuppressed(e)
  72. exception = e
  73. }
  74. }
  75. return Result.failure(exception!!)
  76. }
  77. @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
  78. @kotlin.internal.InlineOnly
  79. @kotlin.internal.LowPriorityInOverloadResolution
  80. public inline fun <R> retryCatchingExceptions(
  81. n: Int,
  82. except: KClass<out Exception>? = null,
  83. block: (count: Int, lastException: Throwable?) -> R
  84. ): Result<R> {
  85. require(n >= 0) {
  86. "param n for retryCatching must not be negative"
  87. }
  88. var exception: Throwable? = null
  89. repeat(n) {
  90. try {
  91. return Result.success(block(it, exception))
  92. } catch (e: Exception) {
  93. if (except?.isInstance(e) == true) {
  94. return Result.failure(e)
  95. }
  96. exception?.addSuppressed(e)
  97. exception = e
  98. }
  99. }
  100. return Result.failure(exception!!)
  101. }
  102. @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
  103. @kotlin.internal.InlineOnly
  104. public inline fun <R> retryCatching(
  105. n: Int,
  106. except: KClass<out Throwable>? = null,
  107. block: () -> R
  108. ): Result<R> {
  109. require(n >= 0) {
  110. "param n for retryCatching must not be negative"
  111. }
  112. var exception: Throwable? = null
  113. repeat(n) {
  114. try {
  115. return Result.success(block())
  116. } catch (e: Throwable) {
  117. if (except?.isInstance(e) == true) {
  118. return Result.failure(e)
  119. }
  120. exception?.addSuppressed(e)
  121. exception = e
  122. }
  123. }
  124. return Result.failure(exception!!)
  125. }
  126. @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
  127. @kotlin.internal.InlineOnly
  128. public inline fun <R> retryCatchingExceptions(
  129. n: Int,
  130. except: KClass<out Exception>? = null,
  131. block: () -> R
  132. ): Result<R> {
  133. require(n >= 0) {
  134. "param n for retryCatching must not be negative"
  135. }
  136. var exception: Throwable? = null
  137. repeat(n) {
  138. try {
  139. return Result.success(block())
  140. } catch (e: Exception) {
  141. if (except?.isInstance(e) == true) {
  142. return Result.failure(e)
  143. }
  144. exception?.addSuppressed(e)
  145. exception = e
  146. }
  147. }
  148. return Result.failure(exception!!)
  149. }
  150. @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
  151. @kotlin.internal.InlineOnly
  152. public inline fun <R> runCatchingExceptions(block: () -> R): Result<R> {
  153. return try {
  154. Result.success(block())
  155. } catch (e: Exception) {
  156. Result.failure(e)
  157. }
  158. }
  159. public inline fun <E> MutableList<E>.replaceAllKotlin(operator: (E) -> E) {
  160. val li: MutableListIterator<E> = this.listIterator()
  161. while (li.hasNext()) {
  162. li.set(operator(li.next()))
  163. }
  164. }
  165. public fun systemProp(name: String, default: String): String =
  166. System.getProperty(name, default) ?: default
  167. public fun systemProp(name: String, default: Boolean): Boolean =
  168. System.getProperty(name, default.toString())?.toBoolean() ?: default
  169. public fun systemProp(name: String, default: Long): Long =
  170. System.getProperty(name, default.toString())?.toLongOrNull() ?: default
  171. public fun Throwable.getRootCause(maxDepth: Int = 20): Throwable {
  172. var depth = 0
  173. var rootCause: Throwable? = this
  174. while (rootCause?.cause != null) {
  175. rootCause = rootCause.cause
  176. if (depth++ >= maxDepth) break
  177. }
  178. return rootCause ?: this
  179. }
  180. /**
  181. * Use [findCause] instead for better performance.
  182. */
  183. @TestOnly
  184. public fun Throwable.causes(maxDepth: Int = 20): Sequence<Throwable> = sequence {
  185. var depth = 0
  186. var rootCause: Throwable? = this@causes
  187. while (rootCause?.cause != null) {
  188. yield(rootCause.cause!!)
  189. rootCause = rootCause.cause
  190. if (depth++ >= maxDepth) break
  191. }
  192. }
  193. public inline fun Throwable.findCause(maxDepth: Int = 20, filter: (Throwable) -> Boolean): Throwable? {
  194. var depth = 0
  195. var rootCause: Throwable? = this
  196. while (true) {
  197. if (rootCause?.cause === rootCause) return rootCause
  198. val current = rootCause?.cause ?: return null
  199. if (filter(current)) return current
  200. rootCause = rootCause.cause
  201. if (depth++ >= maxDepth) return null
  202. }
  203. }
  204. public inline fun Throwable.findCauseOrSelf(maxDepth: Int = 20, filter: (Throwable) -> Boolean): Throwable =
  205. findCause(maxDepth, filter) ?: this
  206. public fun String.capitalize(): String {
  207. return replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString() }
  208. }
  209. public fun String.truncated(length: Int, truncated: String = "..."): String {
  210. return if (this.length > length) {
  211. this.take(10) + truncated
  212. } else this
  213. }
  214. /**
  215. * Similar to [run] bot with [Unit] return type.
  216. *
  217. * You should not reference to [T] directly in the [block].
  218. */
  219. // can convert to contextual receiver in the future, or there might be a stdlib function which we can delegate to.
  220. public inline fun <T> T.context(block: T.() -> Unit) {
  221. contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
  222. return block()
  223. }