DefaultStatesManager

This commit is contained in:
InsanusMokrassar 2021-10-06 13:30:25 +06:00
parent fab789d9c0
commit 119a0588cc
4 changed files with 43 additions and 35 deletions
CHANGELOG.md
fsm
common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers
repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common

View File

@ -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**

View File

@ -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<State>
/**
* @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<Pair<State, State>>(0)
override val onChainStateUpdated: Flow<Pair<State, State>> = _onChainStateUpdated.asSharedFlow()
@ -43,22 +50,22 @@ class DefaultStatesManager(
private val _onEndChain = MutableSharedFlow<State>(0)
override val onEndChain: Flow<State> = _onEndChain.asSharedFlow()
private val contextsToStates = mutableMapOf<Any, State>()
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<State> = contextsToStates.values.toList()
override suspend fun getActiveStates(): List<State> = repo.getStates()
}

View File

@ -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<Any, State>
) : 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<State> = 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)
}

View File

@ -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<Any, State>,
private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true }