All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.tech.skot.model.SKDataWrapper.kt Maven / Gradle / Ivy

package tech.skot.model

import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock


class SKDataWrapper(
    private val defaultValue: D,
    private val getSKData: suspend () -> SKData?,
    private val newSKDataFlow: Flow<*>,
    private val scope: CoroutineScope
) : SKData {

    override val flow =
        MutableStateFlow>(DatedData(defaultValue, currentTimeMillis()))
    override val defaultValidity
        get() = _currentWrappedSKData?.defaultValidity ?: Long.MAX_VALUE

    override val _current: DatedData
        get() = flow.value


    private var _currentWrappedSKData: SKData? = null
    private var currentCollectJob: Job? = null

    private suspend fun subscribeToWrapped(throwError:Boolean) {
        getSKData().let { newSKToBeWrapped ->
            _currentWrappedSKData = newSKToBeWrapped
            currentCollectJob?.cancel()

            if (newSKToBeWrapped != null) {
                val errorToThrow:Exception? = try {
                    newSKToBeWrapped.get(validity = Long.MAX_VALUE)
                    flow.value = newSKToBeWrapped._current ?: DatedData(defaultValue)
                    null
                }
                catch (ex:Exception) {
                    if (ex !is CancellationException) {
                        flow.value = DatedData(defaultValue)
                        ex
                    }
                    else {
                        null
                    }
                }
                finally {
                    currentCollectJob = scope.launch {
                        newSKToBeWrapped.flow.collect {
                            it?.let {
                                flow.value = DatedData(it.data, it.timestamp)
                            }
                        }
                    }
                }
                if (throwError && errorToThrow != null) {
                    throw errorToThrow
                }

            } else {
                flow.value = DatedData(defaultValue)
            }
        }
    }

    private var wrappedSkChangedJob: Job? = null

    private var launchMutex = Mutex()
    private suspend fun launch() {

        launchMutex.withLock {
            if (wrappedSkChangedJob == null) {
                subscribeToWrapped(true)
                wrappedSkChangedJob = scope.launch {
                    newSKDataFlow.drop(1).collect {
                        subscribeToWrapped(false)
                    }
                }
            }
        }
    }


    //pour arrêter manuellement le wrapper, pricuipalement utilisé pour les tests, si on ne maîtrise pas le scope
    fun cancel() {
        wrappedSkChangedJob?.cancel()
    }

    override suspend fun update(): D {
        if (wrappedSkChangedJob == null) {
            launch()
        }
        return _currentWrappedSKData?.update() ?: defaultValue
    }

    override suspend fun get(validity: Long?): D {
        if (wrappedSkChangedJob == null) {
            launch()
        }
        return super.get(validity)
    }

    override suspend fun fallBackValue(): D? {
        return _current.data
    }
}




fun  CoroutineScope.wrap(
    stepData: SKData,
    defaultValue: R,
    targetSKData: suspend D.() -> SKData?
): SKDataWrapper {
    return SKDataWrapper(
        defaultValue = defaultValue,
        getSKData = {
            stepData.get()?.let { targetSKData(it) }
        },
        newSKDataFlow = stepData.flow,
        scope = this
    )
}


fun  SKData.wrap(
    scope: CoroutineScope,
    defaultValue: R,
    targetSKData: suspend D.() -> SKData?
): SKDataWrapper {
    return scope.wrap(this, defaultValue, targetSKData)
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy