diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c19c45fe22..3465ef50216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.7.5 +## 0.8.0 * `Versions`: * `Klock`: `2.4.6` -> `2.4.7` @@ -9,6 +9,9 @@ * Type `Either` got its own serializer * `FSM`: * `Common`: + * Full rework of FSM: + * Now it is more flexible for checking of handler opportunity to handle state + * Now machine and states managers are type-oriented * Add opportunity for comfortable adding default state handler ## 0.7.4 diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/InMemoryStatesManager.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/InMemoryStatesManager.kt deleted file mode 100644 index d00bbbc42fe..00000000000 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/InMemoryStatesManager.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.inmo.micro_utils.fsm.common - -import dev.inmo.micro_utils.fsm.common.managers.InMemoryStatesManager - -@Deprecated("Replaced", ReplaceWith("InMemoryStatesManager", "dev.inmo.micro_utils.fsm.common.managers.InMemoryStatesManager")) -typealias InMemoryStatesManager = InMemoryStatesManager diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesHandler.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesHandler.kt index a64f5133d96..e37c40cd6c8 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesHandler.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesHandler.kt @@ -3,10 +3,10 @@ package dev.inmo.micro_utils.fsm.common /** * Default realization of states handler */ -fun interface StatesHandler { +fun interface StatesHandler { /** * Main handling of [state]. In case when this [state] leads to another [State] and [handleState] returns not null * [State] it is assumed that chain is not completed. */ - suspend fun StatesMachine.handleState(state: I): State? + suspend fun StatesMachine.handleState(state: I): O? } diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesHandlerHolder.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesHandlerHolder.kt index b849ba8462d..17bc5f9f41e 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesHandlerHolder.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesHandlerHolder.kt @@ -2,37 +2,67 @@ package dev.inmo.micro_utils.fsm.common import kotlin.reflect.KClass +/** + * Define checkable holder which can be used to precheck that this handler may handle incoming [State] + */ +interface CheckableHandlerHolder : StatesHandler { + suspend fun checkHandleable(state: O): Boolean +} + /** * Default realization of [StatesHandler]. It will incapsulate checking of [State] type in [checkHandleable] and class * casting in [handleState] */ -class StatesHandlerHolder( - private val inputKlass: KClass, - private val strict: Boolean = false, - private val delegateTo: StatesHandler -) : StatesHandler { +class CustomizableHandlerHolder( + private val delegateTo: StatesHandler, + private val filter: suspend (state: O) -> Boolean +) : CheckableHandlerHolder { /** * Checks that [state] can be handled by [delegateTo]. Under the hood it will check exact equality of [state] * [KClass] and use [KClass.isInstance] of [inputKlass] if [strict] == false */ - fun checkHandleable(state: State) = state::class == inputKlass || (!strict && inputKlass.isInstance(state)) + override suspend fun checkHandleable(state: O) = filter(state) /** * Calls [delegateTo] method [StatesHandler.handleState] with [state] casted to [I]. Use [checkHandleable] * to be sure that this [StatesHandlerHolder] will be able to handle [state] */ - override suspend fun StatesMachine.handleState(state: State): State? { - return delegateTo.run { handleState(state as I) } + override suspend fun StatesMachine.handleState(state: I): O? { + return delegateTo.run { handleState(state) } } } -@Deprecated("Renamed", ReplaceWith("StatesHandlerHolder")) -typealias StateHandlerHolder = StatesHandlerHolder +fun StateHandlerHolder( + inputKlass: KClass, + strict: Boolean = false, + delegateTo: StatesHandler +) = CustomizableHandlerHolder( + StatesHandler { + delegateTo.run { handleState(it as I) } + }, + if (strict) { + { it::class == inputKlass } + } else { + { inputKlass.isInstance(it) } + } +) -inline fun StatesHandler.holder( +inline fun StateHandlerHolder( + strict: Boolean = false, + delegateTo: StatesHandler +) = StateHandlerHolder(I::class, strict, delegateTo) + +inline fun StatesHandler.holder( strict: Boolean = true -) = StatesHandlerHolder( - T::class, +) = StateHandlerHolder( + I::class, strict, this ) + +inline fun StatesHandler.holder( + noinline filter: suspend (state: State) -> Boolean +) = CustomizableHandlerHolder( + { this@holder.run { handleState(it as I) } }, + filter +) 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 3dc9ef89cfa..dcfe420aa82 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 @@ -3,21 +3,21 @@ package dev.inmo.micro_utils.fsm.common import dev.inmo.micro_utils.coroutines.* import kotlinx.coroutines.* -private suspend fun StatesMachine.launchStateHandling( - state: State, - handlers: List> -): State? { - return handlers.firstOrNull { it.checkHandleable(state) } ?.run { - handleState(state) - } -} - /** * Default [StatesMachine] may [startChain] and use inside logic for handling [State]s. By default you may use * [DefaultStatesMachine] or build it with [dev.inmo.micro_utils.fsm.common.dsl.buildFSM]. Implementers MUST NOT start * handling until [start] method will be called */ -interface StatesMachine : StatesHandler { +interface StatesMachine : StatesHandler { + suspend fun launchStateHandling( + state: T, + handlers: List> + ): T? { + return handlers.firstOrNull { it.checkHandleable(state) } ?.run { + handleState(state) + } + } + /** * Starts handling of [State]s */ @@ -26,15 +26,15 @@ interface StatesMachine : StatesHandler { /** * Start chain of [State]s witn [state] */ - suspend fun startChain(state: State) + suspend fun startChain(state: T) companion object { /** * Creates [DefaultStatesMachine] */ - operator fun invoke( - statesManager: StatesManager, - handlers: List> + operator fun invoke( + statesManager: StatesManager, + handlers: List> ) = DefaultStatesMachine(statesManager, handlers) } } @@ -43,14 +43,14 @@ 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 */ -class DefaultStatesMachine ( - private val statesManager: StatesManager, - private val handlers: List> -) : StatesMachine { +class DefaultStatesMachine ( + private val statesManager: StatesManager, + private val handlers: List> +) : StatesMachine { /** * Will call [launchStateHandling] for state handling */ - override suspend fun StatesMachine.handleState(state: State): State? = launchStateHandling(state, handlers) + override suspend fun StatesMachine.handleState(state: T): T? = launchStateHandling(state, handlers) /** * Launch handling of states. On [statesManager] [StatesManager.onStartChain], @@ -59,7 +59,7 @@ class DefaultStatesMachine ( * [StatesManager.endChain]. */ override fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions { - val statePerformer: suspend (State) -> Unit = { state: State -> + val statePerformer: suspend (T) -> Unit = { state: T -> val newState = launchStateHandling(state, handlers) if (newState != null) { statesManager.update(state, newState) @@ -82,7 +82,7 @@ class DefaultStatesMachine ( /** * Just calls [StatesManager.startChain] of [statesManager] */ - override suspend fun startChain(state: State) { + override suspend fun startChain(state: T) { statesManager.startChain(state) } } diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesManager.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesManager.kt index 6b341aafa60..7ad305f338b 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesManager.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesManager.kt @@ -2,29 +2,29 @@ package dev.inmo.micro_utils.fsm.common import kotlinx.coroutines.flow.* -interface StatesManager { - val onChainStateUpdated: Flow> - val onStartChain: Flow - val onEndChain: Flow +interface StatesManager { + val onChainStateUpdated: Flow> + val onStartChain: Flow + val onEndChain: Flow /** * Must set current set using [State.context] */ - suspend fun update(old: State, new: State) + suspend fun update(old: T, new: T) /** * Starts chain with [state] as first [State]. May returns false in case of [State.context] of [state] is already * busy by the other [State] */ - suspend fun startChain(state: State) + suspend fun startChain(state: T) /** * Ends chain with context from [state]. In case when [State.context] of [state] is absent, [state] should be just * ignored */ - suspend fun endChain(state: State) + suspend fun endChain(state: T) - suspend fun getActiveStates(): List + suspend fun getActiveStates(): List } 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 11d39ec0752..aadb53ea5b9 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 @@ -4,36 +4,47 @@ import dev.inmo.micro_utils.fsm.common.* import dev.inmo.micro_utils.fsm.common.managers.* import kotlin.reflect.KClass -class FSMBuilder( - var statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), - var defaultStateHandler: StatesHandler? = StatesHandler { null } +class FSMBuilder( + var statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), + var defaultStateHandler: StatesHandler? = StatesHandler { null } ) { - private var states = mutableListOf>() + private var states = mutableListOf>() - fun add(kClass: KClass, handler: StatesHandler) { - states.add(StatesHandlerHolder(kClass, false, handler)) + fun add(kClass: KClass, handler: StatesHandler) { + states.add(StateHandlerHolder(kClass, false, handler)) } - fun addStrict(kClass: KClass, handler: StatesHandler) { - states.add(StatesHandlerHolder(kClass, true, handler)) + fun add(filter: suspend (state: State) -> Boolean, handler: StatesHandler) { + states.add(handler.holder(filter)) + } + + fun addStrict(kClass: KClass, handler: StatesHandler) { + states.add(StateHandlerHolder(kClass, true, handler)) + } + + inline fun onStateOrSubstate(handler: StatesHandler) { + add(I::class, handler) + } + + inline fun strictlyOn(handler: StatesHandler) { + addStrict(I::class, handler) + } + + inline fun doWhen( + noinline filter: suspend (state: State) -> Boolean, + handler: StatesHandler + ) { + add(filter, handler) } fun build() = StatesMachine( statesManager, states.toList().let { list -> - defaultStateHandler ?.let { list + it.holder(false) } ?: list + defaultStateHandler ?.let { list + it.holder { true } } ?: list } ) } -inline fun FSMBuilder.onStateOrSubstate(handler: StatesHandler) { - add(I::class, handler) -} - -inline fun FSMBuilder.strictlyOn(handler: StatesHandler) { - addStrict(I::class, handler) -} - -fun buildFSM( - block: FSMBuilder.() -> Unit -): StatesMachine = FSMBuilder().apply(block).build() +fun buildFSM( + block: FSMBuilder.() -> Unit +): StatesMachine = FSMBuilder().apply(block).build() 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 3488e0ac7d3..f1ded976fb9 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 @@ -9,25 +9,25 @@ import kotlinx.coroutines.sync.withLock /** * Implement this repo if you want to use some custom repo for [DefaultStatesManager] */ -interface DefaultStatesManagerRepo { +interface DefaultStatesManagerRepo { /** * Must save [state] as current state of chain with [State.context] of [state] */ - suspend fun set(state: State) + suspend fun set(state: T) /** * 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) + suspend fun removeState(state: T) /** * @return Current list of available and saved states */ - suspend fun getStates(): List + suspend fun getStates(): List /** * @return Current state by [context] */ - suspend fun getContextState(context: Any): State? + suspend fun getContextState(context: Any): T? /** * @return Current state by [context] @@ -43,21 +43,21 @@ interface DefaultStatesManagerRepo { * 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 */ -class DefaultStatesManager( - private val repo: DefaultStatesManagerRepo = InMemoryDefaultStatesManagerRepo(), - private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true } -) : StatesManager { - private val _onChainStateUpdated = MutableSharedFlow>(0) - override val onChainStateUpdated: Flow> = _onChainStateUpdated.asSharedFlow() - private val _onStartChain = MutableSharedFlow(0) - override val onStartChain: Flow = _onStartChain.asSharedFlow() - private val _onEndChain = MutableSharedFlow(0) - override val onEndChain: Flow = _onEndChain.asSharedFlow() +class DefaultStatesManager( + private val repo: DefaultStatesManagerRepo = InMemoryDefaultStatesManagerRepo(), + private val onContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true } +) : StatesManager { + private val _onChainStateUpdated = MutableSharedFlow>(0) + override val onChainStateUpdated: Flow> = _onChainStateUpdated.asSharedFlow() + private val _onStartChain = MutableSharedFlow(0) + override val onStartChain: Flow = _onStartChain.asSharedFlow() + private val _onEndChain = MutableSharedFlow(0) + override val onEndChain: Flow = _onEndChain.asSharedFlow() private val mapMutex = Mutex() - override suspend fun update(old: State, new: State) = mapMutex.withLock { - val stateByOldContext: State? = repo.getContextState(old.context) + override suspend fun update(old: T, new: T) = mapMutex.withLock { + val stateByOldContext: T? = repo.getContextState(old.context) when { stateByOldContext != old -> return@withLock stateByOldContext == null || old.context == new.context -> { @@ -76,26 +76,26 @@ class DefaultStatesManager( } } - override suspend fun startChain(state: State) = mapMutex.withLock { + override suspend fun startChain(state: T) = mapMutex.withLock { if (!repo.contains(state.context)) { repo.set(state) _onStartChain.emit(state) } } - private suspend fun endChainWithoutLock(state: State) { + private suspend fun endChainWithoutLock(state: T) { if (repo.getContextState(state.context) == state) { repo.removeState(state) _onEndChain.emit(state) } } - override suspend fun endChain(state: State) { + override suspend fun endChain(state: T) { mapMutex.withLock { endChainWithoutLock(state) } } - override suspend fun getActiveStates(): List = repo.getStates() + override suspend fun getActiveStates(): List = repo.getStates() } diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryDefaultStatesManagerRepo.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryDefaultStatesManagerRepo.kt index 70051e14d07..ca76f7673ba 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryDefaultStatesManagerRepo.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryDefaultStatesManagerRepo.kt @@ -6,20 +6,20 @@ import dev.inmo.micro_utils.fsm.common.State * Simple [DefaultStatesManagerRepo] for [DefaultStatesManager] which will store data in [map] and use primitive * functionality */ -class InMemoryDefaultStatesManagerRepo( - private val map: MutableMap = mutableMapOf() -) : DefaultStatesManagerRepo { - override suspend fun set(state: State) { +class InMemoryDefaultStatesManagerRepo( + private val map: MutableMap = mutableMapOf() +) : DefaultStatesManagerRepo { + override suspend fun set(state: T) { map[state.context] = state } - override suspend fun removeState(state: State) { + override suspend fun removeState(state: T) { map.remove(state.context) } - override suspend fun getStates(): List = map.values.toList() + override suspend fun getStates(): List = map.values.toList() - override suspend fun getContextState(context: Any): State? = map[context] + override suspend fun getContextState(context: Any): T? = map[context] override suspend fun contains(context: Any): Boolean = map.contains(context) } diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryStatesManager.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryStatesManager.kt index 4d8094bd48b..1e424a0625c 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryStatesManager.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/managers/InMemoryStatesManager.kt @@ -7,62 +7,13 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock /** + * Creates [DefaultStatesManager] with [InMemoryDefaultStatesManagerRepo] + * * @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 */ -class InMemoryStatesManager( - private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true } -) : StatesManager { - private val _onChainStateUpdated = MutableSharedFlow>(0) - override val onChainStateUpdated: Flow> = _onChainStateUpdated.asSharedFlow() - private val _onStartChain = MutableSharedFlow(0) - override val onStartChain: Flow = _onStartChain.asSharedFlow() - 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 { - when { - contextsToStates[old.context] != old -> return@withLock - old.context == new.context || !contextsToStates.containsKey(new.context) -> { - contextsToStates[old.context] = 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 - _onChainStateUpdated.emit(old to new) - } - } - } - } - - override suspend fun startChain(state: State) = mapMutex.withLock { - if (!contextsToStates.containsKey(state.context)) { - contextsToStates[state.context] = state - _onStartChain.emit(state) - } - } - - private suspend fun endChainWithoutLock(state: State) { - if (contextsToStates[state.context] == state) { - contextsToStates.remove(state.context) - _onEndChain.emit(state) - } - } - - override suspend fun endChain(state: State) { - mapMutex.withLock { - endChainWithoutLock(state) - } - } - - override suspend fun getActiveStates(): List = contextsToStates.values.toList() - -} +@Deprecated("Use DefaultStatesManager instead", ReplaceWith("DefaultStatesManager")) +fun InMemoryStatesManager( + onContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true } +) = DefaultStatesManager(onContextsConflictResolver = onContextsConflictResolver) diff --git a/fsm/common/src/jvmTest/kotlin/PlayableMain.kt b/fsm/common/src/jvmTest/kotlin/PlayableMain.kt index a317c2a5f17..5a2e61d55e6 100644 --- a/fsm/common/src/jvmTest/kotlin/PlayableMain.kt +++ b/fsm/common/src/jvmTest/kotlin/PlayableMain.kt @@ -1,6 +1,7 @@ import dev.inmo.micro_utils.fsm.common.* import dev.inmo.micro_utils.fsm.common.dsl.buildFSM -import dev.inmo.micro_utils.fsm.common.dsl.strictlyOn +import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager +import dev.inmo.micro_utils.fsm.common.managers.InMemoryStatesManager import kotlinx.coroutines.* sealed interface TrafficLightState : State { @@ -25,9 +26,9 @@ class PlayableMain { } } - val statesManager = InMemoryStatesManager() + val statesManager = DefaultStatesManager() - val machine = buildFSM { + val machine = buildFSM { strictlyOn { delay(1000L) YellowCommon(it.context).also(::println) diff --git a/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedDefaultStatesManagerRepo.kt b/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedDefaultStatesManagerRepo.kt index 68b3b687db6..21894f4bb61 100644 --- a/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedDefaultStatesManagerRepo.kt +++ b/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedDefaultStatesManagerRepo.kt @@ -5,21 +5,21 @@ import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerRepo import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.pagination.getAll -class KeyValueBasedDefaultStatesManagerRepo( - private val keyValueRepo: KeyValueRepo -) : DefaultStatesManagerRepo { - override suspend fun set(state: State) { +class KeyValueBasedDefaultStatesManagerRepo( + private val keyValueRepo: KeyValueRepo +) : DefaultStatesManagerRepo { + override suspend fun set(state: T) { keyValueRepo.set(state.context, state) } - override suspend fun removeState(state: State) { + override suspend fun removeState(state: T) { if (keyValueRepo.get(state.context) == state) { keyValueRepo.unset(state.context) } } - 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 getStates(): List = keyValueRepo.getAll { keys(it) }.map { it.second } + override suspend fun getContextState(context: Any): T? = 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 deleted file mode 100644 index 136bfd45c9c..00000000000 --- a/fsm/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/repos/common/KeyValueBasedStatesManager.kt +++ /dev/null @@ -1,84 +0,0 @@ -package dev.inmo.micro_utils.fsm.repos.common - -import dev.inmo.micro_utils.fsm.common.State -import dev.inmo.micro_utils.fsm.common.StatesManager -import dev.inmo.micro_utils.repos.* -import dev.inmo.micro_utils.repos.mappers.withMapper -import dev.inmo.micro_utils.repos.pagination.getAll -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock - -@Deprecated("Replace with DefaultStatesManager and KeyValueBasedDefaultStatesManagerRepo") -class KeyValueBasedStatesManager( - private val keyValueRepo: KeyValueRepo, - private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true } -) : StatesManager { - private val _onChainStateUpdated = MutableSharedFlow>(0) - override val onChainStateUpdated: Flow> = _onChainStateUpdated.asSharedFlow() - private val _onEndChain = MutableSharedFlow(0) - override val onEndChain: Flow = _onEndChain.asSharedFlow() - - override val onStartChain: Flow = keyValueRepo.onNewValue.map { it.second } - - private val mutex = Mutex() - - override suspend fun update(old: State, new: State) { - mutex.withLock { - when { - keyValueRepo.get(old.context) != old -> return@withLock - old.context == new.context || !keyValueRepo.contains(new.context) -> { - keyValueRepo.set(old.context, new) - _onChainStateUpdated.emit(old to new) - } - else -> { - val stateOnNewOneContext = keyValueRepo.get(new.context)!! - if (onContextsConflictResolver(old, new, stateOnNewOneContext)) { - endChainWithoutLock(stateOnNewOneContext) - keyValueRepo.unset(old.context) - keyValueRepo.set(new.context, new) - _onChainStateUpdated.emit(old to new) - } - } - } - - } - } - - override suspend fun startChain(state: State) { - if (!keyValueRepo.contains(state.context)) { - keyValueRepo.set(state.context, state) - } - } - - private suspend fun endChainWithoutLock(state: State) { - if (keyValueRepo.get(state.context) == state) { - keyValueRepo.unset(state.context) - _onEndChain.emit(state) - } - } - - override suspend fun endChain(state: State) { - mutex.withLock { endChainWithoutLock(state) } - } - - override suspend fun getActiveStates(): List { - return keyValueRepo.getAll { keys(it) }.map { it.second } - } - -} - -inline fun createStatesManager( - targetKeyValueRepo: KeyValueRepo, - noinline contextToOutTransformer: suspend Any.() -> TargetContextType, - noinline stateToOutTransformer: suspend State.() -> TargetStateType, - noinline outToContextTransformer: suspend TargetContextType.() -> Any, - noinline outToStateTransformer: suspend TargetStateType.() -> State, -) = KeyValueBasedStatesManager( - targetKeyValueRepo.withMapper( - contextToOutTransformer, - stateToOutTransformer, - outToContextTransformer, - outToStateTransformer - ) -) diff --git a/gradle.properties b/gradle.properties index 53f9e29b92e..b0f7ae31269 100644 --- a/gradle.properties +++ b/gradle.properties @@ -45,5 +45,5 @@ dokka_version=1.5.31 # Project data group=dev.inmo -version=0.7.5 -android_code_version=79 +version=0.8.0 +android_code_version=80