
commonMain.tech.skot.model.SKData.kt Maven / Gradle / Ivy
package tech.skot.model
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.map
import kotlin.math.min
interface SKData {
val flow: Flow?>
val defaultValidity: Long
val _current: DatedData?
suspend fun update(): D
suspend fun fallBackValue(): D?
open suspend fun get(validity: Long? = null): D {
val datedCurrentValue = _current
val usedValidity = validity ?: defaultValidity
return if (datedCurrentValue == null || usedValidity == 0L || ((datedCurrentValue.timestamp + usedValidity) > 0 && currentTimeMillis() > (datedCurrentValue.timestamp + usedValidity))) {
update()
} else {
datedCurrentValue.data
}
}
suspend fun getDirect() = _current?.data ?: get(validity = Long.MAX_VALUE)
}
interface SKPaginatedData:SKData> {
suspend fun oneMorePage()
val mayHaveAnotherPage:Boolean
}
fun SKData.map(transform: (d: D) -> O): SKData {
return object : SKData {
override val defaultValidity = [email protected]
override val flow = [email protected] {
it?.let { transformDatedData(it) }
}
override val _current
get() = this@map._current?.let { transformDatedData(it) }
private fun transformDatedData(datedData: DatedData) =
transform(datedData.data).let { transformedValue ->
DatedData(
data = transformedValue,
timestamp = datedData.timestamp
)
}
override suspend fun update(): O {
return transform([email protected]())
}
override suspend fun get(validity: Long?): O {
return transform([email protected](validity))
}
override suspend fun fallBackValue(): O? {
return [email protected]()?.let(transform)
}
}
}
fun SKData.mapSuspend(transform: suspend (d: D) -> O): SKData {
return object : SKData {
override val defaultValidity = [email protected]
override val flow = [email protected] {
it?.let { transformDatedData(it) }
}
private var trueCurrent: DatedData? = null
private var transformedCurrent: DatedData? = null
override val _current: DatedData?
get() {
return if (transformedCurrent == this@mapSuspend._current) {
trueCurrent
} else {
null
}
}
private suspend fun transformDatedData(datedData: DatedData) =
transform(datedData.data).let { transformedValue ->
DatedData(
data = transformedValue,
timestamp = datedData.timestamp
).also {
trueCurrent = it
transformedCurrent = datedData
}
}
override suspend fun update(): O {
val originUpdate = [email protected]()
return transform(originUpdate).also {
trueCurrent = DatedData(it)
transformedCurrent = DatedData(
originUpdate,
this@mapSuspend._current?.timestamp ?: currentTimeMillis()
)
}
}
override suspend fun fallBackValue(): O? {
return [email protected]()?.let {
transform(it)
}
}
}
}
fun SKData.combine(other: SKData) = combineSKData(this, other)
fun combineSKData(
data1: SKData,
data2: SKData
): SKData> {
return object : SKData> {
override val defaultValidity: Long by lazy {
min(data1.defaultValidity, data2.defaultValidity)
}
override val _current: DatedData>?
get() = buildPair(data1._current, data2._current)
private fun buildPair(
datedData1: DatedData?,
datedData2: DatedData?
): DatedData>? =
if (datedData1 != null && datedData2 != null) {
DatedData(
data = Pair(datedData1.data, datedData2.data),
timestamp = min(datedData1.timestamp, datedData2.timestamp)
)
} else {
null
}
override val flow: Flow>?> by lazy {
combineTransform(
data1.flow,
data2.flow
) { datedDataFlow1, datedDataFlow2 ->
buildPair(datedDataFlow1, datedDataFlow2)?.let { emit(it) }
}
}
override suspend fun update(): Pair {
return coroutineScope {
val updatedData1 =
async {
data1.update()
}
val updatedData2 =
async {
data2.update()
}
Pair(updatedData1.await(), updatedData2.await())
}
}
override suspend fun get(validity: Long?): Pair {
return coroutineScope {
val gettedData1 = async {
data1.get(validity)
}
val gettedData2 = async {
data2.get(validity)
}
Pair(gettedData1.await(), gettedData2.await())
}
}
override suspend fun fallBackValue(): Pair? {
val data1FallBackValue = data1.fallBackValue()
val data2FallBackValue = data2.fallBackValue()
return if (data1FallBackValue != null && data2FallBackValue != null) {
Pair(data1FallBackValue, data2FallBackValue)
} else {
null
}
}
}
}
fun combineSKData(
data1: SKData,
data2: SKData,
data3: SKData
): SKData> {
return object : SKData> {
override val defaultValidity by lazy {
min(min(data1.defaultValidity, data2.defaultValidity), data3.defaultValidity)
}
override val _current: DatedData>?
get() = buildTriple(data1._current, data2._current, data3._current)
private fun buildTriple(
datedData1: DatedData?,
datedData2: DatedData?,
datedData3: DatedData?
): DatedData>? =
if (datedData1 != null && datedData2 != null && datedData3 != null) {
DatedData(
data = Triple(datedData1.data, datedData2.data, datedData3.data),
timestamp = min(
min(datedData1.timestamp, datedData2.timestamp),
datedData3.timestamp
)
)
} else {
null
}
override val flow: Flow>?> by lazy {
combineTransform(
data1.flow,
data2.flow,
data3.flow
) { datedDataFlow1, datedDataFlow2, datedDataFlow3 ->
buildTriple(datedDataFlow1, datedDataFlow2, datedDataFlow3)?.let { emit(it) }
}
}
override suspend fun update(): Triple {
return coroutineScope {
val updatedData1 =
async {
data1.update()
}
val updatedData2 =
async {
data2.update()
}
val updatedData3 =
async {
data3.update()
}
Triple(updatedData1.await(), updatedData2.await(), updatedData3.await())
}
}
override suspend fun get(validity: Long?): Triple {
return coroutineScope {
val gettedData1 = async {
data1.get(validity)
}
val gettedData2 = async {
data2.get(validity)
}
val gettedData3 = async {
data3.get(validity)
}
Triple(gettedData1.await(), gettedData2.await(), gettedData3.await())
}
}
override suspend fun fallBackValue(): Triple? {
val data1FallBackValue = data1.fallBackValue()
val data2FallBackValue = data2.fallBackValue()
val data3FallBackValue = data3.fallBackValue()
return if (data1FallBackValue != null && data2FallBackValue != null && data3FallBackValue != null) {
Triple(data1FallBackValue, data2FallBackValue, data3FallBackValue)
} else {
null
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy