mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-11-17 22:03:50 +00:00
commit
960c38b696
@ -1,5 +1,14 @@
|
||||
# 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
|
||||
|
||||
* `Common`:
|
||||
|
@ -5,3 +5,18 @@ plugins {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
typealias ByteArrayAllocator = () -> ByteArray
|
||||
typealias SuspendByteArrayAllocator = suspend () -> ByteArray
|
||||
|
||||
val ByteArray.asAllocator: ByteArrayAllocator
|
||||
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> {
|
||||
private val realSerializer = ByteArraySerializer()
|
||||
@ -17,7 +25,7 @@ object ByteArrayAllocatorSerializer : KSerializer<ByteArrayAllocator> {
|
||||
|
||||
override fun deserialize(decoder: Decoder): ByteArrayAllocator {
|
||||
val bytes = realSerializer.deserialize(decoder)
|
||||
return { bytes }
|
||||
return bytes.asAllocator
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
group=dev.inmo
|
||||
version=0.5.12
|
||||
android_code_version=53
|
||||
version=0.5.13
|
||||
android_code_version=54
|
||||
|
@ -12,7 +12,7 @@ open class TypedSerializer<T : Any>(
|
||||
) : KSerializer<T> {
|
||||
protected val serializers = presetSerializers.toMutableMap()
|
||||
@InternalSerializationApi
|
||||
override open val descriptor: SerialDescriptor = buildSerialDescriptor(
|
||||
open override val descriptor: SerialDescriptor = buildSerialDescriptor(
|
||||
"TextSourceSerializer",
|
||||
SerialKind.CONTEXTUAL
|
||||
) {
|
||||
@ -21,7 +21,7 @@ open class TypedSerializer<T : Any>(
|
||||
}
|
||||
|
||||
@InternalSerializationApi
|
||||
override open fun deserialize(decoder: Decoder): T {
|
||||
open override fun deserialize(decoder: Decoder): T {
|
||||
return decoder.decodeStructure(descriptor) {
|
||||
var type: String? = null
|
||||
lateinit var result: T
|
||||
@ -50,7 +50,7 @@ open class TypedSerializer<T : Any>(
|
||||
}
|
||||
|
||||
@InternalSerializationApi
|
||||
override open fun serialize(encoder: Encoder, value: T) {
|
||||
open override fun serialize(encoder: Encoder, value: T) {
|
||||
encoder.encodeStructure(descriptor) {
|
||||
val valueSerializer = value::class.serializer()
|
||||
val type = serializers.keys.first { serializers[it] == valueSerializer }
|
||||
|
@ -28,6 +28,9 @@ String[] includes = [
|
||||
":serialization:encapsulator",
|
||||
":serialization:typed_serializer",
|
||||
|
||||
":fsm:common",
|
||||
":fsm:repos:common",
|
||||
|
||||
":dokka"
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user