From a74f061b02e634ce97686d17cc20a6a062008cfd Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 26 Dec 2021 20:02:18 +0600 Subject: [PATCH 1/7] start 0.8.8 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12af78d0f75..c11a986e387 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.8.8 + ## 0.8.7 * `Ktor`: diff --git a/gradle.properties b/gradle.properties index d8a06549686..ceb04619807 100644 --- a/gradle.properties +++ b/gradle.properties @@ -45,5 +45,5 @@ dokka_version=1.5.31 # Project data group=dev.inmo -version=0.8.7 -android_code_version=87 +version=0.8.8 +android_code_version=88 From 3d2196e35da5e4f3b539c11a04148e758a56e4d9 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 26 Dec 2021 20:07:13 +0600 Subject: [PATCH 2/7] update dependencies (which possible) --- .github/workflows/dokka_push.yml | 4 ++-- .github/workflows/packages_push.yml | 4 ++-- CHANGELOG.md | 4 ++++ gradle.properties | 8 ++++---- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dokka_push.yml b/.github/workflows/dokka_push.yml index 2fefc23dbf6..517ca2ae083 100644 --- a/.github/workflows/dokka_push.yml +++ b/.github/workflows/dokka_push.yml @@ -11,9 +11,9 @@ jobs: - uses: actions/setup-java@v1 with: java-version: 1.8 - - name: Fix android 31.0.0 dx + - name: Fix android 32.0.0 dx continue-on-error: true - run: cd /usr/local/lib/android/sdk/build-tools/31.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar + run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar - name: Build run: ./gradlew dokkaHtml - name: Publish KDocs diff --git a/.github/workflows/packages_push.yml b/.github/workflows/packages_push.yml index 0991d752c1a..8a161775c4a 100644 --- a/.github/workflows/packages_push.yml +++ b/.github/workflows/packages_push.yml @@ -9,9 +9,9 @@ jobs: - uses: actions/setup-java@v1 with: java-version: 1.8 - - name: Fix android 31.0.0 dx + - name: Fix android 32.0.0 dx continue-on-error: true - run: cd /usr/local/lib/android/sdk/build-tools/31.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar + run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar - name: Rewrite version run: | branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`" diff --git a/CHANGELOG.md b/CHANGELOG.md index c11a986e387..d036e4e6f30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 0.8.8 +* `Versions`: + * `AppCompat`: `1.3.1` -> `1.4.0` + * Android Compile SDK: `31.0.0` -> `32.0.0` + ## 0.8.7 * `Ktor`: diff --git a/gradle.properties b/gradle.properties index ceb04619807..df127369aba 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,12 +24,12 @@ uuidVersion=0.3.1 core_ktx_version=1.7.0 androidx_recycler_version=1.2.1 -appcompat_version=1.3.1 +appcompat_version=1.4.0 android_minSdkVersion=19 -android_compileSdkVersion=31 -android_buildToolsVersion=31.0.0 -dexcount_version=3.0.0 +android_compileSdkVersion=32 +android_buildToolsVersion=32.0.0 +dexcount_version=3.0.1 junit_version=4.12 test_ext_junit_version=1.1.2 espresso_core=3.3.0 From f037ce4371e864213f76c0e686aae72033d59ac7 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 26 Dec 2021 21:06:26 +0600 Subject: [PATCH 3/7] updates in FSM --- CHANGELOG.md | 3 + .../dev/inmo/micro_utils/common/Optional.kt | 27 ++++++-- fsm/common/build.gradle | 1 + .../micro_utils/fsm/common/StatesMachine.kt | 65 ++++++++++++++----- .../fsm/common/UpdatableStatesMachine.kt | 57 ++++++++++++++++ .../micro_utils/fsm/common/dsl/FSMBuilder.kt | 15 +++-- 6 files changed, 140 insertions(+), 28 deletions(-) create mode 100644 fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index d036e4e6f30..c255949536c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ * `Versions`: * `AppCompat`: `1.3.1` -> `1.4.0` * Android Compile SDK: `31.0.0` -> `32.0.0` +* `FSM`: + * `DefaultStatesMachine` now is extendable + * New type `UpdatableStatesMachine` with default realization`DefaultUpdatableStatesMachine` ## 0.8.7 diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Optional.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Optional.kt index 3c48c1b4da9..0c3a67521d0 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Optional.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Optional.kt @@ -21,8 +21,10 @@ import kotlinx.serialization.Serializable */ @Serializable data class Optional internal constructor( - internal val data: T?, - internal val dataPresented: Boolean + @Warning("It is unsafe to use this data directly") + val data: T?, + @Warning("It is unsafe to use this data directly") + val dataPresented: Boolean ) { companion object { /** @@ -42,17 +44,31 @@ inline val T.optional /** * Will call [block] when data presented ([Optional.dataPresented] == true) */ -fun Optional.onPresented(block: (T) -> Unit): Optional = apply { +inline fun Optional.onPresented(block: (T) -> Unit): Optional = apply { if (dataPresented) { @Suppress("UNCHECKED_CAST") block(data as T) } } +/** + * Will call [block] when data presented ([Optional.dataPresented] == true) + */ +inline fun Optional.mapOnPresented(block: (T) -> R): R? = run { + if (dataPresented) { @Suppress("UNCHECKED_CAST") block(data as T) } else null +} + /** * Will call [block] when data absent ([Optional.dataPresented] == false) */ -fun Optional.onAbsent(block: () -> Unit): Optional = apply { +inline fun Optional.onAbsent(block: () -> Unit): Optional = apply { if (!dataPresented) { block() } } +/** + * Will call [block] when data presented ([Optional.dataPresented] == true) + */ +inline fun Optional.mapOnAbsent(block: () -> R): R? = run { + if (!dataPresented) { @Suppress("UNCHECKED_CAST") block() } else null +} + /** * Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or null otherwise */ @@ -67,9 +83,10 @@ fun Optional.dataOrThrow(throwable: Throwable) = if (dataPresented) @Supp /** * Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or call [block] and returns the result of it */ -fun Optional.dataOrElse(block: () -> T) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else block() +inline fun Optional.dataOrElse(block: () -> T) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else block() /** * Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or call [block] and returns the result of it */ +@Deprecated("dataOrElse now is inline", ReplaceWith("dataOrElse", "dev.inmo.micro_utils.common.dataOrElse")) suspend fun Optional.dataOrElseSuspendable(block: suspend () -> T) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else block() diff --git a/fsm/common/build.gradle b/fsm/common/build.gradle index 854f51c5dfb..6b6b040f3cf 100644 --- a/fsm/common/build.gradle +++ b/fsm/common/build.gradle @@ -10,6 +10,7 @@ kotlin { sourceSets { commonMain { dependencies { + api project(":micro_utils.common") api project(":micro_utils.coroutines") } } diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesMachine.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesMachine.kt index d470ff12d5c..4a72a715784 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesMachine.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesMachine.kt @@ -1,8 +1,11 @@ package dev.inmo.micro_utils.fsm.common -import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions -import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions +import dev.inmo.micro_utils.common.Optional +import dev.inmo.micro_utils.common.onPresented +import dev.inmo.micro_utils.coroutines.* import kotlinx.coroutines.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock /** * Default [StatesMachine] may [startChain] and use inside logic for handling [State]s. By default you may use @@ -42,17 +45,53 @@ interface StatesMachine : StatesHandler { /** * Default realization of [StatesMachine]. It uses [statesManager] for incapsulation of [State]s storing and contexts - * resolving, and uses [launchStateHandling] for [State] handling + * resolving, and uses [launchStateHandling] for [State] handling. + * + * This class suppose to be extended in case you wish some custom behaviour inside of [launchStateHandling], for example */ -class DefaultStatesMachine ( - private val statesManager: StatesManager, - private val handlers: List> +open class DefaultStatesMachine ( + protected val statesManager: StatesManager, + protected val handlers: List>, ) : StatesMachine { /** * Will call [launchStateHandling] for state handling */ override suspend fun StatesMachine.handleState(state: T): T? = launchStateHandling(state, handlers) + /** + * This + */ + protected val statesJobs = mutableMapOf() + protected val statesJobsMutex = Mutex() + + protected open suspend fun performUpdate(state: T) { + val newState = launchStateHandling(state, handlers) + if (newState != null) { + statesManager.update(state, newState) + } else { + statesManager.endChain(state) + } + } + + open suspend fun performStateUpdate(previousState: Optional, actualState: T, scope: CoroutineScope) { + statesJobsMutex.withLock { + statesJobs[actualState] ?.cancel() + statesJobs[actualState] = scope.launch { + performUpdate(actualState) + }.also { job -> + job.invokeOnCompletion { _ -> + scope.launch { + statesJobsMutex.withLock { + if (statesJobs[actualState] == job) { + statesJobs.remove(actualState) + } + } + } + } + } + } + } + /** * Launch handling of states. On [statesManager] [StatesManager.onStartChain], * [statesManager] [StatesManager.onChainStateUpdated] will be called lambda with performing of state. If @@ -60,23 +99,15 @@ class DefaultStatesMachine ( * [StatesManager.endChain]. */ override fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions { - val statePerformer: suspend (T) -> Unit = { state: T -> - val newState = launchStateHandling(state, handlers) - if (newState != null) { - statesManager.update(state, newState) - } else { - statesManager.endChain(state) - } - } statesManager.onStartChain.subscribeSafelyWithoutExceptions(this) { - launch { statePerformer(it) } + launch { performStateUpdate(Optional.absent(), it, scope.LinkedSupervisorScope()) } } statesManager.onChainStateUpdated.subscribeSafelyWithoutExceptions(this) { - launch { statePerformer(it.second) } + launch { performStateUpdate(Optional.presented(it.first), it.second, scope.LinkedSupervisorScope()) } } statesManager.getActiveStates().forEach { - launch { statePerformer(it) } + launch { performStateUpdate(Optional.absent(), it, scope.LinkedSupervisorScope()) } } } diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt new file mode 100644 index 00000000000..92a47551d41 --- /dev/null +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt @@ -0,0 +1,57 @@ +package dev.inmo.micro_utils.fsm.common + +import dev.inmo.micro_utils.common.* +import kotlinx.coroutines.* +import kotlinx.coroutines.sync.withLock + +/** + * This extender of [StatesMachine] interface declare one new function [updateChain]. Realizations of this interface + * must be able to perform update of chain in internal [StatesManager] + */ +interface UpdatableStatesMachine : StatesMachine { + /** + * Update chain with current state equal to [currentState] with [newState]. Behaviour of this update preforming + * in cases when [currentState] does not exist in [StatesManager] must be declared inside of realization of + * [StatesManager.update] function + */ + suspend fun updateChain(currentState: T, newState: T) +} + +open class DefaultUpdatableStatesMachine( + statesManager: StatesManager, + handlers: List>, + +) : DefaultStatesMachine( + statesManager, + handlers +), UpdatableStatesMachine { + protected val jobsStates = mutableMapOf() + + override suspend fun performStateUpdate(previousState: Optional, actualState: T, scope: CoroutineScope) { + statesJobsMutex.withLock { + statesJobs[actualState] ?.cancel() + val job = previousState.mapOnPresented { + statesJobs.remove(it) + } ?: scope.launch { + performUpdate(actualState) + }.also { job -> + job.invokeOnCompletion { _ -> + scope.launch { + statesJobsMutex.withLock { + statesJobs.remove( + jobsStates[job] ?: return@withLock + ) + } + } + } + } + jobsStates.remove(job) + statesJobs[actualState] = job + jobsStates[job] = actualState + } + } + + override suspend fun updateChain(currentState: T, newState: T) { + statesManager.update(currentState, newState) + } +} diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt index 76786ecac6f..aa244eed616 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt @@ -7,6 +7,14 @@ import kotlin.reflect.KClass class FSMBuilder( var statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), + val fsmBuilder: (states: List>, defaultHandler: StatesHandler?) -> StatesMachine = { states, defaultHandler -> + StatesMachine( + statesManager, + states.let { list -> + defaultHandler ?.let { list + it.holder { true } } ?: list + } + ) + }, var defaultStateHandler: StatesHandler? = StatesHandler { null } ) { private var states = mutableListOf>() @@ -42,12 +50,7 @@ class FSMBuilder( add(filter, handler) } - fun build() = StatesMachine( - statesManager, - states.toList().let { list -> - defaultStateHandler ?.let { list + it.holder { true } } ?: list - } - ) + fun build() = fsmBuilder(states.toList(), defaultStateHandler) } fun buildFSM( From 4bda70268b0076219bbf084681ddde7e8eb09488 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 26 Dec 2021 21:07:40 +0600 Subject: [PATCH 4/7] small fix of style in DefaultUpdatableStatesMachine --- .../dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt index 92a47551d41..91cfb680d04 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt @@ -20,7 +20,6 @@ interface UpdatableStatesMachine : StatesMachine { open class DefaultUpdatableStatesMachine( statesManager: StatesManager, handlers: List>, - ) : DefaultStatesMachine( statesManager, handlers From eb78f21eecd38b19f59e4eb6f6dfbe6e255a5fbc Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 26 Dec 2021 21:21:40 +0600 Subject: [PATCH 5/7] fix FSMBuilder --- .../inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt index aa244eed616..d0378c88d0f 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt @@ -7,12 +7,10 @@ import kotlin.reflect.KClass class FSMBuilder( var statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), - val fsmBuilder: (states: List>, defaultHandler: StatesHandler?) -> StatesMachine = { states, defaultHandler -> + val fsmBuilder: (states: List>, statesManager: StatesManager) -> StatesMachine = { states, statesManager -> StatesMachine( statesManager, - states.let { list -> - defaultHandler ?.let { list + it.holder { true } } ?: list - } + states ) }, var defaultStateHandler: StatesHandler? = StatesHandler { null } @@ -50,7 +48,12 @@ class FSMBuilder( add(filter, handler) } - fun build() = fsmBuilder(states.toList(), defaultStateHandler) + fun build() = fsmBuilder( + states.toList().let { list -> + defaultStateHandler ?.let { list + it.holder { true } } ?: list + }, + statesManager + ) } fun buildFSM( From 494812a66086aacc6460ee1ca085fa620cc94587 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 26 Dec 2021 21:25:12 +0600 Subject: [PATCH 6/7] one more fix --- .../dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt index d0378c88d0f..0668693b956 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/dsl/FSMBuilder.kt @@ -7,7 +7,7 @@ import kotlin.reflect.KClass class FSMBuilder( var statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), - val fsmBuilder: (states: List>, statesManager: StatesManager) -> StatesMachine = { states, statesManager -> + val fsmBuilder: (statesManager: StatesManager, states: List>) -> StatesMachine = { statesManager, states -> StatesMachine( statesManager, states @@ -49,10 +49,10 @@ class FSMBuilder( } fun build() = fsmBuilder( + statesManager, states.toList().let { list -> defaultStateHandler ?.let { list + it.holder { true } } ?: list - }, - statesManager + } ) } From f419fd03d206cc247ff7dfbfad5d65ae1fd3f4b1 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 26 Dec 2021 22:00:26 +0600 Subject: [PATCH 7/7] small fix of performing state update for UpdatableStatesMachine --- .../dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt index 91cfb680d04..deadfb62324 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/UpdatableStatesMachine.kt @@ -28,7 +28,9 @@ open class DefaultUpdatableStatesMachine( override suspend fun performStateUpdate(previousState: Optional, actualState: T, scope: CoroutineScope) { statesJobsMutex.withLock { - statesJobs[actualState] ?.cancel() + if (previousState.dataOrNull() != actualState) { + statesJobs[actualState] ?.cancel() + } val job = previousState.mapOnPresented { statesJobs.remove(it) } ?: scope.launch {