diff --git a/CHANGELOG.md b/CHANGELOG.md index d5c02ed3735..da212d218db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 0.8.0 + +* `Versions`: + * `Klock`: `2.4.6` -> `2.4.7` + * `Ktor`: `1.6.4` -> `1.6.5` + * `Exposed`: `0.35.3` -> `0.36.1` +* `Common`: + * 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 + * `StateHandlerHolder` has been renamed to `CheckableHandlerHolder` + * Add opportunity for comfortable adding default state handler + ## 0.7.4 * `Common`: diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt index 221237b4fee..af15eb4ca00 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt @@ -2,8 +2,6 @@ package dev.inmo.micro_utils.common -import kotlin.jvm.JvmInline - private inline fun getObject( additional: MutableList, iterator: Iterator diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Either.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Either.kt index f57136aefb6..35a984d7003 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Either.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Either.kt @@ -1,5 +1,10 @@ package dev.inmo.micro_utils.common +import kotlinx.serialization.* +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + /** * Realization of this interface will contains at least one not null - [t1] or [t2] * @@ -10,16 +15,90 @@ package dev.inmo.micro_utils.common * @see Either.onFirst * @see Either.onSecond */ +@Serializable(EitherSerializer::class) sealed interface Either { val t1: T1? val t2: T2? - companion object + companion object { + fun serializer( + t1Serializer: KSerializer, + t2Serializer: KSerializer, + ): KSerializer> = EitherSerializer(t1Serializer, t2Serializer) + } +} + +class EitherSerializer( + t1Serializer: KSerializer, + t2Serializer: KSerializer, +) : KSerializer> { + @ExperimentalSerializationApi + @InternalSerializationApi + override val descriptor: SerialDescriptor = buildSerialDescriptor( + "TypedSerializer", + SerialKind.CONTEXTUAL + ) { + element("type", String.serializer().descriptor) + element("value", ContextualSerializer(Either::class).descriptor) + } + private val t1EitherSerializer = EitherFirst.serializer(t1Serializer, t2Serializer) + private val t2EitherSerializer = EitherSecond.serializer(t1Serializer, t2Serializer) + + @ExperimentalSerializationApi + @InternalSerializationApi + override fun deserialize(decoder: Decoder): Either { + return decoder.decodeStructure(descriptor) { + var type: String? = null + lateinit var result: Either + while (true) { + when (val index = decodeElementIndex(descriptor)) { + 0 -> type = decodeStringElement(descriptor, 0) + 1 -> { + result = when (type) { + "t1" -> decodeSerializableElement( + descriptor, + 1, + t1EitherSerializer + ) + "t2" -> decodeSerializableElement( + descriptor, + 1, + t2EitherSerializer + ) + else -> error("Unknown type of either: $type") + } + } + CompositeDecoder.DECODE_DONE -> break + else -> error("Unexpected index: $index") + } + } + result + } + } + + + @ExperimentalSerializationApi + @InternalSerializationApi + override fun serialize(encoder: Encoder, value: Either) { + encoder.encodeStructure(descriptor) { + when (value) { + is EitherFirst -> { + encodeStringElement(descriptor, 0, "t1") + encodeSerializableElement(descriptor, 1, t1EitherSerializer, value) + } + is EitherSecond -> { + encodeStringElement(descriptor, 0, "t2") + encodeSerializableElement(descriptor, 1, t2EitherSerializer, value) + } + } + } + } } /** * This type [Either] will always have not nullable [t1] */ +@Serializable data class EitherFirst( override val t1: T1 ) : Either { @@ -30,6 +109,7 @@ data class EitherFirst( /** * This type [Either] will always have not nullable [t2] */ +@Serializable data class EitherSecond( override val t2: T2 ) : Either { diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/CheckableHandlerHolder.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/CheckableHandlerHolder.kt new file mode 100644 index 00000000000..bdd321b48e0 --- /dev/null +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/CheckableHandlerHolder.kt @@ -0,0 +1,81 @@ +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 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 + */ + 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: I): O? { + return delegateTo.run { handleState(state) } + } +} + +fun CheckableHandlerHolder( + inputKlass: KClass, + strict: Boolean = false, + delegateTo: StatesHandler +) = CustomizableHandlerHolder( + StatesHandler { + delegateTo.run { handleState(it as I) } + }, + if (strict) { + { it::class == inputKlass } + } else { + { inputKlass.isInstance(it) } + } +) + +@Deprecated("Renamed", ReplaceWith("CheckableHandlerHolder")) +fun StateHandlerHolder( + inputKlass: KClass, + strict: Boolean = false, + delegateTo: StatesHandler +) = CheckableHandlerHolder(inputKlass, strict, delegateTo) + +inline fun CheckableHandlerHolder( + strict: Boolean = false, + delegateTo: StatesHandler +) = CheckableHandlerHolder(I::class, strict, delegateTo) + +@Deprecated("Renamed", ReplaceWith("CheckableHandlerHolder")) +inline fun StateHandlerHolder( + strict: Boolean = false, + delegateTo: StatesHandler +) = CheckableHandlerHolder(strict, delegateTo) + +inline fun StatesHandler.holder( + strict: Boolean = true +) = CheckableHandlerHolder( + 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/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/StateHandlerHolder.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StateHandlerHolder.kt deleted file mode 100644 index a92f9b96602..00000000000 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StateHandlerHolder.kt +++ /dev/null @@ -1,27 +0,0 @@ -package dev.inmo.micro_utils.fsm.common - -import kotlin.reflect.KClass - -/** - * Default realization of [StatesHandler]. It will incapsulate checking of [State] type in [checkHandleable] and class - * casting in [handleState] - */ -class StateHandlerHolder( - private val inputKlass: KClass, - private val strict: Boolean = false, - private val delegateTo: StatesHandler -) : StatesHandler { - /** - * 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)) - - /** - * Calls [delegateTo] method [StatesHandler.handleState] with [state] casted to [I]. Use [checkHandleable] - * to be sure that this [StateHandlerHolder] will be able to handle [state] - */ - override suspend fun StatesMachine.handleState(state: State): State? { - return delegateTo.run { handleState(state as I) } - } -} 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/StatesMachine.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesMachine.kt index 11170c5b030..f31e641a3b0 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 @@ -1,24 +1,24 @@ package dev.inmo.micro_utils.fsm.common -import dev.inmo.micro_utils.coroutines.* +import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import kotlinx.coroutines.* -import kotlinx.coroutines.flow.asFlow - -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 */ @@ -27,15 +27,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) } } @@ -44,14 +44,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], @@ -60,7 +60,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) @@ -83,7 +83,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..972106c6d83 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 @@ -1,30 +1,30 @@ package dev.inmo.micro_utils.fsm.common -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.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 a5fd74a5ddb..5713883f3f5 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 @@ -1,37 +1,51 @@ package dev.inmo.micro_utils.fsm.common.dsl import dev.inmo.micro_utils.fsm.common.* -import dev.inmo.micro_utils.fsm.common.managers.* -import dev.inmo.micro_utils.fsm.common.managers.InMemoryStatesManager +import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager +import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo import kotlin.reflect.KClass -class FSMBuilder( - var statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()) +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(StateHandlerHolder(kClass, false, handler)) + fun add(kClass: KClass, handler: StatesHandler) { + states.add(CheckableHandlerHolder(kClass, false, handler)) } - fun addStrict(kClass: KClass, handler: StatesHandler) { - states.add(StateHandlerHolder(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(CheckableHandlerHolder(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() + states.toList().let { 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..323e895b315 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 @@ -1,68 +1,16 @@ package dev.inmo.micro_utils.fsm.common.managers import dev.inmo.micro_utils.fsm.common.State -import dev.inmo.micro_utils.fsm.common.StatesManager import kotlinx.coroutines.flow.* -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 20f356055d4..0e9d13f86a8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,11 +10,11 @@ org.gradle.jvmargs=-Xmx2g kotlin_version=1.5.31 kotlin_coroutines_version=1.5.2 kotlin_serialisation_core_version=1.3.0 -kotlin_exposed_version=0.35.3 +kotlin_exposed_version=0.36.1 -ktor_version=1.6.4 +ktor_version=1.6.5 -klockVersion=2.4.6 +klockVersion=2.4.7 github_release_plugin_version=2.2.12 @@ -45,5 +45,5 @@ dokka_version=1.5.31 # Project data group=dev.inmo -version=0.7.4 -android_code_version=78 +version=0.8.0 +android_code_version=80 diff --git a/language_codes/src/commonMain/kotlin/dev/inmo/micro_utils/language_codes/LanguageCodes.kt b/language_codes/src/commonMain/kotlin/dev/inmo/micro_utils/language_codes/LanguageCodes.kt index c6bceb1a36e..6f717bb8468 100644 --- a/language_codes/src/commonMain/kotlin/dev/inmo/micro_utils/language_codes/LanguageCodes.kt +++ b/language_codes/src/commonMain/kotlin/dev/inmo/micro_utils/language_codes/LanguageCodes.kt @@ -1,3 +1,5 @@ +@file:Suppress("SERIALIZER_TYPE_INCOMPATIBLE") + package dev.inmo.micro_utils.language_codes import kotlinx.serialization.Serializable diff --git a/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/KnownMimeTypes.kt b/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/KnownMimeTypes.kt index 17c13034199..bc90115e64a 100644 --- a/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/KnownMimeTypes.kt +++ b/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/KnownMimeTypes.kt @@ -1,3 +1,5 @@ +@file:Suppress("SERIALIZER_TYPE_INCOMPATIBLE") + package dev.inmo.micro_utils.mime_types import kotlinx.serialization.Serializable