package dev.inmo.micro_utils.repos import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.utils.doAllWithCurrentPaging import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging import dev.inmo.micro_utils.pagination.utils.paginate import kotlinx.coroutines.flow.Flow /** * Read part of [KeyValueRepo] * * @param Key This type will be used as key in all operations related to searches of data * @param Value This type will be used as returning data in most "get" operations */ interface ReadKeyValueRepo : Repo { /** * @return Result [Value] in case when it is presented in repo by its [k] or null otherwise */ suspend fun get(k: Key): Value? /** * This method should use sorted by [Key]s search and return the [PaginationResult]. By default, it should use * ascending sort for [Key]s */ suspend fun values(pagination: Pagination, reversed: Boolean = false): PaginationResult /** * This method should use sorted by [Key]s search and return the [PaginationResult]. By default, it should use * ascending sort for [Key]s */ suspend fun keys(pagination: Pagination, reversed: Boolean = false): PaginationResult /** * This method should use sorted by [Key]s search and return the [PaginationResult]. By default, it should use * ascending sort for [Key]s. * * **DEFAULT REALIZATION IS NOT OPTIMAL AND HAS BEEN ADDED TO COVER CASES OF DIFFERENT COMMON MAPPINGS AND TRANSFORMATIONS** * * @param v This value should be used to exclude from search the items with different [Value]s */ suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean = false): PaginationResult { return getAllWithNextPaging { keys(it) }.filter { get(it) == v }.paginate(pagination, reversed) } /** * @return true if [key] is presented in current collection or false otherwise */ suspend fun contains(key: Key): Boolean /** * @return count of all collection objects */ suspend fun count(): Long } typealias ReadStandardKeyValueRepo = ReadKeyValueRepo /** * Write part of [KeyValueRepo] * * @param Key This type will be used as key in all operations related to changes of data * @param Value This type will be used as incoming data in most operations */ interface WriteKeyValueRepo : Repo { /** * This flow must emit data each time when data by [Key] has been changed with [set] method or in any other way * excluding cases of data removing * * @see onValueRemoved */ val onNewValue: Flow> /** * This flow must emit data each time when data by [Key] has been removed with [unset]/[unsetWithValues] methods or * in any other way * * @see onNewValue */ val onValueRemoved: Flow /** * Will set as batch [toSet] data in current repo. Must pass the data which were successfully updated in repo to * [onNewValue] */ suspend fun set(toSet: Map) /** * Will unset as batch data with keys from [toUnset]. Must pass the [Key]s which were successfully removed in repo to * [onValueRemoved] */ suspend fun unset(toUnset: List) /** * Will unset as batch data with values from [toUnset]. Must pass the [Key]s which were successfully removed in repo * to [onValueRemoved] */ suspend fun unsetWithValues(toUnset: List) } typealias WriteStandardKeyValueRepo = WriteKeyValueRepo suspend inline fun WriteKeyValueRepo.set( vararg toSet: Pair ) = set(toSet.toMap()) suspend inline fun WriteKeyValueRepo.set( toSet: List> ) = set(toSet.toMap()) suspend inline fun WriteKeyValueRepo.set( k: Key, v: Value ) = set(k to v) suspend inline fun WriteKeyValueRepo.unset( vararg k: Key ) = unset(k.toList()) suspend inline fun WriteKeyValueRepo.unsetWithValues( vararg v: Value ) = unsetWithValues(v.toList()) /** * Full version of standard key-value repository with all set/unset/clear/get methods */ interface KeyValueRepo : ReadKeyValueRepo, WriteKeyValueRepo { /** * By default, will walk throw all the [keys] with [Value]s from [toUnset] and run [doAllWithCurrentPaging] with * [unset] of found data [Key]s */ override suspend fun unsetWithValues(toUnset: List) = toUnset.forEach { v -> doAllWithCurrentPaging { keys(v, it).also { unset(it.results) } } } /** * By default, will remove all the data of current repo using [doAllWithCurrentPaging], [keys] and [unset] */ suspend fun clear() { var count: Int do { count = count().takeIf { it < Int.MAX_VALUE } ?.toInt() ?: Int.MAX_VALUE keys(FirstPagePagination(count)).also { unset(it.results) } } while(count > 0) } } typealias StandardKeyValueRepo = KeyValueRepo class DelegateBasedKeyValueRepo( readDelegate: ReadKeyValueRepo, writeDelegate: WriteKeyValueRepo ) : KeyValueRepo, ReadKeyValueRepo by readDelegate, WriteKeyValueRepo by writeDelegate