mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-12-23 09:07:14 +00:00
commit
df3c01ff0a
19
CHANGELOG.md
19
CHANGELOG.md
@ -1,5 +1,24 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.16.8
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Ktor`: `2.2.2` -> `2.2.3`
|
||||||
|
* `Ktor`:
|
||||||
|
* `Client`
|
||||||
|
* Fixes in `HttpClient.uniUpload`
|
||||||
|
* `Server`
|
||||||
|
* Fixes in `PartData.FileItem.download`
|
||||||
|
* `Repos`:
|
||||||
|
* `Cache`:
|
||||||
|
* New type of caches: `FallbackCacheRepo`
|
||||||
|
* Fixes in `Write*` variants of cached repos
|
||||||
|
* New type `ActionWrapper`
|
||||||
|
* New `AutoRecache*` classes for all types of repos as `FallbackCacheRepo`s
|
||||||
|
* `Common`:
|
||||||
|
* New transformations for key-value and key-values vice-verse
|
||||||
|
* Fixes in `FileReadKeyValueRepo`
|
||||||
|
|
||||||
## 0.16.7
|
## 0.16.7
|
||||||
|
|
||||||
* `Common`:
|
* `Common`:
|
||||||
|
@ -14,5 +14,5 @@ crypto_js_version=4.1.1
|
|||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.16.7
|
version=0.16.8
|
||||||
android_code_version=175
|
android_code_version=176
|
||||||
|
@ -13,7 +13,7 @@ jb-dokka = "1.7.20"
|
|||||||
klock = "3.4.0"
|
klock = "3.4.0"
|
||||||
uuid = "0.6.0"
|
uuid = "0.6.0"
|
||||||
|
|
||||||
ktor = "2.2.2"
|
ktor = "2.2.3"
|
||||||
|
|
||||||
gh-release = "2.4.1"
|
gh-release = "2.4.1"
|
||||||
|
|
||||||
|
@ -17,8 +17,11 @@ import io.ktor.http.HttpStatusCode
|
|||||||
import io.ktor.http.Parameters
|
import io.ktor.http.Parameters
|
||||||
import io.ktor.http.content.PartData
|
import io.ktor.http.content.PartData
|
||||||
import kotlinx.serialization.DeserializationStrategy
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
|
import kotlinx.serialization.InternalSerializationApi
|
||||||
|
import kotlinx.serialization.SerializationStrategy
|
||||||
import kotlinx.serialization.StringFormat
|
import kotlinx.serialization.StringFormat
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.serializer
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,6 +32,7 @@ import java.io.File
|
|||||||
* in case you wish to pass other source of multipart binary data than regular file
|
* in case you wish to pass other source of multipart binary data than regular file
|
||||||
* @suppress
|
* @suppress
|
||||||
*/
|
*/
|
||||||
|
@OptIn(InternalSerializationApi::class)
|
||||||
actual suspend fun <T> HttpClient.uniUpload(
|
actual suspend fun <T> HttpClient.uniUpload(
|
||||||
url: String,
|
url: String,
|
||||||
data: Map<String, Any>,
|
data: Map<String, Any>,
|
||||||
@ -60,7 +64,7 @@ actual suspend fun <T> HttpClient.uniUpload(
|
|||||||
)
|
)
|
||||||
else -> append(
|
else -> append(
|
||||||
k,
|
k,
|
||||||
stringFormat.encodeToString(v)
|
stringFormat.encodeToString(v::class.serializer() as SerializationStrategy<in Any>, v)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ import java.io.File
|
|||||||
|
|
||||||
fun PartData.FileItem.download(target: File) {
|
fun PartData.FileItem.download(target: File) {
|
||||||
provider().use { input ->
|
provider().use { input ->
|
||||||
target.outputStream().use {
|
target.outputStream().asOutput().use {
|
||||||
input.copyTo(it.asOutput())
|
input.copyTo(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/FallbackCacheRepo.kt
vendored
Normal file
7
repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/FallbackCacheRepo.kt
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.cache
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any inheritor of this should work with next logic: try to take data from their original repo, if successful - save data to internal
|
||||||
|
* [dev.inmo.micro_utils.repos.cache.cache.FullKVCache] or try to take data from that internal cache
|
||||||
|
*/
|
||||||
|
interface FallbackCacheRepo : CacheRepo
|
37
repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/fallback/ActionWrapper.kt
vendored
Normal file
37
repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/fallback/ActionWrapper.kt
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.cache.fallback
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import kotlinx.coroutines.withTimeout
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Realizations should [wrap] the work with some conditions like retries on exceptions, calling timeout, etc.
|
||||||
|
*
|
||||||
|
* @see Timeouted
|
||||||
|
* @see Direct
|
||||||
|
*/
|
||||||
|
interface ActionWrapper {
|
||||||
|
/**
|
||||||
|
* Should execute [block] to take the result [T], but may return failure in case when something went wrong.
|
||||||
|
* This method should never throw any [Exception]
|
||||||
|
*/
|
||||||
|
suspend fun <T> wrap(block: suspend () -> T): Result<T>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type of [ActionWrapper]s will use [withTimeout]([timeoutMillis]) and if original call
|
||||||
|
* will not return anything in that timeout just return [Result] with failure
|
||||||
|
*/
|
||||||
|
class Timeouted(private val timeoutMillis: Long) : ActionWrapper {
|
||||||
|
override suspend fun <T> wrap(block: suspend () -> T): Result<T> = runCatchingSafely {
|
||||||
|
withTimeout(timeoutMillis) {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* It is passthrough variant of [ActionWrapper] which will just call incoming block with wrapping into [runCatchingSafely]
|
||||||
|
*/
|
||||||
|
object Direct : ActionWrapper {
|
||||||
|
|
||||||
|
override suspend fun <T> wrap(block: suspend () -> T): Result<T> = runCatchingSafely { block() }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.cache.fallback.crud
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.repos.CRUDRepo
|
||||||
|
import dev.inmo.micro_utils.repos.WriteCRUDRepo
|
||||||
|
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
||||||
|
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
open class AutoRecacheCRUDRepo<RegisteredObject, Id, InputObject>(
|
||||||
|
originalRepo: CRUDRepo<RegisteredObject, Id, InputObject>,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(),
|
||||||
|
recacheDelay: Long = 60.seconds.inWholeMilliseconds,
|
||||||
|
actionWrapper: ActionWrapper = ActionWrapper.Direct,
|
||||||
|
idGetter: (RegisteredObject) -> Id
|
||||||
|
) : AutoRecacheReadCRUDRepo<RegisteredObject, Id>(
|
||||||
|
originalRepo,
|
||||||
|
scope,
|
||||||
|
kvCache,
|
||||||
|
recacheDelay,
|
||||||
|
actionWrapper,
|
||||||
|
idGetter
|
||||||
|
),
|
||||||
|
WriteCRUDRepo<RegisteredObject, Id, InputObject> by AutoRecacheWriteCRUDRepo(originalRepo, scope, kvCache, idGetter),
|
||||||
|
CRUDRepo<RegisteredObject, Id, InputObject> {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
originalRepo: CRUDRepo<RegisteredObject, Id, InputObject>,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
originalCallTimeoutMillis: Long,
|
||||||
|
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(),
|
||||||
|
recacheDelay: Long = 60.seconds.inWholeMilliseconds,
|
||||||
|
idGetter: (RegisteredObject) -> Id
|
||||||
|
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter)
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.cache.fallback.crud
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import dev.inmo.micro_utils.pagination.Pagination
|
||||||
|
import dev.inmo.micro_utils.pagination.PaginationResult
|
||||||
|
import dev.inmo.micro_utils.repos.ReadCRUDRepo
|
||||||
|
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
||||||
|
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
|
||||||
|
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
|
||||||
|
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
|
||||||
|
import dev.inmo.micro_utils.repos.set
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
open class AutoRecacheReadCRUDRepo<RegisteredObject, Id>(
|
||||||
|
protected open val originalRepo: ReadCRUDRepo<RegisteredObject, Id>,
|
||||||
|
protected val scope: CoroutineScope,
|
||||||
|
protected val kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(),
|
||||||
|
protected val recacheDelay: Long = 60.seconds.inWholeMilliseconds,
|
||||||
|
protected val actionWrapper: ActionWrapper = ActionWrapper.Direct,
|
||||||
|
protected val idGetter: (RegisteredObject) -> Id
|
||||||
|
) : ReadCRUDRepo<RegisteredObject, Id>, FallbackCacheRepo {
|
||||||
|
val autoUpdateJob = scope.launch {
|
||||||
|
while (isActive) {
|
||||||
|
actualizeAll()
|
||||||
|
|
||||||
|
delay(recacheDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
originalRepo: ReadCRUDRepo<RegisteredObject, Id>,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
originalCallTimeoutMillis: Long,
|
||||||
|
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(),
|
||||||
|
recacheDelay: Long = 60.seconds.inWholeMilliseconds,
|
||||||
|
idGetter: (RegisteredObject) -> Id
|
||||||
|
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter)
|
||||||
|
|
||||||
|
protected open suspend fun actualizeAll(): Result<Unit> {
|
||||||
|
return runCatchingSafely {
|
||||||
|
kvCache.actualizeAll(originalRepo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(id: Id): Boolean = actionWrapper.wrap {
|
||||||
|
originalRepo.contains(id)
|
||||||
|
}.getOrElse {
|
||||||
|
kvCache.contains(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun count(): Long = actionWrapper.wrap {
|
||||||
|
originalRepo.count()
|
||||||
|
}.getOrElse {
|
||||||
|
kvCache.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getByPagination(
|
||||||
|
pagination: Pagination
|
||||||
|
): PaginationResult<RegisteredObject> = actionWrapper.wrap {
|
||||||
|
originalRepo.getByPagination(pagination)
|
||||||
|
}.getOrNull() ?.also {
|
||||||
|
it.results.forEach {
|
||||||
|
kvCache.set(idGetter(it), it)
|
||||||
|
}
|
||||||
|
} ?: kvCache.values(pagination)
|
||||||
|
|
||||||
|
override suspend fun getIdsByPagination(
|
||||||
|
pagination: Pagination
|
||||||
|
): PaginationResult<Id> = actionWrapper.wrap {
|
||||||
|
originalRepo.getIdsByPagination(pagination)
|
||||||
|
}.getOrElse { kvCache.keys(pagination) }
|
||||||
|
|
||||||
|
override suspend fun getById(id: Id): RegisteredObject? = actionWrapper.wrap {
|
||||||
|
originalRepo.getById(id)
|
||||||
|
}.getOrNull() ?.also {
|
||||||
|
kvCache.set(idGetter(it), it)
|
||||||
|
} ?: kvCache.get(id)
|
||||||
|
|
||||||
|
override suspend fun invalidate() {
|
||||||
|
actualizeAll()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.cache.fallback.crud
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.plus
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.micro_utils.repos.UpdatedValuePair
|
||||||
|
import dev.inmo.micro_utils.repos.WriteCRUDRepo
|
||||||
|
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
||||||
|
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
|
||||||
|
import dev.inmo.micro_utils.repos.set
|
||||||
|
import dev.inmo.micro_utils.repos.unset
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
|
||||||
|
open class AutoRecacheWriteCRUDRepo<RegisteredObject, Id, InputObject>(
|
||||||
|
protected val originalRepo: WriteCRUDRepo<RegisteredObject, Id, InputObject>,
|
||||||
|
protected val scope: CoroutineScope,
|
||||||
|
protected val kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(),
|
||||||
|
protected val idGetter: (RegisteredObject) -> Id
|
||||||
|
) : WriteCRUDRepo<RegisteredObject, Id, InputObject>, FallbackCacheRepo {
|
||||||
|
override val deletedObjectsIdsFlow: Flow<Id>
|
||||||
|
get() = (originalRepo.deletedObjectsIdsFlow + kvCache.onValueRemoved).distinctUntilChanged()
|
||||||
|
override val newObjectsFlow: Flow<RegisteredObject>
|
||||||
|
get() = (originalRepo.newObjectsFlow + kvCache.onNewValue.map { it.second }).distinctUntilChanged()
|
||||||
|
override val updatedObjectsFlow: Flow<RegisteredObject>
|
||||||
|
get() = originalRepo.updatedObjectsFlow
|
||||||
|
|
||||||
|
private val onRemovingUpdatesListeningJob = originalRepo.deletedObjectsIdsFlow.subscribeSafelyWithoutExceptions(scope) {
|
||||||
|
kvCache.unset(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val onNewAndUpdatedObjectsListeningJob = merge(
|
||||||
|
originalRepo.newObjectsFlow,
|
||||||
|
originalRepo.updatedObjectsFlow,
|
||||||
|
).subscribeSafelyWithoutExceptions(scope) {
|
||||||
|
kvCache.set(idGetter(it), it)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun update(
|
||||||
|
values: List<UpdatedValuePair<Id, InputObject>>
|
||||||
|
): List<RegisteredObject> = originalRepo.update(values).onEach {
|
||||||
|
kvCache.set(idGetter(it), it)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun update(
|
||||||
|
id: Id,
|
||||||
|
value: InputObject
|
||||||
|
): RegisteredObject? = originalRepo.update(id, value) ?.also {
|
||||||
|
kvCache.set(idGetter(it), it)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteById(ids: List<Id>) = originalRepo.deleteById(ids).also {
|
||||||
|
kvCache.unset(ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun create(values: List<InputObject>): List<RegisteredObject> = originalRepo.create(values).onEach {
|
||||||
|
kvCache.set(idGetter(it), it)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun invalidate() {
|
||||||
|
kvCache.clear()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.cache.fallback.keyvalue
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.repos.KeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.WriteKeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
||||||
|
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
open class AutoRecacheKeyValueRepo<Id, RegisteredObject>(
|
||||||
|
override val originalRepo: KeyValueRepo<Id, RegisteredObject>,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(),
|
||||||
|
recacheDelay: Long = 60.seconds.inWholeMilliseconds,
|
||||||
|
actionWrapper: ActionWrapper = ActionWrapper.Direct,
|
||||||
|
idGetter: (RegisteredObject) -> Id
|
||||||
|
) : AutoRecacheReadKeyValueRepo<Id, RegisteredObject> (
|
||||||
|
originalRepo,
|
||||||
|
scope,
|
||||||
|
kvCache,
|
||||||
|
recacheDelay,
|
||||||
|
actionWrapper,
|
||||||
|
idGetter
|
||||||
|
),
|
||||||
|
WriteKeyValueRepo<Id, RegisteredObject> by AutoRecacheWriteKeyValueRepo(originalRepo, scope, kvCache),
|
||||||
|
KeyValueRepo<Id, RegisteredObject> {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
originalRepo: KeyValueRepo<Id, RegisteredObject>,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
originalCallTimeoutMillis: Long,
|
||||||
|
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(),
|
||||||
|
recacheDelay: Long = 60.seconds.inWholeMilliseconds,
|
||||||
|
idGetter: (RegisteredObject) -> Id
|
||||||
|
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter)
|
||||||
|
|
||||||
|
override suspend fun unsetWithValues(toUnset: List<RegisteredObject>) = originalRepo.unsetWithValues(
|
||||||
|
toUnset
|
||||||
|
).also {
|
||||||
|
kvCache.unsetWithValues(toUnset)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.cache.fallback.keyvalue
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import dev.inmo.micro_utils.pagination.Pagination
|
||||||
|
import dev.inmo.micro_utils.pagination.PaginationResult
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
||||||
|
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
|
||||||
|
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
|
||||||
|
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
|
||||||
|
import dev.inmo.micro_utils.repos.set
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
open class AutoRecacheReadKeyValueRepo<Id, RegisteredObject>(
|
||||||
|
protected open val originalRepo: ReadKeyValueRepo<Id, RegisteredObject>,
|
||||||
|
protected val scope: CoroutineScope,
|
||||||
|
protected val kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(),
|
||||||
|
protected val recacheDelay: Long = 60.seconds.inWholeMilliseconds,
|
||||||
|
protected val actionWrapper: ActionWrapper = ActionWrapper.Direct,
|
||||||
|
protected val idGetter: (RegisteredObject) -> Id
|
||||||
|
) : ReadKeyValueRepo<Id, RegisteredObject>, FallbackCacheRepo {
|
||||||
|
val autoUpdateJob = scope.launch {
|
||||||
|
while (isActive) {
|
||||||
|
actualizeAll()
|
||||||
|
|
||||||
|
delay(recacheDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
originalRepo: ReadKeyValueRepo<Id, RegisteredObject>,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
originalCallTimeoutMillis: Long,
|
||||||
|
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(),
|
||||||
|
recacheDelay: Long = 60.seconds.inWholeMilliseconds,
|
||||||
|
idGetter: (RegisteredObject) -> Id
|
||||||
|
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter)
|
||||||
|
|
||||||
|
protected open suspend fun actualizeAll(): Result<Unit> {
|
||||||
|
return runCatchingSafely {
|
||||||
|
kvCache.actualizeAll(originalRepo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(key: Id): Boolean = actionWrapper.wrap {
|
||||||
|
originalRepo.contains(key)
|
||||||
|
}.getOrElse {
|
||||||
|
kvCache.contains(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun count(): Long = actionWrapper.wrap {
|
||||||
|
originalRepo.count()
|
||||||
|
}.getOrElse {
|
||||||
|
kvCache.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun get(k: Id): RegisteredObject? = actionWrapper.wrap {
|
||||||
|
originalRepo.get(k)
|
||||||
|
}.getOrNull() ?.also {
|
||||||
|
kvCache.set(k, it)
|
||||||
|
} ?: kvCache.get(k)
|
||||||
|
|
||||||
|
override suspend fun values(
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<RegisteredObject> = actionWrapper.wrap {
|
||||||
|
originalRepo.values(pagination, reversed)
|
||||||
|
}.getOrNull() ?.also {
|
||||||
|
it.results.forEach {
|
||||||
|
kvCache.set(idGetter(it), it)
|
||||||
|
}
|
||||||
|
} ?: kvCache.values(pagination, reversed)
|
||||||
|
|
||||||
|
override suspend fun keys(
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Id> = actionWrapper.wrap {
|
||||||
|
originalRepo.keys(pagination, reversed)
|
||||||
|
}.getOrElse { kvCache.keys(pagination, reversed) }
|
||||||
|
|
||||||
|
override suspend fun keys(
|
||||||
|
v: RegisteredObject,
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Id> = actionWrapper.wrap {
|
||||||
|
originalRepo.keys(v, pagination, reversed)
|
||||||
|
}.getOrElse { kvCache.keys(v, pagination, reversed) }
|
||||||
|
|
||||||
|
override suspend fun invalidate() {
|
||||||
|
actualizeAll()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.cache.fallback.keyvalue
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.plus
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.micro_utils.repos.WriteKeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
||||||
|
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
|
||||||
|
import dev.inmo.micro_utils.repos.set
|
||||||
|
import dev.inmo.micro_utils.repos.unset
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
|
||||||
|
open class AutoRecacheWriteKeyValueRepo<Id, RegisteredObject>(
|
||||||
|
protected val originalRepo: WriteKeyValueRepo<Id, RegisteredObject>,
|
||||||
|
protected val scope: CoroutineScope,
|
||||||
|
protected val kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache()
|
||||||
|
) : WriteKeyValueRepo<Id, RegisteredObject>, FallbackCacheRepo {
|
||||||
|
override val onValueRemoved: Flow<Id>
|
||||||
|
get() = (originalRepo.onValueRemoved + kvCache.onValueRemoved).distinctUntilChanged()
|
||||||
|
|
||||||
|
override val onNewValue: Flow<Pair<Id, RegisteredObject>>
|
||||||
|
get() = (originalRepo.onNewValue + kvCache.onNewValue).distinctUntilChanged()
|
||||||
|
|
||||||
|
private val onRemovingUpdatesListeningJob = originalRepo.onValueRemoved.subscribeSafelyWithoutExceptions(scope) {
|
||||||
|
kvCache.unset(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val onNewAndUpdatedObjectsListeningJob = originalRepo.onNewValue.subscribeSafelyWithoutExceptions(scope) {
|
||||||
|
kvCache.set(it.first, it.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun unsetWithValues(toUnset: List<RegisteredObject>) = originalRepo.unsetWithValues(
|
||||||
|
toUnset
|
||||||
|
).also {
|
||||||
|
kvCache.unsetWithValues(toUnset)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun unset(toUnset: List<Id>) = originalRepo.unset(
|
||||||
|
toUnset
|
||||||
|
).also {
|
||||||
|
kvCache.unset(toUnset)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun set(toSet: Map<Id, RegisteredObject>) = originalRepo.set(
|
||||||
|
toSet
|
||||||
|
).also {
|
||||||
|
kvCache.set(toSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun invalidate() {
|
||||||
|
kvCache.clear()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.cache.fallback.keyvalues
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.repos.KeyValuesRepo
|
||||||
|
import dev.inmo.micro_utils.repos.WriteKeyValuesRepo
|
||||||
|
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
||||||
|
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
open class AutoRecacheKeyValuesRepo<Id, RegisteredObject>(
|
||||||
|
override val originalRepo: KeyValuesRepo<Id, RegisteredObject>,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
kvCache: FullKVCache<Id, List<RegisteredObject>> = FullKVCache(),
|
||||||
|
recacheDelay: Long = 60.seconds.inWholeMilliseconds,
|
||||||
|
actionWrapper: ActionWrapper = ActionWrapper.Direct
|
||||||
|
) : AutoRecacheReadKeyValuesRepo<Id, RegisteredObject> (
|
||||||
|
originalRepo,
|
||||||
|
scope,
|
||||||
|
kvCache,
|
||||||
|
recacheDelay,
|
||||||
|
actionWrapper
|
||||||
|
),
|
||||||
|
WriteKeyValuesRepo<Id, RegisteredObject> by AutoRecacheWriteKeyValuesRepo(originalRepo, scope, kvCache),
|
||||||
|
KeyValuesRepo<Id, RegisteredObject> {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
originalRepo: KeyValuesRepo<Id, RegisteredObject>,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
originalCallTimeoutMillis: Long,
|
||||||
|
kvCache: FullKVCache<Id, List<RegisteredObject>> = FullKVCache(),
|
||||||
|
recacheDelay: Long = 60.seconds.inWholeMilliseconds
|
||||||
|
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis))
|
||||||
|
|
||||||
|
override suspend fun clearWithValue(v: RegisteredObject) {
|
||||||
|
super.clearWithValue(v)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.cache.fallback.keyvalues
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import dev.inmo.micro_utils.pagination.Pagination
|
||||||
|
import dev.inmo.micro_utils.pagination.PaginationResult
|
||||||
|
import dev.inmo.micro_utils.pagination.changeResultsUnchecked
|
||||||
|
import dev.inmo.micro_utils.pagination.createPaginationResult
|
||||||
|
import dev.inmo.micro_utils.pagination.emptyPaginationResult
|
||||||
|
import dev.inmo.micro_utils.pagination.firstIndex
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.optionallyReverse
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.paginate
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
|
||||||
|
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
||||||
|
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
|
||||||
|
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
|
||||||
|
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
|
||||||
|
import dev.inmo.micro_utils.repos.set
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
open class AutoRecacheReadKeyValuesRepo<Id, RegisteredObject>(
|
||||||
|
protected open val originalRepo: ReadKeyValuesRepo<Id, RegisteredObject>,
|
||||||
|
protected val scope: CoroutineScope,
|
||||||
|
protected val kvCache: FullKVCache<Id, List<RegisteredObject>> = FullKVCache(),
|
||||||
|
protected val recacheDelay: Long = 60.seconds.inWholeMilliseconds,
|
||||||
|
protected val actionWrapper: ActionWrapper = ActionWrapper.Direct
|
||||||
|
) : ReadKeyValuesRepo<Id, RegisteredObject>, FallbackCacheRepo {
|
||||||
|
val autoUpdateJob = scope.launch {
|
||||||
|
while (isActive) {
|
||||||
|
actualizeAll()
|
||||||
|
|
||||||
|
delay(recacheDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
originalRepo: ReadKeyValuesRepo<Id, RegisteredObject>,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
originalCallTimeoutMillis: Long,
|
||||||
|
kvCache: FullKVCache<Id, List<RegisteredObject>> = FullKVCache(),
|
||||||
|
recacheDelay: Long = 60.seconds.inWholeMilliseconds
|
||||||
|
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis))
|
||||||
|
|
||||||
|
protected open suspend fun actualizeAll(): Result<Unit> {
|
||||||
|
return runCatchingSafely {
|
||||||
|
kvCache.actualizeAll(originalRepo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(k: Id): Boolean = actionWrapper.wrap {
|
||||||
|
originalRepo.contains(k)
|
||||||
|
}.getOrElse {
|
||||||
|
kvCache.contains(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun count(): Long = actionWrapper.wrap {
|
||||||
|
originalRepo.count()
|
||||||
|
}.getOrElse {
|
||||||
|
kvCache.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun keys(
|
||||||
|
v: RegisteredObject,
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Id> = actionWrapper.wrap {
|
||||||
|
originalRepo.keys(v, pagination, reversed)
|
||||||
|
}.getOrElse {
|
||||||
|
val results = mutableListOf<Id>()
|
||||||
|
|
||||||
|
val toSkip = pagination.firstIndex
|
||||||
|
var count = 0
|
||||||
|
|
||||||
|
doForAllWithNextPaging {
|
||||||
|
kvCache.keys(pagination, reversed).also {
|
||||||
|
it.results.forEach {
|
||||||
|
if (kvCache.get(it) ?.contains(v) == true) {
|
||||||
|
count++
|
||||||
|
if (count < toSkip || results.size >= pagination.size) {
|
||||||
|
return@forEach
|
||||||
|
} else {
|
||||||
|
results.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@getOrElse results.createPaginationResult(
|
||||||
|
pagination,
|
||||||
|
count.toLong()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun keys(
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Id> = actionWrapper.wrap {
|
||||||
|
originalRepo.keys(pagination, reversed)
|
||||||
|
}.getOrElse { kvCache.keys(pagination, reversed) }
|
||||||
|
|
||||||
|
override suspend fun get(
|
||||||
|
k: Id,
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<RegisteredObject> = actionWrapper.wrap {
|
||||||
|
originalRepo.get(k, pagination, reversed)
|
||||||
|
}.getOrNull() ?.also {
|
||||||
|
it.results.forEach {
|
||||||
|
kvCache.set(k, ((kvCache.get(k) ?: return@also) + it).distinct())
|
||||||
|
}
|
||||||
|
} ?: kvCache.get(k) ?.run {
|
||||||
|
paginate(pagination.optionallyReverse(size, reversed)).let {
|
||||||
|
if (reversed) {
|
||||||
|
it.changeResultsUnchecked(
|
||||||
|
it.results.reversed()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ?: emptyPaginationResult()
|
||||||
|
|
||||||
|
override suspend fun count(k: Id): Long = actionWrapper.wrap {
|
||||||
|
originalRepo.count(k)
|
||||||
|
}.getOrElse {
|
||||||
|
kvCache.get(k) ?.size ?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(k: Id, v: RegisteredObject): Boolean {
|
||||||
|
return (actionWrapper.wrap {
|
||||||
|
originalRepo.contains(k, v)
|
||||||
|
}.getOrNull() ?.also {
|
||||||
|
kvCache.set(k, ((kvCache.get(k) ?: return@also) + v).distinct())
|
||||||
|
}) ?: (kvCache.get(k) ?.contains(v) == true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun invalidate() {
|
||||||
|
actualizeAll()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.cache.fallback.keyvalues
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.plus
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.micro_utils.pagination.FirstPagePagination
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
||||||
|
import dev.inmo.micro_utils.repos.WriteKeyValuesRepo
|
||||||
|
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
||||||
|
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
|
||||||
|
import dev.inmo.micro_utils.repos.set
|
||||||
|
import dev.inmo.micro_utils.repos.unset
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
|
||||||
|
open class AutoRecacheWriteKeyValuesRepo<Id, RegisteredObject>(
|
||||||
|
protected val originalRepo: WriteKeyValuesRepo<Id, RegisteredObject>,
|
||||||
|
protected val scope: CoroutineScope,
|
||||||
|
protected val kvCache: FullKVCache<Id, List<RegisteredObject>> = FullKVCache()
|
||||||
|
) : WriteKeyValuesRepo<Id, RegisteredObject>, FallbackCacheRepo {
|
||||||
|
override val onValueRemoved: Flow<Pair<Id, RegisteredObject>>
|
||||||
|
get() = originalRepo.onValueRemoved
|
||||||
|
|
||||||
|
override val onNewValue: Flow<Pair<Id, RegisteredObject>>
|
||||||
|
get() = originalRepo.onNewValue
|
||||||
|
override val onDataCleared: Flow<Id>
|
||||||
|
get() = (originalRepo.onDataCleared + kvCache.onValueRemoved).distinctUntilChanged()
|
||||||
|
|
||||||
|
private val onDataClearedListeningJob = originalRepo.onDataCleared.subscribeSafelyWithoutExceptions(scope) {
|
||||||
|
kvCache.unset(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val onRemovingUpdatesListeningJob = originalRepo.onValueRemoved.subscribeSafelyWithoutExceptions(scope) {
|
||||||
|
kvCache.set(
|
||||||
|
it.first,
|
||||||
|
(kvCache.get(
|
||||||
|
it.first
|
||||||
|
) ?: return@subscribeSafelyWithoutExceptions) - it.second
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val onNewAndUpdatedObjectsListeningJob = originalRepo.onNewValue.subscribeSafelyWithoutExceptions(scope) {
|
||||||
|
kvCache.set(
|
||||||
|
it.first,
|
||||||
|
(kvCache.get(
|
||||||
|
it.first
|
||||||
|
) ?: return@subscribeSafelyWithoutExceptions) + it.second
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun clearWithValue(v: RegisteredObject) {
|
||||||
|
originalRepo.clearWithValue(v)
|
||||||
|
doForAllWithNextPaging(FirstPagePagination(kvCache.count().takeIf { it < Int.MAX_VALUE } ?.toInt() ?: Int.MAX_VALUE)) {
|
||||||
|
kvCache.keys(it).also {
|
||||||
|
it.results.forEach { id ->
|
||||||
|
kvCache.get(id) ?.takeIf { it.contains(v) } ?.let {
|
||||||
|
kvCache.unset(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun clear(k: Id) {
|
||||||
|
originalRepo.clear(k)
|
||||||
|
kvCache.unset(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun remove(toRemove: Map<Id, List<RegisteredObject>>) {
|
||||||
|
originalRepo.remove(toRemove)
|
||||||
|
toRemove.forEach { (k, v) ->
|
||||||
|
kvCache.set(k, (kvCache.get(k) ?: return@forEach) - v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun add(toAdd: Map<Id, List<RegisteredObject>>) {
|
||||||
|
originalRepo.add(toAdd)
|
||||||
|
toAdd.forEach { (k, v) ->
|
||||||
|
kvCache.set(k, (kvCache.get(k) ?: return@forEach) + v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun invalidate() {
|
||||||
|
kvCache.clear()
|
||||||
|
}
|
||||||
|
}
|
@ -3,11 +3,9 @@ package dev.inmo.micro_utils.repos.cache.full
|
|||||||
import dev.inmo.micro_utils.common.*
|
import dev.inmo.micro_utils.common.*
|
||||||
import dev.inmo.micro_utils.pagination.Pagination
|
import dev.inmo.micro_utils.pagination.Pagination
|
||||||
import dev.inmo.micro_utils.pagination.PaginationResult
|
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.*
|
||||||
import dev.inmo.micro_utils.repos.cache.*
|
import dev.inmo.micro_utils.repos.cache.*
|
||||||
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
||||||
import dev.inmo.micro_utils.repos.cache.cache.KVCache
|
|
||||||
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
|
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -9,6 +9,7 @@ import dev.inmo.micro_utils.repos.cache.util.actualizeAll
|
|||||||
import dev.inmo.micro_utils.repos.pagination.getAll
|
import dev.inmo.micro_utils.repos.pagination.getAll
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
|
||||||
open class FullReadKeyValueCacheRepo<Key,Value>(
|
open class FullReadKeyValueCacheRepo<Key,Value>(
|
||||||
@ -80,7 +81,7 @@ fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached(
|
|||||||
) = FullReadKeyValueCacheRepo(this, kvCache)
|
) = FullReadKeyValueCacheRepo(this, kvCache)
|
||||||
|
|
||||||
open class FullWriteKeyValueCacheRepo<Key,Value>(
|
open class FullWriteKeyValueCacheRepo<Key,Value>(
|
||||||
protected open val parentRepo: WriteKeyValueRepo<Key, Value>,
|
parentRepo: WriteKeyValueRepo<Key, Value>,
|
||||||
protected open val kvCache: FullKVCache<Key, Value>,
|
protected open val kvCache: FullKVCache<Key, Value>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
) : WriteKeyValueRepo<Key, Value> by parentRepo, FullCacheRepo {
|
) : WriteKeyValueRepo<Key, Value> by parentRepo, FullCacheRepo {
|
||||||
@ -98,7 +99,7 @@ fun <Key, Value> WriteKeyValueRepo<Key, Value>.caching(
|
|||||||
) = FullWriteKeyValueCacheRepo(this, kvCache, scope)
|
) = FullWriteKeyValueCacheRepo(this, kvCache, scope)
|
||||||
|
|
||||||
open class FullKeyValueCacheRepo<Key,Value>(
|
open class FullKeyValueCacheRepo<Key,Value>(
|
||||||
override val parentRepo: KeyValueRepo<Key, Value>,
|
protected open val parentRepo: KeyValueRepo<Key, Value>,
|
||||||
kvCache: FullKVCache<Key, Value>,
|
kvCache: FullKVCache<Key, Value>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
) : FullWriteKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, scope),
|
) : FullWriteKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, scope),
|
||||||
|
@ -112,7 +112,7 @@ fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
|
|||||||
) = FullReadKeyValuesCacheRepo(this, kvCache)
|
) = FullReadKeyValuesCacheRepo(this, kvCache)
|
||||||
|
|
||||||
open class FullWriteKeyValuesCacheRepo<Key,Value>(
|
open class FullWriteKeyValuesCacheRepo<Key,Value>(
|
||||||
protected open val parentRepo: WriteKeyValuesRepo<Key, Value>,
|
parentRepo: WriteKeyValuesRepo<Key, Value>,
|
||||||
protected open val kvCache: FullKVCache<Key, List<Value>>,
|
protected open val kvCache: FullKVCache<Key, List<Value>>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
) : WriteKeyValuesRepo<Key, Value> by parentRepo, FullCacheRepo {
|
) : WriteKeyValuesRepo<Key, Value> by parentRepo, FullCacheRepo {
|
||||||
@ -140,7 +140,7 @@ fun <Key, Value> WriteKeyValuesRepo<Key, Value>.caching(
|
|||||||
) = FullWriteKeyValuesCacheRepo(this, kvCache, scope)
|
) = FullWriteKeyValuesCacheRepo(this, kvCache, scope)
|
||||||
|
|
||||||
open class FullKeyValuesCacheRepo<Key,Value>(
|
open class FullKeyValuesCacheRepo<Key,Value>(
|
||||||
override val parentRepo: KeyValuesRepo<Key, Value>,
|
protected open val parentRepo: KeyValuesRepo<Key, Value>,
|
||||||
kvCache: FullKVCache<Key, List<Value>>,
|
kvCache: FullKVCache<Key, List<Value>>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
) : FullWriteKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, scope),
|
) : FullWriteKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, scope),
|
||||||
|
@ -2,60 +2,52 @@ package dev.inmo.micro_utils.repos.cache.util
|
|||||||
|
|
||||||
import dev.inmo.micro_utils.pagination.FirstPagePagination
|
import dev.inmo.micro_utils.pagination.FirstPagePagination
|
||||||
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.getAllByWithNextPaging
|
||||||
import dev.inmo.micro_utils.repos.ReadCRUDRepo
|
import dev.inmo.micro_utils.repos.ReadCRUDRepo
|
||||||
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
|
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
|
||||||
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
|
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
|
||||||
import dev.inmo.micro_utils.repos.cache.cache.KVCache
|
import dev.inmo.micro_utils.repos.cache.cache.KVCache
|
||||||
|
import dev.inmo.micro_utils.repos.pagination.getAll
|
||||||
import dev.inmo.micro_utils.repos.set
|
import dev.inmo.micro_utils.repos.set
|
||||||
|
|
||||||
suspend inline fun <K, V> KVCache<K, V>.actualizeAll(
|
suspend inline fun <K, V> KVCache<K, V>.actualizeAll(
|
||||||
|
clear: Boolean = true,
|
||||||
getAll: () -> Map<K, V>
|
getAll: () -> Map<K, V>
|
||||||
) {
|
) {
|
||||||
clear()
|
set(
|
||||||
set(getAll())
|
getAll().also {
|
||||||
|
if (clear) {
|
||||||
|
clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend inline fun <K, V> KVCache<K, V>.actualizeAll(
|
suspend inline fun <K, V> KVCache<K, V>.actualizeAll(
|
||||||
repo: ReadKeyValueRepo<K, V>
|
repo: ReadKeyValueRepo<K, V>,
|
||||||
|
clear: Boolean = true,
|
||||||
) {
|
) {
|
||||||
clear()
|
actualizeAll(clear) {
|
||||||
val count = repo.count().takeIf { it < Int.MAX_VALUE } ?.toInt() ?: Int.MAX_VALUE
|
repo.getAll { keys(it) }.toMap()
|
||||||
val initPagination = FirstPagePagination(count)
|
|
||||||
doForAllWithNextPaging(initPagination) {
|
|
||||||
keys(it).also {
|
|
||||||
set(
|
|
||||||
it.results.mapNotNull { k -> repo.get(k) ?.let { k to it } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend inline fun <K, V> KVCache<K, List<V>>.actualizeAll(
|
suspend inline fun <K, V> KVCache<K, List<V>>.actualizeAll(
|
||||||
repo: ReadKeyValuesRepo<K, V>
|
repo: ReadKeyValuesRepo<K, V>,
|
||||||
|
clear: Boolean = true,
|
||||||
) {
|
) {
|
||||||
clear()
|
actualizeAll(clear) {
|
||||||
val count = repo.count().takeIf { it < Int.MAX_VALUE } ?.toInt() ?: Int.MAX_VALUE
|
repo.getAll { keys(it) }.toMap()
|
||||||
val initPagination = FirstPagePagination(count)
|
|
||||||
doForAllWithNextPaging(initPagination) {
|
|
||||||
keys(it).also {
|
|
||||||
set(
|
|
||||||
it.results.associateWith { k -> repo.getAll(k) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend inline fun <K, V> KVCache<K, V>.actualizeAll(
|
suspend inline fun <K, V> KVCache<K, V>.actualizeAll(
|
||||||
repo: ReadCRUDRepo<V, K>
|
repo: ReadCRUDRepo<V, K>,
|
||||||
|
clear: Boolean = true,
|
||||||
) {
|
) {
|
||||||
clear()
|
actualizeAll(clear) {
|
||||||
val count = repo.count().takeIf { it < Int.MAX_VALUE } ?.toInt() ?: Int.MAX_VALUE
|
repo.getAllByWithNextPaging {
|
||||||
val initPagination = FirstPagePagination(count)
|
getIdsByPagination(it)
|
||||||
doForAllWithNextPaging(initPagination) {
|
}.mapNotNull { it to (repo.getById(it) ?: return@mapNotNull null) }.toMap()
|
||||||
keys(it).also {
|
|
||||||
set(
|
|
||||||
it.results.mapNotNull { k -> repo.getById(k) ?.let { k to it } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.transforms.kv
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.repos.KeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.KeyValuesRepo
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
|
||||||
|
import kotlin.js.JsName
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
|
fun <K, V> ReadKeyValuesRepo<K, V>.asReadKeyValueRepo() = ReadKeyValueFromKeyValuesRepo(this)
|
||||||
|
|
||||||
|
fun <K, V> KeyValuesRepo<K, V>.asKeyValueRepo() = KeyValueFromKeyValuesRepo(this)
|
@ -0,0 +1,41 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.transforms.kv
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.pagination.FirstPagePagination
|
||||||
|
import dev.inmo.micro_utils.pagination.Pagination
|
||||||
|
import dev.inmo.micro_utils.pagination.PaginationResult
|
||||||
|
import dev.inmo.micro_utils.pagination.changeResults
|
||||||
|
import dev.inmo.micro_utils.pagination.changeResultsUnchecked
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.optionallyReverse
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.paginate
|
||||||
|
import dev.inmo.micro_utils.repos.KeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.KeyValuesRepo
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
|
||||||
|
import dev.inmo.micro_utils.repos.transforms.kvs.ReadKeyValuesFromKeyValueRepo
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
|
||||||
|
open class KeyValueFromKeyValuesRepo<Key, Value>(
|
||||||
|
private val original: KeyValuesRepo<Key, Value>
|
||||||
|
) : KeyValueRepo<Key, List<Value>>, ReadKeyValueFromKeyValuesRepo<Key, Value>(original) {
|
||||||
|
override val onNewValue: Flow<Pair<Key, List<Value>>> = merge(
|
||||||
|
original.onNewValue,
|
||||||
|
original.onValueRemoved
|
||||||
|
).mapNotNull {
|
||||||
|
it.first to (get(it.first) ?: return@mapNotNull null)
|
||||||
|
}
|
||||||
|
override val onValueRemoved: Flow<Key> = original.onDataCleared
|
||||||
|
|
||||||
|
override suspend fun unset(toUnset: List<Key>) {
|
||||||
|
toUnset.forEach {
|
||||||
|
original.clear(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun set(toSet: Map<Key, List<Value>>) {
|
||||||
|
original.set(toSet)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.transforms.kv
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.pagination.FirstPagePagination
|
||||||
|
import dev.inmo.micro_utils.pagination.Pagination
|
||||||
|
import dev.inmo.micro_utils.pagination.PaginationResult
|
||||||
|
import dev.inmo.micro_utils.pagination.changeResults
|
||||||
|
import dev.inmo.micro_utils.pagination.changeResultsUnchecked
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.optionallyReverse
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.paginate
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
|
||||||
|
import dev.inmo.micro_utils.repos.transforms.kvs.ReadKeyValuesFromKeyValueRepo
|
||||||
|
|
||||||
|
open class ReadKeyValueFromKeyValuesRepo<Key, Value>(
|
||||||
|
private val original: ReadKeyValuesRepo<Key, Value>
|
||||||
|
) : ReadKeyValueRepo<Key, List<Value>> {
|
||||||
|
override suspend fun get(k: Key): List<Value>? = original.getAll(k)
|
||||||
|
|
||||||
|
override suspend fun values(
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<List<Value>> {
|
||||||
|
val keys = keys(pagination, reversed)
|
||||||
|
return keys.changeResults(
|
||||||
|
keys.results.mapNotNull {
|
||||||
|
get(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
|
||||||
|
return original.keys(pagination, reversed)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun count(): Long {
|
||||||
|
return original.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(key: Key): Boolean {
|
||||||
|
return original.contains(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun keys(v: List<Value>, pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
|
||||||
|
val keys = mutableSetOf<Key>()
|
||||||
|
|
||||||
|
doForAllWithNextPaging(FirstPagePagination(count().toInt())) {
|
||||||
|
original.keys(it).also {
|
||||||
|
it.results.forEach {
|
||||||
|
val values = get(it) ?: return@forEach
|
||||||
|
if (values.containsAll(v) && v.containsAll(values)) {
|
||||||
|
keys.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val paginated = keys.paginate(
|
||||||
|
pagination.optionallyReverse(keys.count(), reversed)
|
||||||
|
)
|
||||||
|
return if (reversed) {
|
||||||
|
paginated.changeResultsUnchecked(paginated.results.reversed())
|
||||||
|
} else {
|
||||||
|
paginated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.transforms.kvs
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.repos.KeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
|
||||||
|
import kotlin.js.JsName
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
|
|
||||||
|
fun <K, V, VI : Iterable<V>> ReadKeyValueRepo<K, VI>.asReadKeyValuesRepo() = ReadKeyValuesFromKeyValueRepo(this)
|
||||||
|
|
||||||
|
fun <K, V, VI : Iterable<V>> KeyValueRepo<K, VI>.asKeyValuesRepo(
|
||||||
|
listToValuesIterable: suspend (List<V>) -> VI
|
||||||
|
): KeyValuesFromKeyValueRepo<K, V, VI> = KeyValuesFromKeyValueRepo(this, listToValuesIterable)
|
||||||
|
|
||||||
|
@JvmName("asListKeyValuesRepo")
|
||||||
|
@JsName("asListKeyValuesRepo")
|
||||||
|
fun <K, V> KeyValueRepo<K, List<V>>.asKeyValuesRepo(): KeyValuesFromKeyValueRepo<K, V, List<V>> = asKeyValuesRepo { it }
|
||||||
|
|
||||||
|
@JvmName("asSetKeyValuesRepo")
|
||||||
|
@JsName("asSetKeyValuesRepo")
|
||||||
|
fun <K, V> KeyValueRepo<K, Set<V>>.asKeyValuesRepo(): KeyValuesFromKeyValueRepo<K, V, Set<V>> = asKeyValuesRepo { it.toSet() }
|
@ -0,0 +1,77 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.transforms.kvs
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.pagination.FirstPagePagination
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
||||||
|
import dev.inmo.micro_utils.repos.KeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.KeyValuesRepo
|
||||||
|
import dev.inmo.micro_utils.repos.unset
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import kotlin.js.JsName
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
|
open class KeyValuesFromKeyValueRepo<Key, Value, ValuesIterable : Iterable<Value>>(
|
||||||
|
private val original: KeyValueRepo<Key, ValuesIterable>,
|
||||||
|
private val listToValuesIterable: suspend (List<Value>) -> ValuesIterable
|
||||||
|
) : KeyValuesRepo<Key, Value>, ReadKeyValuesFromKeyValueRepo<Key, Value, ValuesIterable>(original) {
|
||||||
|
private val _onNewValue = MutableSharedFlow<Pair<Key, Value>>()
|
||||||
|
private val _onValueRemoved = MutableSharedFlow<Pair<Key, Value>>()
|
||||||
|
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
|
||||||
|
override val onValueRemoved: Flow<Pair<Key, Value>> = _onValueRemoved.asSharedFlow()
|
||||||
|
override val onDataCleared: Flow<Key> = original.onValueRemoved
|
||||||
|
|
||||||
|
override suspend fun clearWithValue(v: Value) {
|
||||||
|
val keys = mutableSetOf<Key>()
|
||||||
|
|
||||||
|
doForAllWithNextPaging(FirstPagePagination(count().toInt())) {
|
||||||
|
original.keys(it).also {
|
||||||
|
it.results.forEach {
|
||||||
|
if (contains(it, v)) {
|
||||||
|
keys.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
original.unset(keys.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun clear(k: Key) {
|
||||||
|
original.unset(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
||||||
|
original.set(
|
||||||
|
toRemove.mapNotNull { (k, removing) ->
|
||||||
|
val exists = original.get(k) ?: return@mapNotNull null
|
||||||
|
k to listToValuesIterable(exists - removing).also {
|
||||||
|
if (it.firstOrNull() == null) {
|
||||||
|
original.unset(k)
|
||||||
|
return@mapNotNull null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.toMap()
|
||||||
|
)
|
||||||
|
toRemove.forEach { (k, v) ->
|
||||||
|
v.forEach {
|
||||||
|
_onValueRemoved.emit(k to it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun add(toAdd: Map<Key, List<Value>>) {
|
||||||
|
original.set(
|
||||||
|
toAdd.mapNotNull { (k, adding) ->
|
||||||
|
val exists = original.get(k) ?: emptyList()
|
||||||
|
k to listToValuesIterable(exists + adding)
|
||||||
|
}.toMap()
|
||||||
|
)
|
||||||
|
toAdd.forEach { (k, v) ->
|
||||||
|
v.forEach {
|
||||||
|
_onNewValue.emit(k to it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.transforms.kvs
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.pagination.FirstPagePagination
|
||||||
|
import dev.inmo.micro_utils.pagination.Pagination
|
||||||
|
import dev.inmo.micro_utils.pagination.PaginationResult
|
||||||
|
import dev.inmo.micro_utils.pagination.changeResultsUnchecked
|
||||||
|
import dev.inmo.micro_utils.pagination.emptyPaginationResult
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.optionallyReverse
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.paginate
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
|
||||||
|
|
||||||
|
open class ReadKeyValuesFromKeyValueRepo<Key, Value, ValuesIterable : Iterable<Value>>(
|
||||||
|
private val original: ReadKeyValueRepo<Key, ValuesIterable>
|
||||||
|
) : ReadKeyValuesRepo<Key, Value> {
|
||||||
|
override suspend fun get(
|
||||||
|
k: Key,
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Value> {
|
||||||
|
val iterable = original.get(k) ?: return emptyPaginationResult(pagination)
|
||||||
|
val paginated = iterable.paginate(
|
||||||
|
pagination.optionallyReverse(iterable.count(), reversed)
|
||||||
|
)
|
||||||
|
return if (reversed) {
|
||||||
|
paginated.changeResultsUnchecked(paginated.results.reversed())
|
||||||
|
} else {
|
||||||
|
paginated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun keys(
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Key> = original.keys(pagination, reversed)
|
||||||
|
|
||||||
|
override suspend fun count(): Long = original.count()
|
||||||
|
|
||||||
|
override suspend fun count(k: Key): Long = original.get(k) ?.count() ?.toLong() ?: 0L
|
||||||
|
|
||||||
|
override suspend fun contains(k: Key, v: Value): Boolean = original.get(k) ?.contains(v) == true
|
||||||
|
|
||||||
|
override suspend fun contains(k: Key): Boolean = original.contains(k)
|
||||||
|
|
||||||
|
override suspend fun keys(
|
||||||
|
v: Value,
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Key> {
|
||||||
|
val keys = mutableSetOf<Key>()
|
||||||
|
|
||||||
|
doForAllWithNextPaging(FirstPagePagination(count().toInt())) {
|
||||||
|
original.keys(it).also {
|
||||||
|
it.results.forEach {
|
||||||
|
if (contains(it, v)) {
|
||||||
|
keys.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val paginated = keys.paginate(
|
||||||
|
pagination.optionallyReverse(keys.count(), reversed)
|
||||||
|
)
|
||||||
|
return if (reversed) {
|
||||||
|
paginated.changeResultsUnchecked(paginated.results.reversed())
|
||||||
|
} else {
|
||||||
|
paginated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,11 +31,19 @@ class FileReadKeyValueRepo(
|
|||||||
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<File> {
|
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<File> {
|
||||||
val count = count()
|
val count = count()
|
||||||
val resultPagination = if (reversed) pagination.reverse(count) else pagination
|
val resultPagination = if (reversed) pagination.reverse(count) else pagination
|
||||||
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndexExclusive) ?: return emptyPaginationResult()
|
val filesList = folder.list()
|
||||||
if (reversed) {
|
val files: Array<String> = if (resultPagination.firstIndex < count) {
|
||||||
filesPaths.reverse()
|
val filesPaths = filesList.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndexExclusive.coerceAtMost(filesList.size))
|
||||||
|
|
||||||
|
if (reversed) {
|
||||||
|
filesPaths.reversedArray()
|
||||||
|
} else {
|
||||||
|
filesPaths
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emptyArray<String>()
|
||||||
}
|
}
|
||||||
return filesPaths.map { File(folder, it) }.createPaginationResult(
|
return files.map { File(folder, it) }.createPaginationResult(
|
||||||
resultPagination,
|
resultPagination,
|
||||||
count
|
count
|
||||||
)
|
)
|
||||||
@ -44,11 +52,21 @@ class FileReadKeyValueRepo(
|
|||||||
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<String> {
|
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<String> {
|
||||||
val count = count()
|
val count = count()
|
||||||
val resultPagination = if (reversed) pagination.reverse(count) else pagination
|
val resultPagination = if (reversed) pagination.reverse(count) else pagination
|
||||||
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndexExclusive) ?: return emptyPaginationResult()
|
val filesList = folder.list()
|
||||||
if (reversed) {
|
|
||||||
filesPaths.reverse()
|
val files: Array<String> = if (resultPagination.firstIndex < count) {
|
||||||
|
val filesPaths = filesList.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndexExclusive.coerceAtMost(filesList.size))
|
||||||
|
|
||||||
|
if (reversed) {
|
||||||
|
filesPaths.reversedArray()
|
||||||
|
} else {
|
||||||
|
filesPaths
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emptyArray<String>()
|
||||||
}
|
}
|
||||||
return filesPaths.toList().createPaginationResult(
|
|
||||||
|
return files.toList().createPaginationResult(
|
||||||
resultPagination,
|
resultPagination,
|
||||||
count
|
count
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user