mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-11-03 21:51:59 +00:00 
			
		
		
		
	DefaultStatesManager
This commit is contained in:
		@@ -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 }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user