mirror of
				https://github.com/InsanusMokrassar/TelegramBotApiLibraries.git
				synced 2025-11-04 06:00:24 +00:00 
			
		
		
		
	add dsl preview
This commit is contained in:
		@@ -2,14 +2,14 @@ package dev.inmo.tgbotapi.libraries.fsm.core
 | 
			
		||||
 | 
			
		||||
import kotlin.reflect.KClass
 | 
			
		||||
 | 
			
		||||
class StateHandlerHolder<I : State, O : State>(
 | 
			
		||||
class StateHandlerHolder<I : State>(
 | 
			
		||||
    private val inputKlass: KClass<I>,
 | 
			
		||||
    private val strict: Boolean = false,
 | 
			
		||||
    private val delegateTo: StatesHandler<I, O>
 | 
			
		||||
) : StatesHandler<State, O> {
 | 
			
		||||
    private val delegateTo: StatesHandler<I>
 | 
			
		||||
) : StatesHandler<State> {
 | 
			
		||||
    fun checkHandleable(state: State) = state::class == inputKlass || (!strict && inputKlass.isInstance(state))
 | 
			
		||||
 | 
			
		||||
    override suspend fun handleState(state: State): O? {
 | 
			
		||||
        return delegateTo.handleState(state as I)
 | 
			
		||||
    override suspend fun StatesMachine.handleState(state: State): State? {
 | 
			
		||||
        return delegateTo.run { handleState(state as I) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
package dev.inmo.tgbotapi.libraries.fsm.core
 | 
			
		||||
 | 
			
		||||
fun interface StatesHandler<I : State, O : State> {
 | 
			
		||||
    suspend fun handleState(state: I): O?
 | 
			
		||||
fun interface StatesHandler<I : State> {
 | 
			
		||||
    suspend fun StatesMachine.handleState(state: I): State?
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,26 +4,24 @@ import dev.inmo.micro_utils.coroutines.*
 | 
			
		||||
import kotlinx.coroutines.*
 | 
			
		||||
import kotlinx.coroutines.flow.asFlow
 | 
			
		||||
 | 
			
		||||
private suspend fun <I : State, O : State> launchStateHandling(
 | 
			
		||||
private suspend fun <I : State> StatesMachine.launchStateHandling(
 | 
			
		||||
    state: State,
 | 
			
		||||
    handlers: List<StateHandlerHolder<out I, out O>>
 | 
			
		||||
): O? {
 | 
			
		||||
    return handlers.firstOrNull { it.checkHandleable(state) } ?.handleState(
 | 
			
		||||
        state
 | 
			
		||||
    )
 | 
			
		||||
    handlers: List<StateHandlerHolder<out I>>
 | 
			
		||||
): State? {
 | 
			
		||||
    return handlers.firstOrNull { it.checkHandleable(state) } ?.run {
 | 
			
		||||
        handleState(state)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class StatesMachine<T : State, I : T, O : T>(
 | 
			
		||||
    private val statesManager: StatesManager<T>,
 | 
			
		||||
    private val handlers: List<StateHandlerHolder<out I, out O>>
 | 
			
		||||
) : StatesHandler<T, O> {
 | 
			
		||||
    override suspend fun handleState(state: T): O? {
 | 
			
		||||
        return launchStateHandling(state, handlers)
 | 
			
		||||
    }
 | 
			
		||||
class StatesMachine (
 | 
			
		||||
    private val statesManager: StatesManager,
 | 
			
		||||
    private val handlers: List<StateHandlerHolder<*>>
 | 
			
		||||
) : StatesHandler<State> {
 | 
			
		||||
    override suspend fun StatesMachine.handleState(state: State): State? = launchStateHandling(state, handlers)
 | 
			
		||||
 | 
			
		||||
    fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions {
 | 
			
		||||
        val statePerformer: suspend (T) -> Unit = { state: T ->
 | 
			
		||||
            val newState = handleState(state)
 | 
			
		||||
        val statePerformer: suspend (State) -> Unit = { state: State ->
 | 
			
		||||
            val newState = launchStateHandling(state, handlers)
 | 
			
		||||
            if (newState != null) {
 | 
			
		||||
                statesManager.update(state, newState)
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -41,4 +39,8 @@ class StatesMachine<T : State, I : T, O : T>(
 | 
			
		||||
            launch { statePerformer(it) }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun startChain(state: State) {
 | 
			
		||||
        statesManager.startChain(state)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,30 +4,30 @@ import kotlinx.coroutines.flow.*
 | 
			
		||||
import kotlinx.coroutines.sync.Mutex
 | 
			
		||||
import kotlinx.coroutines.sync.withLock
 | 
			
		||||
 | 
			
		||||
interface StatesManager<T : State> {
 | 
			
		||||
    val onChainStateUpdated: Flow<Pair<T, T>>
 | 
			
		||||
    val onStartChain: Flow<T>
 | 
			
		||||
    val onEndChain: Flow<T>
 | 
			
		||||
interface StatesManager {
 | 
			
		||||
    val onChainStateUpdated: Flow<Pair<State, State>>
 | 
			
		||||
    val onStartChain: Flow<State>
 | 
			
		||||
    val onEndChain: Flow<State>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Must set current set using [State.context]
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun update(old: T, new: T)
 | 
			
		||||
    suspend fun update(old: State, new: State)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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: T)
 | 
			
		||||
    suspend fun startChain(state: State)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Ends chain with context from [state]. In case when [State.context] of [state] is absent, [state] should be just
 | 
			
		||||
     * ignored
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun endChain(state: T)
 | 
			
		||||
    suspend fun endChain(state: State)
 | 
			
		||||
 | 
			
		||||
    suspend fun getActiveStates(): List<T>
 | 
			
		||||
    suspend fun getActiveStates(): List<State>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -35,20 +35,20 @@ interface StatesManager<T : State> {
 | 
			
		||||
 * 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<T : State>(
 | 
			
		||||
    private val onContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true }
 | 
			
		||||
) : StatesManager<T> {
 | 
			
		||||
    private val _onChainStateUpdated = MutableSharedFlow<Pair<T, T>>(0)
 | 
			
		||||
    override val onChainStateUpdated: Flow<Pair<T, T>> = _onChainStateUpdated.asSharedFlow()
 | 
			
		||||
    private val _onStartChain = MutableSharedFlow<T>(0)
 | 
			
		||||
    override val onStartChain: Flow<T> = _onStartChain.asSharedFlow()
 | 
			
		||||
    private val _onEndChain = MutableSharedFlow<T>(0)
 | 
			
		||||
    override val onEndChain: Flow<T> = _onEndChain.asSharedFlow()
 | 
			
		||||
class InMemoryStatesManager(
 | 
			
		||||
    private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true }
 | 
			
		||||
) : StatesManager {
 | 
			
		||||
    private val _onChainStateUpdated = MutableSharedFlow<Pair<State, State>>(0)
 | 
			
		||||
    override val onChainStateUpdated: Flow<Pair<State, State>> = _onChainStateUpdated.asSharedFlow()
 | 
			
		||||
    private val _onStartChain = MutableSharedFlow<State>(0)
 | 
			
		||||
    override val onStartChain: Flow<State> = _onStartChain.asSharedFlow()
 | 
			
		||||
    private val _onEndChain = MutableSharedFlow<State>(0)
 | 
			
		||||
    override val onEndChain: Flow<State> = _onEndChain.asSharedFlow()
 | 
			
		||||
 | 
			
		||||
    private val contextsToStates = mutableMapOf<Any, T>()
 | 
			
		||||
    private val contextsToStates = mutableMapOf<Any, State>()
 | 
			
		||||
    private val mapMutex = Mutex()
 | 
			
		||||
 | 
			
		||||
    override suspend fun update(old: T, new: T) = mapMutex.withLock {
 | 
			
		||||
    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) -> {
 | 
			
		||||
@@ -67,26 +67,26 @@ class InMemoryStatesManager<T : State>(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun startChain(state: T) = mapMutex.withLock {
 | 
			
		||||
    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: T) {
 | 
			
		||||
    private suspend fun endChainWithoutLock(state: State) {
 | 
			
		||||
        if (contextsToStates[state.context] == state) {
 | 
			
		||||
            contextsToStates.remove(state.context)
 | 
			
		||||
            _onEndChain.emit(state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun endChain(state: T) {
 | 
			
		||||
    override suspend fun endChain(state: State) {
 | 
			
		||||
        mapMutex.withLock {
 | 
			
		||||
            endChainWithoutLock(state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getActiveStates(): List<T> = contextsToStates.values.toList()
 | 
			
		||||
    override suspend fun getActiveStates(): List<State> = contextsToStates.values.toList()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,35 @@
 | 
			
		||||
package dev.inmo.tgbotapi.libraries.fsm.core.dsl
 | 
			
		||||
 | 
			
		||||
import dev.inmo.tgbotapi.libraries.fsm.core.*
 | 
			
		||||
import kotlin.reflect.KClass
 | 
			
		||||
 | 
			
		||||
class FSMBuilder(
 | 
			
		||||
    var statesManager: StatesManager = InMemoryStatesManager()
 | 
			
		||||
) {
 | 
			
		||||
    private var states = mutableListOf<StateHandlerHolder<*>>()
 | 
			
		||||
 | 
			
		||||
    fun <I : State> add(kClass: KClass<I>, handler: StatesHandler<I>) {
 | 
			
		||||
        states.add(StateHandlerHolder(kClass, false, handler))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun <I : State> addStrict(kClass: KClass<I>, handler: StatesHandler<I>) {
 | 
			
		||||
        states.add(StateHandlerHolder(kClass, true, handler))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun build() = StatesMachine(
 | 
			
		||||
        statesManager,
 | 
			
		||||
        states.toList()
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <reified I : State> FSMBuilder.onStateOrSubstate(handler: StatesHandler<I>) {
 | 
			
		||||
    add(I::class, handler)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <reified I : State> FSMBuilder.strictlyOn(handler: StatesHandler<I>) {
 | 
			
		||||
    addStrict(I::class, handler)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun buildFSM(
 | 
			
		||||
    block: FSMBuilder.() -> Unit
 | 
			
		||||
): StatesMachine = FSMBuilder().apply(block).build()
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
import dev.inmo.tgbotapi.libraries.fsm.core.*
 | 
			
		||||
import dev.inmo.tgbotapi.libraries.fsm.core.dsl.buildFSM
 | 
			
		||||
import dev.inmo.tgbotapi.libraries.fsm.core.dsl.strictlyOn
 | 
			
		||||
import kotlinx.coroutines.*
 | 
			
		||||
import kotlin.random.Random
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
@@ -24,27 +26,25 @@ class PlayableMain {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val statesManager = InMemoryStatesManager<TrafficLightState>()
 | 
			
		||||
            val statesManager = InMemoryStatesManager()
 | 
			
		||||
 | 
			
		||||
            val machine = StatesMachine(
 | 
			
		||||
                statesManager,
 | 
			
		||||
                listOf(
 | 
			
		||||
                    StateHandlerHolder(GreenCommon::class) {
 | 
			
		||||
            val machine = buildFSM {
 | 
			
		||||
                strictlyOn<GreenCommon> {
 | 
			
		||||
                    delay(1000L)
 | 
			
		||||
                    YellowCommon(it.context).also(::println)
 | 
			
		||||
                    },
 | 
			
		||||
                    StateHandlerHolder(YellowCommon::class) {
 | 
			
		||||
                }
 | 
			
		||||
                strictlyOn<YellowCommon> {
 | 
			
		||||
                    delay(1000L)
 | 
			
		||||
                    RedCommon(it.context).also(::println)
 | 
			
		||||
                    },
 | 
			
		||||
                    StateHandlerHolder(RedCommon::class) {
 | 
			
		||||
                }
 | 
			
		||||
                strictlyOn<RedCommon> {
 | 
			
		||||
                    delay(1000L)
 | 
			
		||||
                    GreenCommon(it.context).also(::println)
 | 
			
		||||
                }
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
                this.statesManager = statesManager
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            initialStates.forEach { statesManager.startChain(it) }
 | 
			
		||||
            initialStates.forEach { machine.startChain(it) }
 | 
			
		||||
 | 
			
		||||
            val scope = CoroutineScope(Dispatchers.Default)
 | 
			
		||||
            machine.start(scope).join()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user