mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-11-17 13:53:49 +00:00
DefaultStatesManager
This commit is contained in:
parent
fab789d9c0
commit
119a0588cc
@ -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**
|
||||
|
@ -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()
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -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 }
|
||||
|
Loading…
Reference in New Issue
Block a user