mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-11-17 22:03:50 +00:00
commit
960c38b696
@ -1,5 +1,14 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.5.13
|
||||||
|
|
||||||
|
* `Common`:
|
||||||
|
* Add functionality for multiplatform working with files:
|
||||||
|
* Main class for files `MPPFile`
|
||||||
|
* Inline class for filenames work encapsulation `FileName`
|
||||||
|
* `FSM`
|
||||||
|
* Module inited and in preview state
|
||||||
|
|
||||||
## 0.5.12
|
## 0.5.12
|
||||||
|
|
||||||
* `Common`:
|
* `Common`:
|
||||||
|
@ -5,3 +5,18 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
jvmMain {
|
||||||
|
dependencies {
|
||||||
|
api project(":micro_utils.coroutines")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
androidMain {
|
||||||
|
dependencies {
|
||||||
|
api project(":micro_utils.coroutines")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,9 +7,17 @@ import kotlinx.serialization.encoding.Decoder
|
|||||||
import kotlinx.serialization.encoding.Encoder
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
|
||||||
typealias ByteArrayAllocator = () -> ByteArray
|
typealias ByteArrayAllocator = () -> ByteArray
|
||||||
|
typealias SuspendByteArrayAllocator = suspend () -> ByteArray
|
||||||
|
|
||||||
val ByteArray.asAllocator: ByteArrayAllocator
|
val ByteArray.asAllocator: ByteArrayAllocator
|
||||||
get() = { this }
|
get() = { this }
|
||||||
|
val ByteArray.asSuspendAllocator: SuspendByteArrayAllocator
|
||||||
|
get() = { this }
|
||||||
|
val ByteArrayAllocator.asSuspendAllocator: SuspendByteArrayAllocator
|
||||||
|
get() = { this() }
|
||||||
|
suspend fun SuspendByteArrayAllocator.asAllocator(): ByteArrayAllocator {
|
||||||
|
return invoke().asAllocator
|
||||||
|
}
|
||||||
|
|
||||||
object ByteArrayAllocatorSerializer : KSerializer<ByteArrayAllocator> {
|
object ByteArrayAllocatorSerializer : KSerializer<ByteArrayAllocator> {
|
||||||
private val realSerializer = ByteArraySerializer()
|
private val realSerializer = ByteArraySerializer()
|
||||||
@ -17,7 +25,7 @@ object ByteArrayAllocatorSerializer : KSerializer<ByteArrayAllocator> {
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): ByteArrayAllocator {
|
override fun deserialize(decoder: Decoder): ByteArrayAllocator {
|
||||||
val bytes = realSerializer.deserialize(decoder)
|
val bytes = realSerializer.deserialize(decoder)
|
||||||
return { bytes }
|
return bytes.asAllocator
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, value: ByteArrayAllocator) {
|
override fun serialize(encoder: Encoder, value: ByteArrayAllocator) {
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@JvmInline
|
||||||
|
value class FileName(val string: String) {
|
||||||
|
val name: String
|
||||||
|
get() = string.takeLastWhile { it != '/' }
|
||||||
|
val extension: String
|
||||||
|
get() = name.takeLastWhile { it != '.' }
|
||||||
|
val nameWithoutExtension: String
|
||||||
|
get() {
|
||||||
|
val filename = name
|
||||||
|
return filename.indexOfLast { it == '.' }.takeIf { it > -1 } ?.let {
|
||||||
|
filename.substring(0, it)
|
||||||
|
} ?: filename
|
||||||
|
}
|
||||||
|
override fun toString(): String = string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PreviewFeature
|
||||||
|
expect class MPPFile
|
||||||
|
|
||||||
|
expect val MPPFile.filename: FileName
|
||||||
|
expect val MPPFile.filesize: Long
|
||||||
|
expect val MPPFile.bytesAllocator: SuspendByteArrayAllocator
|
||||||
|
suspend fun MPPFile.bytes() = bytesAllocator()
|
||||||
|
|
@ -0,0 +1,32 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import org.khronos.webgl.ArrayBuffer
|
||||||
|
import org.w3c.dom.ErrorEvent
|
||||||
|
import org.w3c.files.File
|
||||||
|
import org.w3c.files.FileReader
|
||||||
|
import kotlin.js.Promise
|
||||||
|
|
||||||
|
actual typealias MPPFile = File
|
||||||
|
|
||||||
|
fun MPPFile.readBytesPromise() = Promise<ByteArray> { success, failure ->
|
||||||
|
val reader = FileReader()
|
||||||
|
reader.onload = {
|
||||||
|
success((reader.result as ArrayBuffer).toByteArray())
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
reader.onerror = {
|
||||||
|
failure(Exception((it as ErrorEvent).message))
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
reader.readAsArrayBuffer(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun MPPFile.dirtyReadBytes(): ByteArray = readBytesPromise().await()
|
||||||
|
|
||||||
|
actual val MPPFile.filename: FileName
|
||||||
|
get() = FileName(name)
|
||||||
|
actual val MPPFile.filesize: Long
|
||||||
|
get() = size.toLong()
|
||||||
|
@Warning("That is not optimized version of bytes allocator. Use asyncBytesAllocator everywhere you can")
|
||||||
|
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
|
||||||
|
get() = ::dirtyReadBytes
|
@ -0,0 +1,8 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlin.coroutines.*
|
||||||
|
import kotlin.js.Promise
|
||||||
|
|
||||||
|
suspend fun <T> Promise<T>.await(): T = suspendCoroutine { cont ->
|
||||||
|
then({ cont.resume(it) }, { cont.resumeWithException(it) })
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.doInIO
|
||||||
|
import dev.inmo.micro_utils.coroutines.doOutsideOfCoroutine
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
actual typealias MPPFile = File
|
||||||
|
|
||||||
|
actual val MPPFile.filename: FileName
|
||||||
|
get() = FileName(name)
|
||||||
|
actual val MPPFile.filesize: Long
|
||||||
|
get() = length()
|
||||||
|
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
|
||||||
|
get() = {
|
||||||
|
doInIO {
|
||||||
|
doOutsideOfCoroutine {
|
||||||
|
readBytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
fsm/common/build.gradle
Normal file
17
fsm/common/build.gradle
Normal file
@ -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(":micro_utils.coroutines")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package dev.inmo.micro_utils.fsm.common
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
val context: Any
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package dev.inmo.micro_utils.fsm.common
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
class StateHandlerHolder<I : State>(
|
||||||
|
private val inputKlass: KClass<I>,
|
||||||
|
private val strict: Boolean = false,
|
||||||
|
private val delegateTo: StatesHandler<I>
|
||||||
|
) : StatesHandler<State> {
|
||||||
|
fun checkHandleable(state: State) = state::class == inputKlass || (!strict && inputKlass.isInstance(state))
|
||||||
|
|
||||||
|
override suspend fun StatesMachine.handleState(state: State): State? {
|
||||||
|
return delegateTo.run { handleState(state as I) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package dev.inmo.micro_utils.fsm.common
|
||||||
|
|
||||||
|
fun interface StatesHandler<I : State> {
|
||||||
|
suspend fun StatesMachine.handleState(state: I): State?
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package dev.inmo.micro_utils.fsm.common
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.asFlow
|
||||||
|
|
||||||
|
private suspend fun <I : State> StatesMachine.launchStateHandling(
|
||||||
|
state: State,
|
||||||
|
handlers: List<StateHandlerHolder<out I>>
|
||||||
|
): State? {
|
||||||
|
return handlers.firstOrNull { it.checkHandleable(state) } ?.run {
|
||||||
|
handleState(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (State) -> Unit = { state: State ->
|
||||||
|
val newState = launchStateHandling(state, handlers)
|
||||||
|
if (newState != null) {
|
||||||
|
statesManager.update(state, newState)
|
||||||
|
} else {
|
||||||
|
statesManager.endChain(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statesManager.onStartChain.subscribeSafelyWithoutExceptions(this) {
|
||||||
|
launch { statePerformer(it) }
|
||||||
|
}
|
||||||
|
statesManager.onChainStateUpdated.subscribeSafelyWithoutExceptions(this) {
|
||||||
|
launch { statePerformer(it.second) }
|
||||||
|
}
|
||||||
|
|
||||||
|
statesManager.getActiveStates().forEach {
|
||||||
|
launch { statePerformer(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun startChain(state: State) {
|
||||||
|
statesManager.startChain(state)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package dev.inmo.micro_utils.fsm.common
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
|
||||||
|
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: 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: 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: State)
|
||||||
|
|
||||||
|
suspend fun getActiveStates(): List<State>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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<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, State>()
|
||||||
|
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<State> = contextsToStates.values.toList()
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package dev.inmo.micro_utils.fsm.common.dsl
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.fsm.common.*
|
||||||
|
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()
|
53
fsm/common/src/jvmTest/kotlin/PlayableMain.kt
Normal file
53
fsm/common/src/jvmTest/kotlin/PlayableMain.kt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
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 kotlinx.coroutines.*
|
||||||
|
|
||||||
|
sealed interface TrafficLightState : State {
|
||||||
|
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
|
||||||
|
|
||||||
|
class PlayableMain {
|
||||||
|
// @Test
|
||||||
|
fun test() {
|
||||||
|
runBlocking {
|
||||||
|
val countOfTrafficLights = 10
|
||||||
|
val initialStates = (0 until countOfTrafficLights).map {
|
||||||
|
when (0/*Random.nextInt(3)*/) {
|
||||||
|
0 -> GreenCommon(it)
|
||||||
|
1 -> YellowCommon(it)
|
||||||
|
else -> RedCommon(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val statesManager = InMemoryStatesManager()
|
||||||
|
|
||||||
|
val machine = buildFSM {
|
||||||
|
strictlyOn<GreenCommon> {
|
||||||
|
delay(1000L)
|
||||||
|
YellowCommon(it.context).also(::println)
|
||||||
|
}
|
||||||
|
strictlyOn<YellowCommon> {
|
||||||
|
delay(1000L)
|
||||||
|
RedCommon(it.context).also(::println)
|
||||||
|
}
|
||||||
|
strictlyOn<RedCommon> {
|
||||||
|
delay(1000L)
|
||||||
|
GreenCommon(it.context).also(::println)
|
||||||
|
}
|
||||||
|
this.statesManager = statesManager
|
||||||
|
}
|
||||||
|
|
||||||
|
initialStates.forEach { machine.startChain(it) }
|
||||||
|
|
||||||
|
val scope = CoroutineScope(Dispatchers.Default)
|
||||||
|
machine.start(scope).join()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
fsm/common/src/main/AndroidManifest.xml
Normal file
1
fsm/common/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.fsm.common"/>
|
18
fsm/repos/common/build.gradle
Normal file
18
fsm/repos/common/build.gradle
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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(":micro_utils.fsm.common")
|
||||||
|
api project(":micro_utils.repos.common")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
class KeyValueBasedStatesManager(
|
||||||
|
private val keyValueRepo: KeyValueRepo<Any, State>,
|
||||||
|
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 _onEndChain = MutableSharedFlow<State>(0)
|
||||||
|
override val onEndChain: Flow<State> = _onEndChain.asSharedFlow()
|
||||||
|
|
||||||
|
override val onStartChain: Flow<State> = 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<State> {
|
||||||
|
return keyValueRepo.getAll { keys(it) }.map { it.second }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified TargetContextType, reified TargetStateType> createStatesManager(
|
||||||
|
targetKeyValueRepo: KeyValueRepo<TargetContextType, TargetStateType>,
|
||||||
|
noinline contextToOutTransformer: suspend Any.() -> TargetContextType,
|
||||||
|
noinline stateToOutTransformer: suspend State.() -> TargetStateType,
|
||||||
|
noinline outToContextTransformer: suspend TargetContextType.() -> Any,
|
||||||
|
noinline outToStateTransformer: suspend TargetStateType.() -> State,
|
||||||
|
) = KeyValueBasedStatesManager(
|
||||||
|
targetKeyValueRepo.withMapper<Any, State, TargetContextType, TargetStateType>(
|
||||||
|
contextToOutTransformer,
|
||||||
|
stateToOutTransformer,
|
||||||
|
outToContextTransformer,
|
||||||
|
outToStateTransformer
|
||||||
|
)
|
||||||
|
)
|
1
fsm/repos/common/src/main/AndroidManifest.xml
Normal file
1
fsm/repos/common/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.fsm.repos.common"/>
|
@ -45,5 +45,5 @@ dokka_version=1.4.32
|
|||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.5.12
|
version=0.5.13
|
||||||
android_code_version=53
|
android_code_version=54
|
||||||
|
@ -12,7 +12,7 @@ open class TypedSerializer<T : Any>(
|
|||||||
) : KSerializer<T> {
|
) : KSerializer<T> {
|
||||||
protected val serializers = presetSerializers.toMutableMap()
|
protected val serializers = presetSerializers.toMutableMap()
|
||||||
@InternalSerializationApi
|
@InternalSerializationApi
|
||||||
override open val descriptor: SerialDescriptor = buildSerialDescriptor(
|
open override val descriptor: SerialDescriptor = buildSerialDescriptor(
|
||||||
"TextSourceSerializer",
|
"TextSourceSerializer",
|
||||||
SerialKind.CONTEXTUAL
|
SerialKind.CONTEXTUAL
|
||||||
) {
|
) {
|
||||||
@ -21,7 +21,7 @@ open class TypedSerializer<T : Any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@InternalSerializationApi
|
@InternalSerializationApi
|
||||||
override open fun deserialize(decoder: Decoder): T {
|
open override fun deserialize(decoder: Decoder): T {
|
||||||
return decoder.decodeStructure(descriptor) {
|
return decoder.decodeStructure(descriptor) {
|
||||||
var type: String? = null
|
var type: String? = null
|
||||||
lateinit var result: T
|
lateinit var result: T
|
||||||
@ -50,7 +50,7 @@ open class TypedSerializer<T : Any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@InternalSerializationApi
|
@InternalSerializationApi
|
||||||
override open fun serialize(encoder: Encoder, value: T) {
|
open override fun serialize(encoder: Encoder, value: T) {
|
||||||
encoder.encodeStructure(descriptor) {
|
encoder.encodeStructure(descriptor) {
|
||||||
val valueSerializer = value::class.serializer()
|
val valueSerializer = value::class.serializer()
|
||||||
val type = serializers.keys.first { serializers[it] == valueSerializer }
|
val type = serializers.keys.first { serializers[it] == valueSerializer }
|
||||||
|
@ -28,6 +28,9 @@ String[] includes = [
|
|||||||
":serialization:encapsulator",
|
":serialization:encapsulator",
|
||||||
":serialization:typed_serializer",
|
":serialization:typed_serializer",
|
||||||
|
|
||||||
|
":fsm:common",
|
||||||
|
":fsm:repos:common",
|
||||||
|
|
||||||
":dokka"
|
":dokka"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user