diff --git a/CHANGELOG.md b/CHANGELOG.md index f40584597f2..76c4b9e9164 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 0.9.25 +* `Handlers`: + * Subproject has been created + ## 0.9.24 * `Ktor`: diff --git a/handlers/common/build.gradle b/handlers/common/build.gradle new file mode 100644 index 00000000000..6b6b040f3cf --- /dev/null +++ b/handlers/common/build.gradle @@ -0,0 +1,18 @@ +plugins { + id "org.jetbrains.kotlin.multiplatform" + id "org.jetbrains.kotlin.plugin.serialization" + id "com.android.library" +} + +apply from: "$mppProjectWithSerializationPresetPath" + +kotlin { + sourceSets { + commonMain { + dependencies { + api project(":micro_utils.common") + api project(":micro_utils.coroutines") + } + } + } +} diff --git a/handlers/common/src/commonMain/kotlin/dev/inmo/micro_utils/handlers/common/Handler.kt b/handlers/common/src/commonMain/kotlin/dev/inmo/micro_utils/handlers/common/Handler.kt new file mode 100644 index 00000000000..d70dfbd7294 --- /dev/null +++ b/handlers/common/src/commonMain/kotlin/dev/inmo/micro_utils/handlers/common/Handler.kt @@ -0,0 +1,6 @@ +package dev.inmo.micro_utils.handlers.common + +fun interface Handler { + suspend fun check(data: T): Boolean = true + suspend fun handle(data: T) +} diff --git a/handlers/common/src/commonMain/kotlin/dev/inmo/micro_utils/handlers/common/HandlersRegistrar.kt b/handlers/common/src/commonMain/kotlin/dev/inmo/micro_utils/handlers/common/HandlersRegistrar.kt new file mode 100644 index 00000000000..f5ca6de9bc9 --- /dev/null +++ b/handlers/common/src/commonMain/kotlin/dev/inmo/micro_utils/handlers/common/HandlersRegistrar.kt @@ -0,0 +1,72 @@ +package dev.inmo.micro_utils.handlers.common + +import dev.inmo.micro_utils.coroutines.* +import kotlinx.coroutines.* + +/** + * @param layers Will be used in [handle] to iterate over the handler and find layer which have at least one [Handler] + * to [Handler.handle] its incoming data + * @param defaultHandler If presented will be used in case where there are no any layer from [layers] with any handler + * to handle data inside of [handle] method + */ +class HandlersRegistrar( + private val layers: Iterable>>, + private val defaultHandler: Handler? = null +) : Handler { + /** + * Will iterate over the [layers]. On each layer (in face each [Iterable] of [Handler]s) ALL the handlers will be + * checked using [Handler.check]. In case if there are no any [Handler] on current layer with true as a result of + * [Handler.check], [HandlersRegistrar] will take the next level. + */ + override suspend fun check(data: T): Boolean { + val scope = CoroutineScope(currentCoroutineContext()) + return layers.any { + it.map { + scope.async { + safelyWithResult { + it.check(data) + }.getOrElse { false } + } + }.awaitAll().any() + } || (defaultHandler != null && defaultHandler.check(data)) + } + /** + * Will iterate over the [layers]. + * + * * On each layer (each one is an [Iterable] of [Handler]s) ALL the handlers will be checked using [Handler.check] + * and all the handlers returned true as a result of [Handler.check] will get call of [Handler.handle] inside of + * their async coroutine. + * * In case if there are no any [Handler] on current layer with true as a result of [Handler.check], + * [HandlersRegistrar] will take the next level. + * * In case if there are no any [Handler] on all the [layers] with true as a result of [Handler.check], + * [defaultHandler] will be used (with checking using [Handler.check] as well) + */ + override suspend fun handle(data: T) { + val scope = CoroutineScope(currentCoroutineContext()) + val notHandled = layers.none { + val launchedJobs = it.map { + scope.async { + val launch = safelyWithResult { + it.check(data) + }.getOrElse { false } + if (launch) { + scope.launchSafelyWithoutExceptions { + it.handle(data) + } + } else { + null + } + } + }.awaitAll().filterNotNull() + + launchedJobs.joinAll() + launchedJobs.isNotEmpty() + } + + if (!notHandled && defaultHandler != null && defaultHandler.check(data)) { + safelyWithoutExceptions { + defaultHandler.handle(data) + } + } + } +} diff --git a/handlers/common/src/main/AndroidManifest.xml b/handlers/common/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..098b777a02b --- /dev/null +++ b/handlers/common/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/settings.gradle b/settings.gradle index d7471ac4ccc..19184f52be6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -35,6 +35,8 @@ String[] includes = [ ":fsm:common", ":fsm:repos:common", + ":handlers:common", + ":dokka" ]