package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.supervisorScope import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext typealias ExceptionHandler = suspend (Throwable) -> T /** * This instance will be used in all calls of [safely] where exception handler has not been passed */ var defaultSafelyExceptionHandler: ExceptionHandler = { throw it } class SafelyExceptionHandlerKey : CoroutineContext.Key> inline fun safelyExceptionHandlerKey() = SafelyExceptionHandlerKey() class SafelyExceptionHandler( val handler: ExceptionHandler ) : CoroutineContext.Element { override val key: CoroutineContext.Key<*> = safelyExceptionHandlerKey() } /** * It will run [block] inside of [supervisorScope] to avoid problems with catching of exceptions * * @param [onException] Will be called when happen exception inside of [block]. By default will throw exception - this * exception will be available for catching * * @see defaultSafelyExceptionHandler * @see safelyWithoutExceptions */ suspend inline fun safely( noinline onException: ExceptionHandler = defaultSafelyExceptionHandler, noinline block: suspend CoroutineScope.() -> T ): T { return try { supervisorScope(block) } catch (e: Throwable) { val handler = if (onException == defaultSafelyExceptionHandler) { coroutineContext[safelyExceptionHandlerKey()] ?.handler ?: onException } else { onException } handler(e) } } /** * Shortcut for [safely] without exception handler (instead of this you will receive null as a result) */ suspend inline fun safelyWithoutExceptions( noinline block: suspend CoroutineScope.() -> T ): T? = safely({ null }, block)