package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.* class DeferredAction( val deferred: Deferred, val callback: suspend (T) -> O ) { suspend operator fun invoke() = callback(deferred.await()) } class DoWithFirstBuilder( private val scope: CoroutineScope ) { private val deferreds = mutableListOf>() operator fun plus(block: suspend CoroutineScope.() -> T) { deferreds.add(scope.async(start = CoroutineStart.LAZY, block = block)) } inline fun add(noinline block: suspend CoroutineScope.() -> T) = plus(block) inline fun include(noinline block: suspend CoroutineScope.() -> T) = plus(block) fun build() = deferreds.toList() } fun Deferred.buildAction(callback: suspend (T) -> O) = DeferredAction(this, callback) suspend fun Iterable>.invokeFirstOf( scope: CoroutineScope, cancelOnResult: Boolean = true ): O { return map { it.deferred }.awaitFirstWithDeferred(scope, cancelOnResult).let { result -> first { it.deferred == result.first }.invoke() } } suspend fun invokeFirstOf( scope: CoroutineScope, vararg variants: DeferredAction<*, O>, cancelOnResult: Boolean = true ): O = variants.toList().invokeFirstOf(scope, cancelOnResult) suspend fun Iterable>.invokeOnFirst( scope: CoroutineScope, cancelOnResult: Boolean = true, callback: suspend (T) -> O ): O = map { it.buildAction(callback) }.invokeFirstOf(scope, cancelOnResult) suspend fun CoroutineScope.invokeOnFirstOf( cancelOnResult: Boolean = true, block: DoWithFirstBuilder.() -> Unit, callback: suspend (T) -> O ) = firstOf( DoWithFirstBuilder(this).apply(block).build(), cancelOnResult ).let { callback(it) } suspend fun invokeOnFirst( scope: CoroutineScope, vararg variants: Deferred, cancelOnResult: Boolean = true, callback: suspend (T) -> O ): O = variants.toList().invokeOnFirst(scope, cancelOnResult, callback) suspend fun CoroutineScope.firstOf( variants: Iterable>, cancelOnResult: Boolean = true ) = variants.invokeOnFirst(this, cancelOnResult) { it } suspend fun CoroutineScope.firstOf( cancelOnResult: Boolean = true, block: DoWithFirstBuilder.() -> Unit ) = firstOf( DoWithFirstBuilder(this).apply(block).build(), cancelOnResult ) suspend fun CoroutineScope.firstOf( vararg variants: Deferred, cancelOnResult: Boolean = true ) = firstOf(variants.toList(), cancelOnResult) suspend fun List>.first( scope: CoroutineScope, cancelOnResult: Boolean = true ) = scope.firstOf(this, cancelOnResult)