diff --git a/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesHolder.kt b/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesHolder.kt new file mode 100644 index 0000000..0bc6466 --- /dev/null +++ b/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesHolder.kt @@ -0,0 +1,48 @@ +package dev.inmo.tgbotapi.libraries.fsm.core + +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.* + +interface StatesHolder { + val onNewState: Flow + val onStateRemoved: Flow + + /** + * Must always save [state] inside of [this] [StatesHolder] AND send event in [onNewState] + */ + suspend fun saveState(state: T) + + /** + * Must always remove [state] in case it is stored AND send event in [onStateRemoved] if it was removed + */ + suspend fun removeState(state: T) + + /** + * Must returns currently stored states + */ + suspend fun loadStates(): List +} + +class ListBasedStatesHolder( + base: List = emptyList() +) : StatesHolder { + private val data: MutableList = base.toMutableList() + private val _onNewState = MutableSharedFlow(0, onBufferOverflow = BufferOverflow.SUSPEND) + override val onNewState: Flow = _onNewState.asSharedFlow() + private val _onStateRemoved = MutableSharedFlow(0, onBufferOverflow = BufferOverflow.SUSPEND) + override val onStateRemoved: Flow = _onStateRemoved.asSharedFlow() + + override suspend fun saveState(state: T) { + data.add(state) + _onNewState.emit(state) + } + + override suspend fun removeState(state: T) { + if (data.remove(state)) { + _onStateRemoved.emit(state) + } + } + + override suspend fun loadStates(): List = data.toList() + +} diff --git a/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesMachine.kt b/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesMachine.kt index 2b22ec0..8c2c0d5 100644 --- a/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesMachine.kt +++ b/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesMachine.kt @@ -14,24 +14,22 @@ private suspend fun launchStateHandling( } class StatesMachine( - private val statesRepo: StatesRepo, + private val statesHolder: StatesHolder, private val statesQuotaManager: StatesQuotaManager, private val handlers: List> ) : StatesHandler { override suspend fun handleState(state: T): O? { - return statesQuotaManager.doOnQuota( - state - ) { + return statesQuotaManager.doOnQuota(state) { launchStateHandling(state, handlers) } } fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions { - val statesFlow = statesRepo.loadStates().asFlow() + statesRepo.onNewState + val statesFlow = statesHolder.loadStates().asFlow() + statesHolder.onNewState statesFlow.subscribeSafelyWithoutExceptions(this) { val newState = handleState(it) - newState ?.also { statesRepo.saveState(newState) } - statesRepo.removeState(it) + newState ?.also { statesHolder.saveState(newState) } + statesHolder.removeState(it) } } } diff --git a/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesQuotaManager.kt b/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesQuotaManager.kt index b182d1a..42c8fd1 100644 --- a/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesQuotaManager.kt +++ b/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesQuotaManager.kt @@ -1,5 +1,23 @@ package dev.inmo.tgbotapi.libraries.fsm.core +import kotlinx.coroutines.sync.Mutex + interface StatesQuotaManager { suspend fun doOnQuota(state: T, block: suspend (T) -> O?): O? + + suspend fun transitQuota(from: State, to: State?) +} + +class InMemoryStatesQuotaManager : StatesQuotaManager { + private val currentContextsAndStates = mutableMapOf() + + private val mutex = Mutex() + + override suspend fun doOnQuota(state: T, block: suspend (T) -> O?): O? { + + } + + override suspend fun transitQuota(state: State) { + TODO("Not yet implemented") + } } diff --git a/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesRepo.kt b/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesRepo.kt deleted file mode 100644 index 4c8d568..0000000 --- a/fsm/core/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/StatesRepo.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.inmo.tgbotapi.libraries.fsm.core - -import kotlinx.coroutines.flow.Flow - -interface StatesRepo { - val onNewState: Flow - val onStateRemoved: Flow - - suspend fun saveState(state: T) - suspend fun removeState(state: T) - suspend fun loadStates(): List -} diff --git a/fsm/core/src/commonTest/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/PlayableMain.kt b/fsm/core/src/commonTest/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/PlayableMain.kt new file mode 100644 index 0000000..17d713f --- /dev/null +++ b/fsm/core/src/commonTest/kotlin/dev/inmo/tgbotapi/libraries/fsm/core/PlayableMain.kt @@ -0,0 +1,30 @@ +package dev.inmo.tgbotapi.libraries.fsm.core + +import kotlin.random.Random + +sealed interface TrafficLightState : ImmediateOrNeverState { + val trafficLightNumber: Int + override val context: Int + get() = trafficLightNumber +} +data class GreenCommon(override val trafficLightNumber: Int) : TrafficLightState +data class YellowCommon(override val trafficLightNumber: Int) : TrafficLightState +data class RedCommon(override val trafficLightNumber: Int) : TrafficLightState + +suspend fun main() { + val countOfTrafficLights = 10 + val initialStates = (0 until countOfTrafficLights).map { + when (Random.nextInt(3)) { + 0 -> GreenCommon(it) + 1 -> YellowCommon(it) + else -> RedCommon(it) + } + } + + val statesHolder = ListBasedStatesHolder(initialStates) + val machine = StatesMachine( + statesHolder, + + ) +} + diff --git a/fsm/tgbotapi/build.gradle b/fsm/tgbotapi/build.gradle new file mode 100644 index 0000000..47b1607 --- /dev/null +++ b/fsm/tgbotapi/build.gradle @@ -0,0 +1,17 @@ +plugins { + id "org.jetbrains.kotlin.multiplatform" + id "org.jetbrains.kotlin.plugin.serialization" + id "com.android.library" +} + +apply from: "$mppProjectWithSerializationPresetPath" + +kotlin { + sourceSets { + commonMain { + dependencies { + api project(":tgbotapi.libraries.fsm.core") + } + } + } +} diff --git a/fsm/tgbotapi/src/main/AndroidManifest.xml b/fsm/tgbotapi/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7022277 --- /dev/null +++ b/fsm/tgbotapi/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/settings.gradle b/settings.gradle index b95bc89..c4c00db 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,8 @@ String[] includes = [ ":cache:admins:plagubot", ":cache:media", - ":fsm:core" + ":fsm:core", + ":fsm:tgbotapi" ]