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

kernl.data.repo.associativecache.BaseAssociativeCacheLiveRepository.kt Maven / Gradle / Ivy

Go to download

Kernl: A Kotlin Symbol Processing (KSP) library for automatic repository generation.

There is a newer version: 0.0.1-beta6
Show newest version
package io.github.mattshoe.shoebox.kernl.data.repo.associativecache

import io.github.mattshoe.shoebox.kernl.data.DataResult
import io.github.mattshoe.shoebox.kernl.data.source.DataSource
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlin.reflect.KClass

abstract class BaseAssociativeCacheLiveRepository(
    private val dispatcher: CoroutineDispatcher = Dispatchers.IO
):
    AssociativeMemoryCacheLiveRepository {

    private data class CacheEntry(
        val dataSource: DataSource
    )
    private val dataCacheMutex = Mutex()
    private val dataCache = mutableMapOf>()
    abstract val dataType: KClass

    abstract suspend fun fetchData(params: TParams): TData

    override fun stream(params: TParams, forceFetch: Boolean): Flow> {
        return initializeNewStream(params, forceFetch)
            .catch {
                emit(DataResult.Error(it))
            }
    }

    override fun latestValue(params: TParams): DataResult? {
        return try {
            dataCache[params]?.dataSource?.value
        } catch (e: Throwable) {
            null
        }
    }

    override suspend fun refresh(params: TParams) {
        withContext(dispatcher) {
            with (findDataCacheEntry(params)) {
                dataSource.initialize(forceFetch = true) {
                    fetchData(params)
                }
            }
        }
    }

    override suspend fun invalidate(params: TParams) {
        withContext(dispatcher) {
            dataCache[params]?.dataSource?.invalidate()
        }
    }

    override suspend fun invalidateAll() {
        withContext(dispatcher) {
            invalidateAllDataSources()
        }
    }

    private fun initializeNewStream(params: TParams, forceFetch: Boolean): Flow> {
        return channelFlow {
            withContext(Dispatchers.IO) {
                loadDataIntoCache(params, forceFetch)
                getDataFromDataCache(params)
                    .collectLatest {
                        send(it)
                    }
            }
        }
    }

    private suspend fun loadDataIntoCache(params: TParams, forceFetch: Boolean) {
        dataCacheMutex.withLock {
            with (findDataCacheEntry(params)) {
                dataSource.initialize(forceFetch) {
                    fetchData(params)
                }

                dataCache[params] = this
            }
        }
    }

    private suspend fun getDataFromDataCache(params: TParams): Flow> {
        return dataCacheMutex.withLock {
            dataCache[params]
                ?.dataSource
                ?.data
                ?: flowOf(
                    DataResult.Error(
                        IllegalAccessError("No data found in the network cache!")
                    )
                )
        }
    }

    private fun findDataCacheEntry(params: TParams): CacheEntry {
        return dataCache[params]
            ?: CacheEntry(
                DataSource.Builder
                    .memoryCache(dataType)
                    .dispatcher(dispatcher)
                    .build()
            )
    }

    private suspend fun invalidateAllDataSources() {
        dataCache.values.forEach {
            it.dataSource.invalidate()
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy