commonMain.s2.sourcing.dsl.view.ViewLoader.kt Maven / Gradle / Ivy
package s2.sourcing.dsl.view
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.fold
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.flow.transform
import s2.dsl.automate.Evt
import s2.dsl.automate.model.WithS2Id
import s2.sourcing.dsl.Loader
import s2.sourcing.dsl.event.EventRepository
open class ViewLoader(
private val eventRepository: EventRepository,
private val view: View
): Loader where
EVENT: Evt,
EVENT: WithS2Id {
override suspend fun load(id: ID & Any): ENTITY? {
return eventRepository.load(id).let { events ->
load(events)
}
}
override suspend fun load(events: Flow): ENTITY? {
return evolve(events, null)
}
override suspend fun loadAndEvolve(id: ID & Any, news: Flow): ENTITY? {
return eventRepository.load(id).let { events ->
load(events)
}.let { entity ->
evolve(news, entity)
}
}
override suspend fun evolve(events: Flow, entity: ENTITY?): ENTITY? {
return events.fold(entity) { updated, event ->
view.evolve(event, updated)
}
}
override suspend fun reloadHistory(): List = eventRepository.loadAll()
.groupBy { event -> event.s2Id() }
.reducePerKey(::load)
.mapNotNull{it}
.toList()
// https://stackoverflow.com/a/58678049
// private fun Flow.groupBy(getKey: (T) -> K): Flow>> = flow {
// val storage = mutableMapOf>()
// try {
// collect { t ->
// val key = getKey(t)
// println("Handle ${key}")
// storage.getOrPut(key) {
// Channel(32).also { emit(key to it.consumeAsFlow()) }
// }.send(t)
// }
// } finally {
// println("Handle groupBy finally")
// storage.values.forEach { chan ->
// try {
// println("Handle groupBy finally ${chan}")
// chan.close()
// } catch (e: Exception) {
// println("Handle groupBy finally exception: ${e}")
// }
// }
// }
// }
suspend fun Flow.groupBy(keySelector: suspend (T) -> K): Map> {
val resultMap = mutableMapOf>()
transform { value ->
val key = keySelector(value)
val list = resultMap.getOrPut(key) { mutableListOf() }
list.add(value)
emit(resultMap)
}.toList()
return resultMap.mapValues { it.value.asFlow() }
}
@OptIn(FlowPreview::class)
private fun Map>.reducePerKey(reduce: suspend (Flow) -> R): Flow {
return this.values.asFlow().map { flow ->
reduce(flow)
}
}
}