package dev.inmo.micro_utils.repos.cache.full import dev.inmo.micro_utils.common.* import dev.inmo.micro_utils.pagination.Pagination import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.cache.* import dev.inmo.micro_utils.repos.cache.cache.FullKVCache import dev.inmo.micro_utils.repos.cache.cache.KVCache import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers open class FullReadCRUDCacheRepo( protected open val parentRepo: ReadCRUDRepo, protected open val kvCache: FullKVCache, protected open val idGetter: (ObjectType) -> IdType ) : ReadCRUDRepo, FullCacheRepo { protected inline fun doOrTakeAndActualize( action: FullKVCache.() -> Optional, actionElse: ReadCRUDRepo.() -> T, actualize: FullKVCache.(T) -> Unit ): T { kvCache.action().onPresented { return it }.onAbsent { return parentRepo.actionElse().also { kvCache.actualize(it) } } error("The result should be returned above") } protected open suspend fun actualizeAll() { kvCache.clear() doForAllWithNextPaging { parentRepo.getByPagination(it).also { kvCache.set(it.results.associateBy { idGetter(it) }) } } } override suspend fun getByPagination(pagination: Pagination): PaginationResult = doOrTakeAndActualize( { values(pagination).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull }, { getByPagination(pagination) }, { if (it.results.isNotEmpty()) actualizeAll() } ) override suspend fun getIdsByPagination(pagination: Pagination): PaginationResult = doOrTakeAndActualize( { keys(pagination).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull }, { getIdsByPagination(pagination) }, { if (it.results.isNotEmpty()) actualizeAll() } ) override suspend fun count(): Long = doOrTakeAndActualize( { count().takeIf { it != 0L }.optionalOrAbsentIfNull }, { count() }, { if (it != 0L) actualizeAll() } ) override suspend fun contains(id: IdType): Boolean = doOrTakeAndActualize( { contains(id).takeIf { it }.optionalOrAbsentIfNull }, { contains(id) }, { if (it) parentRepo.getById(id) ?.let { set(id, it) } } ) override suspend fun getById(id: IdType): ObjectType? = doOrTakeAndActualize( { get(id) ?.optional ?: Optional.absent() }, { getById(id) }, { it ?.let { set(idGetter(it), it) } } ) } fun ReadCRUDRepo.cached( kvCache: FullKVCache, idGetter: (ObjectType) -> IdType ) = FullReadCRUDCacheRepo(this, kvCache, idGetter) open class FullCRUDCacheRepo( override val parentRepo: CRUDRepo, kvCache: FullKVCache, scope: CoroutineScope = CoroutineScope(Dispatchers.Default), idGetter: (ObjectType) -> IdType ) : FullReadCRUDCacheRepo( parentRepo, kvCache, idGetter ), WriteCRUDRepo by WriteCRUDCacheRepo( parentRepo, kvCache, scope, idGetter ), CRUDRepo fun CRUDRepo.cached( kvCache: FullKVCache, scope: CoroutineScope = CoroutineScope(Dispatchers.Default), idGetter: (ObjectType) -> IdType ) = FullCRUDCacheRepo(this, kvCache, scope, idGetter)