diff --git a/CHANGELOG.md b/CHANGELOG.md index f9fef295aa3..58b1ffb5b8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## 0.7.1 +* `FSM`: + * `Common` + * New manager `DefaultStatesManager` with `DefaultStatesManagerRepo` for abstraction of manager and storing of + data info + ## 0.7.0 **THIS VERSION HAS MIGRATED FROM KOTLINX DATETIME TO KORLIBS KLOCK. CAREFUL** diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt index 221d9c48ef9..dda9ae1729e 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/DefaultStatesManager.kt @@ -6,35 +6,42 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -interface DefaultStatesManagerStatesRepo { +interface DefaultStatesManagerRepo { /** * Must save [state] as current state of chain with [State.context] of [state] */ - suspend fun newState(state: State) - /** - * Must save [to] as instead of [from]. In case when they have different [State.context] - [from] must be just - * removed like with [removeState] - */ - suspend fun updateState(from: State, to: State) + suspend fun set(state: State) /** * Remove exactly [state]. In case if internally [State.context] is busy with different [State], that [State] should * NOT be removed */ suspend fun removeState(state: State) /** - * @return current list of available and saved states + * @return Current list of available and saved states */ suspend fun getStates(): List + + /** + * @return Current state by [context] + */ + suspend fun getContextState(context: Any): State? + + /** + * @return Current state by [context] + */ + suspend fun contains(context: Any): Boolean } /** * @param onContextsConflictResolver Receive old [State], new one and the state currently placed on new [State.context] * key. In case when this callback will returns true, the state placed on [State.context] of new will be replaced by * new state by using [endChain] with that state + * @param repo This repo will be used as repository for storing states. All operations with this repo will happen BEFORE + * any event will be sent to [onChainStateUpdated], [onStartChain] or [onEndChain]. */ class DefaultStatesManager( private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true }, - private val repo: DefaultStatesManagerStatesRepo + private val repo: DefaultStatesManagerRepo ) : StatesManager { private val _onChainStateUpdated = MutableSharedFlow>(0) override val onChainStateUpdated: Flow> = _onChainStateUpdated.asSharedFlow() @@ -43,22 +50,22 @@ class DefaultStatesManager( private val _onEndChain = MutableSharedFlow(0) override val onEndChain: Flow = _onEndChain.asSharedFlow() - private val contextsToStates = mutableMapOf() private val mapMutex = Mutex() override suspend fun update(old: State, new: State) = mapMutex.withLock { + val stateByOldContext: State? = repo.getContextState(old.context) when { - contextsToStates[old.context] != old -> return@withLock - old.context == new.context || !contextsToStates.containsKey(new.context) -> { - contextsToStates[old.context] = new + stateByOldContext != old -> return@withLock + stateByOldContext == null || old.context == new.context -> { + repo.set(new) _onChainStateUpdated.emit(old to new) } else -> { - val stateOnNewOneContext = contextsToStates.getValue(new.context) - if (onContextsConflictResolver(old, new, stateOnNewOneContext)) { - endChainWithoutLock(stateOnNewOneContext) - contextsToStates.remove(old.context) - contextsToStates[new.context] = new + val stateOnNewOneContext = repo.getContextState(new.context) + if (stateOnNewOneContext == null || onContextsConflictResolver(old, new, stateOnNewOneContext)) { + stateOnNewOneContext ?.let { endChainWithoutLock(it) } + repo.removeState(old) + repo.set(new) _onChainStateUpdated.emit(old to new) } } @@ -66,15 +73,15 @@ class DefaultStatesManager( } override suspend fun startChain(state: State) = mapMutex.withLock { - if (!contextsToStates.containsKey(state.context)) { - contextsToStates[state.context] = state + if (!repo.contains(state.context)) { + repo.set(state) _onStartChain.emit(state) } } private suspend fun endChainWithoutLock(state: State) { - if (contextsToStates[state.context] == state) { - contextsToStates.remove(state.context) + if (repo.getContextState(state.context) == state) { + repo.removeState(state) _onEndChain.emit(state) } } @@ -85,6 +92,6 @@ class DefaultStatesManager( } } - override suspend fun getActiveStates(): List = contextsToStates.values.toList() + override suspend fun getActiveStates(): List = repo.getStates() } diff --git a/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedDefaultStatesManagerStatesRepo.kt b/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedDefaultStatesManagerRepo.kt similarity index 61% rename from fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedDefaultStatesManagerStatesRepo.kt rename to fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedDefaultStatesManagerRepo.kt index 73ad2929041..68b3b687db6 100644 --- a/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedDefaultStatesManagerStatesRepo.kt +++ b/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedDefaultStatesManagerRepo.kt @@ -1,24 +1,17 @@ package dev.inmo.micro_utils.fsm.repos.common import dev.inmo.micro_utils.fsm.common.State -import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerStatesRepo +import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerRepo import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.pagination.getAll -class KeyValueBasedDefaultStatesManagerStatesRepo( +class KeyValueBasedDefaultStatesManagerRepo( private val keyValueRepo: KeyValueRepo -) : DefaultStatesManagerStatesRepo { - override suspend fun newState(state: State) { +) : DefaultStatesManagerRepo { + override suspend fun set(state: State) { keyValueRepo.set(state.context, state) } - override suspend fun updateState(from: State, to: State) { - if (from.context != to.context && keyValueRepo.get(from.context) == from) { - keyValueRepo.unset(from.context) - } - keyValueRepo.set(to.context, to) - } - override suspend fun removeState(state: State) { if (keyValueRepo.get(state.context) == state) { keyValueRepo.unset(state.context) @@ -26,4 +19,7 @@ class KeyValueBasedDefaultStatesManagerStatesRepo( } override suspend fun getStates(): List = keyValueRepo.getAll { keys(it) }.map { it.second } + override suspend fun getContextState(context: Any): State? = keyValueRepo.get(context) + + override suspend fun contains(context: Any): Boolean = keyValueRepo.contains(context) } diff --git a/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedStatesManager.kt b/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedStatesManager.kt index bffe06b4180..136bfd45c9c 100644 --- a/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedStatesManager.kt +++ b/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedStatesManager.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -@Deprecated +@Deprecated("Replace with DefaultStatesManager and KeyValueBasedDefaultStatesManagerRepo") class KeyValueBasedStatesManager( private val keyValueRepo: KeyValueRepo, private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true }