2020-09-24 02:27:16 +00:00
package dev.inmo.micro_utils.coroutines
2020-12-22 17:17:28 +00:00
import kotlinx.coroutines.*
2020-12-22 09:16:06 +00:00
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext
2020-09-24 02:27:16 +00:00
typealias ExceptionHandler < T > = suspend ( Throwable ) -> T
2020-12-22 09:16:06 +00:00
/ * *
* This instance will be used in all calls of [ safely ] where exception handler has not been passed
* /
var defaultSafelyExceptionHandler : ExceptionHandler < Nothing > = { throw it }
2020-12-22 09:21:25 +00:00
/ * *
* Key for [ SafelyExceptionHandler ] which can be used in [ CoroutineContext . get ] to get current default
* [ SafelyExceptionHandler ]
* /
2020-12-22 17:17:28 +00:00
@Deprecated ( " This method will be useless in future major update " , ReplaceWith ( " ContextSafelyExceptionHandlerKey " , " dev.inmo.micro_utils.coroutines.ContextSafelyExceptionHandler " ) )
2020-12-22 16:45:43 +00:00
class SafelyExceptionHandlerKey < T > : CoroutineContext . Key < SafelyExceptionHandler < T > >
2020-12-22 09:21:25 +00:00
/ * *
* Shortcut for creating instance of [ SafelyExceptionHandlerKey ]
* /
@Suppress ( " NOTHING_TO_INLINE " )
2020-12-22 17:17:28 +00:00
@Deprecated ( " This method will be useless in future major update " , ReplaceWith ( " ContextSafelyExceptionHandlerKey " , " dev.inmo.micro_utils.coroutines.ContextSafelyExceptionHandler " ) )
2020-12-22 16:45:43 +00:00
inline fun < T > safelyExceptionHandlerKey ( ) = SafelyExceptionHandlerKey < T > ( )
2020-12-22 09:21:25 +00:00
/ * *
* Wrapper for [ ExceptionHandler ] which can be used in [ CoroutineContext ] to set local ( for [ CoroutineContext ] ) default
* [ ExceptionHandler ] . To get it use [ CoroutineContext . get ] with key [ SafelyExceptionHandlerKey ]
*
* @see SafelyExceptionHandlerKey
* @see ExceptionHandler
* /
2020-12-22 17:17:28 +00:00
@Deprecated ( " This method will be useless in future major update " , ReplaceWith ( " ContextSafelyExceptionHandler " , " dev.inmo.micro_utils.coroutines.ContextSafelyExceptionHandler " ) )
2020-12-22 16:45:43 +00:00
class SafelyExceptionHandler < T > (
val handler : ExceptionHandler < T >
2020-12-22 09:16:06 +00:00
) : CoroutineContext . Element {
2020-12-22 16:45:43 +00:00
override val key : CoroutineContext . Key < * > = safelyExceptionHandlerKey < T > ( )
2020-12-22 09:16:06 +00:00
}
2020-12-22 17:17:28 +00:00
/ * *
* This key can ( and will ) be used to get [ ContextSafelyExceptionHandler ] from [ coroutineContext ] of suspend functions
* and in [ ContextSafelyExceptionHandler ] for defining of its [ CoroutineContext . Element . key ]
*
* @see safelyWithContextExceptionHandler
* @see ContextSafelyExceptionHandler
* /
object ContextSafelyExceptionHandlerKey : CoroutineContext . Key < ContextSafelyExceptionHandler >
/ * *
* [ ExceptionHandler ] wrapper which was created to make possible to use [ handler ] across all coroutines calls
*
* @see safelyWithContextExceptionHandler
* @see ContextSafelyExceptionHandlerKey
* /
class ContextSafelyExceptionHandler (
val handler : ExceptionHandler < Unit >
) : CoroutineContext . Element {
override val key : CoroutineContext . Key < * >
get ( ) = ContextSafelyExceptionHandlerKey
}
/ * *
* @return [ ContextSafelyExceptionHandler ] from [ coroutineContext ] by key [ ContextSafelyExceptionHandlerKey ] if
* exists
*
* @see ContextSafelyExceptionHandler
* @see ContextSafelyExceptionHandlerKey
* /
suspend inline fun contextSafelyExceptionHandler ( ) = coroutineContext [ ContextSafelyExceptionHandlerKey ]
/ * *
* This method will set new [ coroutineContext ] with [ ContextSafelyExceptionHandler ] . In case if [ coroutineContext ]
* already contains [ ContextSafelyExceptionHandler ] , [ ContextSafelyExceptionHandler . handler ] will be used BEFORE
* [ contextExceptionHandler ] in case of exception .
*
* After all , will be called [ withContext ] method with created [ ContextSafelyExceptionHandler ] and block which will call
* [ safely ] method with [ safelyExceptionHandler ] as onException parameter and [ block ] as execution block
* /
suspend fun < T > safelyWithContextExceptionHandler (
contextExceptionHandler : ExceptionHandler < Unit > ,
safelyExceptionHandler : ExceptionHandler < T > = defaultSafelyExceptionHandler ,
block : suspend CoroutineScope . ( ) -> T
) : T {
val contextSafelyExceptionHandler = contextSafelyExceptionHandler ( ) ?. handler ?. let { oldHandler ->
ContextSafelyExceptionHandler {
oldHandler ( it )
contextExceptionHandler ( it )
}
} ?: ContextSafelyExceptionHandler ( contextExceptionHandler )
return withContext ( contextSafelyExceptionHandler ) {
safely ( safelyExceptionHandler , block )
}
}
2020-09-24 02:27:16 +00:00
/ * *
* It will run [ block ] inside of [ supervisorScope ] to avoid problems with catching of exceptions
*
2020-12-22 09:21:25 +00:00
* Priorities of [ ExceptionHandler ] s :
*
* * [ onException ] In case if custom ( will be used anyway if not [ defaultSafelyExceptionHandler ] )
* * [ CoroutineContext . get ] with [ SafelyExceptionHandlerKey ] as key
* * [ defaultSafelyExceptionHandler ]
*
2020-09-24 02:27:16 +00:00
* @param [ onException ] Will be called when happen exception inside of [ block ] . By default will throw exception - this
* exception will be available for catching
2020-12-22 09:16:06 +00:00
*
* @see defaultSafelyExceptionHandler
* @see safelyWithoutExceptions
2020-12-22 17:17:28 +00:00
* @see safelyWithContextExceptionHandler
2020-09-24 02:27:16 +00:00
* /
suspend inline fun < T > safely (
2020-12-22 16:45:43 +00:00
noinline onException : ExceptionHandler < T > = defaultSafelyExceptionHandler ,
noinline block : suspend CoroutineScope . ( ) -> T
2020-09-24 02:27:16 +00:00
) : T {
return try {
2020-12-22 16:45:43 +00:00
supervisorScope ( block )
2020-12-22 16:45:36 +00:00
} catch ( e : Throwable ) {
2020-12-22 17:17:28 +00:00
coroutineContext [ ContextSafelyExceptionHandlerKey ] ?. handler ?. invoke ( e )
onException ( e )
2020-09-24 02:27:16 +00:00
}
}
2020-11-02 15:25:33 +00:00
/ * *
* Shortcut for [ safely ] without exception handler ( instead of this you will receive null as a result )
* /
suspend inline fun < T > safelyWithoutExceptions (
noinline block : suspend CoroutineScope . ( ) -> T
) : T ? = safely ( { null } , block )