Compare commits

...

38 Commits

Author SHA1 Message Date
e3ff1b9609 fill changelog 2022-07-22 16:31:33 +06:00
70c31966ca Update gradle-wrapper.properties 2022-07-18 14:33:04 +06:00
0e4188882f Update libs.versions.toml 2022-07-18 14:08:23 +06:00
6bf0ce92ba Update libs.versions.toml 2022-07-18 13:51:00 +06:00
d51bdc5086 Update gradle.properties 2022-07-18 13:48:09 +06:00
e5dd4363f1 Merge pull request #175 from InsanusMokrassar/0.11.12
0.11.12
2022-07-02 02:50:47 +06:00
a3a48bbaac fixes in file key value repo 2022-07-02 02:28:43 +06:00
5e716fb9a8 start 0.11.12 2022-07-02 02:26:58 +06:00
11a36153cc Merge pull request #174 from InsanusMokrassar/0.11.11
0.11.11: improvements of cache repos
2022-06-30 14:03:29 +06:00
8bee354f04 improvements of cache repos 2022-06-30 13:59:32 +06:00
f7dd2b5ce7 Merge pull request #173 from InsanusMokrassar/0.11.11
0.11.11
2022-06-30 13:41:43 +06:00
8ca10c00bb update dokka workflow 2022-06-30 13:41:12 +06:00
905c7e8eda deprecate hmacSha256 2022-06-30 13:19:37 +06:00
d4c5e849bf deprecate BodyPair 2022-06-30 13:15:19 +06:00
8250a2a021 start 0.11.11 2022-06-30 13:14:06 +06:00
01b3df7b8c Update github_release.gradle 2022-06-30 10:12:54 +06:00
daa6e4aff5 Merge pull request #172 from InsanusMokrassar/0.11.10
0.11.10
2022-06-30 02:56:14 +06:00
23bcb26a58 complete improvements in caches 2022-06-30 02:44:44 +06:00
e55f60c30b rename unlimited kv cache 2022-06-30 00:22:07 +06:00
0d0c16e16d add full cache repos 2022-06-29 23:53:49 +06:00
540d5cce7c start add full repos caches 2022-06-29 19:43:58 +06:00
a548b00979 update repos cache 2022-06-29 19:31:57 +06:00
4b7ca6d565 start 0.11.10 2022-06-29 19:29:38 +06:00
0473fa238c Merge pull request #171 from InsanusMokrassar/0.11.9
0.11.9
2022-06-29 01:56:03 +06:00
cfc7119697 update dependencies 2022-06-28 23:06:09 +06:00
22a6520d3e start 0.11.9 2022-06-28 23:01:32 +06:00
fb25e91191 start 0.12.0 2022-06-28 22:57:21 +06:00
c116b270b6 Merge pull request #170 from InsanusMokrassar/0.11.8
0.11.8
2022-06-28 14:18:11 +06:00
aa2d598689 fixes in FileKeyValueRepo 2022-06-28 14:13:04 +06:00
5ef3bb746b start 0.11.8 2022-06-28 14:12:12 +06:00
037616e271 Merge pull request #169 from InsanusMokrassar/0.11.7
0.11.7
2022-06-28 03:07:54 +06:00
abbea906f1 fixes 2022-06-28 01:46:24 +06:00
9132e216c9 add several extensions for MapperRepo 2022-06-28 01:43:57 +06:00
12a7e3c4af fixes 2022-06-28 01:06:46 +06:00
b40c093917 SimpleMapper, SimpleSuspendableMapper, mappers for CRUDRepo 2022-06-28 00:56:51 +06:00
7ac12455c8 add ReadCRUDRepo mappers 2022-06-28 00:19:25 +06:00
5043eec7a2 start 0.11.7 2022-06-27 23:35:10 +06:00
cf31f53e01 Merge pull request #168 from InsanusMokrassar/0.11.6
0.11.6
2022-06-24 00:19:34 +06:00
35 changed files with 912 additions and 127 deletions

View File

@@ -15,7 +15,7 @@ jobs:
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
- name: Build
run: ./gradlew dokkaHtml
run: ./gradlew build && ./gradlew dokkaHtml
- name: Publish KDocs
uses: peaceiris/actions-gh-pages@v3
with:

View File

@@ -1,5 +1,62 @@
# Changelog
## 0.11.13
* `Versions`:
* `Coroutines`: `1.6.3` -> `1.6.4`
* `Compose`: `1.2.0-alpha01-dev629` -> `1.2.0-alpha01-dev731`
## 0.11.12
* `Repos`:
* `Common`:
* `JVM`:
* Fixes in `ReadFileKeyValueRepo` methods (`values`/`keys`)
## 0.11.11
* `Crypto`:
* `hmacSha256` has been deprecated
* `Ktor`:
* `Client`:
* `BodyPair` has been deprecated
* `Repos`:
* `Cache`:
* New interface `CacheRepo`
* New interface `FullCacheRepo`
* `actualize*` methods inside of full cache repos now open for overriding
## 0.11.10
* `Repos`:
* `Cache`:
* `KVCache` has been replaced to the package `dev.inmo.micro_utils.repos.cache`
* `SimpleKVCache` has been replaced to the package `dev.inmo.micro_utils.repos.cache`
* New `KVCache` subtype - `FullKVCache`
* Add `Full*` variants of standard repos
* Add `cached`/`caching` (for write repos) extensions for all standard types of repos
## 0.11.9
* `Versions`
* `Coroutines`: `1.6.1` -> `1.6.3`
* `Ktor`: `2.0.2` -> `2.0.3`
* `Compose`: `1.2.0-alpha01-dev686` -> `1.2.0-alpha01-dev729`
## 0.11.8
* `Repos`:
* `Common`:
* Fixes in `FileKeyValueRepo`
## 0.11.7
* `Common`:
* New abstractions `SimpleMapper` and `SimpleSuspendableMapper`
* `Repos`:
* `Common`:
* Add mappers for `CRUDRepo`
## 0.11.6
* `FSM`:

View File

@@ -0,0 +1,53 @@
package dev.inmo.micro_utils.common
import kotlin.jvm.JvmName
interface SimpleMapper<T1, T2> {
fun convertToT1(from: T2): T1
fun convertToT2(from: T1): T2
}
@JvmName("convertFromT2")
fun <T1, T2> SimpleMapper<T1, T2>.convert(from: T2) = convertToT1(from)
@JvmName("convertFromT1")
fun <T1, T2> SimpleMapper<T1, T2>.convert(from: T1) = convertToT2(from)
class SimpleMapperImpl<T1, T2>(
private val t1: (T2) -> T1,
private val t2: (T1) -> T2,
) : SimpleMapper<T1, T2> {
override fun convertToT1(from: T2): T1 = t1.invoke(from)
override fun convertToT2(from: T1): T2 = t2.invoke(from)
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T1, T2> simpleMapper(
noinline t1: (T2) -> T1,
noinline t2: (T1) -> T2,
) = SimpleMapperImpl(t1, t2)
interface SimpleSuspendableMapper<T1, T2> {
suspend fun convertToT1(from: T2): T1
suspend fun convertToT2(from: T1): T2
}
@JvmName("convertFromT2")
suspend fun <T1, T2> SimpleSuspendableMapper<T1, T2>.convert(from: T2) = convertToT1(from)
@JvmName("convertFromT1")
suspend fun <T1, T2> SimpleSuspendableMapper<T1, T2>.convert(from: T1) = convertToT2(from)
class SimpleSuspendableMapperImpl<T1, T2>(
private val t1: suspend (T2) -> T1,
private val t2: suspend (T1) -> T2,
) : SimpleSuspendableMapper<T1, T2> {
override suspend fun convertToT1(from: T2): T1 = t1.invoke(from)
override suspend fun convertToT2(from: T1): T2 = t2.invoke(from)
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T1, T2> simpleSuspendableMapper(
noinline t1: suspend (T2) -> T1,
noinline t2: suspend (T1) -> T2,
) = SimpleSuspendableMapperImpl(t1, t2)

View File

@@ -41,6 +41,13 @@ data class Optional<T> internal constructor(
inline val <T> T.optional
get() = Optional.presented(this)
inline val <T : Any> T?.optionalOrAbsentIfNull
get() = if (this == null) {
Optional.absent<T>()
} else {
Optional.presented(this)
}
/**
* Will call [block] when data presented ([Optional.dataPresented] == true)
*/

View File

@@ -1,3 +1,4 @@
package dev.inmo.micro_utils.crypto
@Deprecated("Deprecated due to incorrect of work sometimes and redundancy. Can be replaced by korlibs krypto")
expect fun SourceString.hmacSha256(key: String): String

View File

@@ -8,6 +8,7 @@ external interface CryptoJs {
@JsNonModule
external val CryptoJS: CryptoJs
@Deprecated("Deprecated due to incorrect of work sometimes and redundancy. Can be replaced by korlibs krypto")
actual fun SourceString.hmacSha256(key: String): String {
return CryptoJS.asDynamic().HmacSHA256(this, key).toString().unsafeCast<String>()
}

View File

@@ -3,6 +3,7 @@ package dev.inmo.micro_utils.crypto
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
@Deprecated("Deprecated due to incorrect of work sometimes and redundancy. Can be replaced by korlibs krypto")
actual fun SourceString.hmacSha256(key: String): String {
val mac = Mac.getInstance("HmacSHA256")

View File

@@ -21,7 +21,7 @@ if (new File(projectDir, "secret.gradle").exists()) {
owner "InsanusMokrassar"
repo "MicroUtils"
tagName "${project.version}"
tagName "v${project.version}"
releaseName "${project.version}"
targetCommitish "${project.version}"

View File

@@ -14,5 +14,5 @@ crypto_js_version=4.1.1
# Project data
group=dev.inmo
version=0.11.6
android_code_version=130
version=0.11.13
android_code_version=137

View File

@@ -2,18 +2,18 @@
kt = "1.6.21"
kt-serialization = "1.3.3"
kt-coroutines = "1.6.1"
kt-coroutines = "1.6.4"
jb-compose = "1.2.0-alpha01-dev686"
jb-compose = "1.2.0-alpha01-dev731"
jb-exposed = "0.38.2"
jb-dokka = "1.6.21"
klock = "2.7.0"
uuid = "0.4.1"
ktor = "2.0.2"
ktor = "2.0.3"
gh-release = "2.3.7"
gh-release = "2.4.1"
android-gradle = "7.0.4"
dexcount = "3.1.0"

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -12,6 +12,7 @@ import io.ktor.http.*
import io.ktor.utils.io.core.ByteReadPacket
import kotlinx.serialization.*
@Deprecated("This class will be removed in next")
typealias BodyPair<T> = Pair<SerializationStrategy<T>, T>
class UnifiedRequester(
@@ -33,7 +34,7 @@ class UnifiedRequester(
suspend fun <BodyType, ResultType> unipost(
url: String,
bodyInfo: BodyPair<BodyType>,
bodyInfo: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>
) = client.unipost(url, bodyInfo, resultDeserializer, serialFormat)
@@ -52,7 +53,7 @@ class UnifiedRequester(
url: String,
filename: String,
inputProvider: InputProvider,
otherData: BodyPair<BodyType>,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
@@ -75,7 +76,7 @@ class UnifiedRequester(
suspend fun <BodyType, ResultType> unimultipart(
url: String,
mppFile: MPPFile,
otherData: BodyPair<BodyType>,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
@@ -120,7 +121,7 @@ fun <T> SerializationStrategy<T>.encodeUrlQueryValue(
suspend fun <BodyType, ResultType> HttpClient.unipost(
url: String,
bodyInfo: BodyPair<BodyType>,
bodyInfo: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) = post(url) {
@@ -162,7 +163,7 @@ suspend fun <ResultType> HttpClient.unimultipart(
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
url: String,
filename: String,
otherData: BodyPair<BodyType>,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
inputProvider: InputProvider,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
@@ -220,7 +221,7 @@ suspend fun <ResultType> HttpClient.unimultipart(
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
url: String,
mppFile: MPPFile,
otherData: BodyPair<BodyType>,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},

View File

@@ -15,4 +15,4 @@ kotlin {
}
}
}
}
}

View File

@@ -1,16 +1,16 @@
package dev.inmo.micro_utils.repos.cache
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.KVCache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.*
open class ReadCRUDCacheRepo<ObjectType, IdType>(
protected val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
protected val kvCache: KVCache<IdType, ObjectType>,
protected val idGetter: (ObjectType) -> IdType
) : ReadCRUDRepo<ObjectType, IdType> by parentRepo {
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
protected open val kvCache: KVCache<IdType, ObjectType>,
protected open val idGetter: (ObjectType) -> IdType
) : ReadCRUDRepo<ObjectType, IdType> by parentRepo, CacheRepo {
override suspend fun getById(id: IdType): ObjectType? = kvCache.get(id) ?: (parentRepo.getById(id) ?.also {
kvCache.set(id, it)
})
@@ -18,8 +18,63 @@ open class ReadCRUDCacheRepo<ObjectType, IdType>(
override suspend fun contains(id: IdType): Boolean = kvCache.contains(id) || parentRepo.contains(id)
}
fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached(
kvCache: KVCache<IdType, ObjectType>,
idGetter: (ObjectType) -> IdType
) = ReadCRUDCacheRepo(this, kvCache, idGetter)
open class WriteCRUDCacheRepo<ObjectType, IdType, InputValueType>(
protected open val parentRepo: WriteCRUDRepo<ObjectType, IdType, InputValueType>,
protected open val kvCache: KVCache<IdType, ObjectType>,
protected open val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
protected open val idGetter: (ObjectType) -> IdType
) : WriteCRUDRepo<ObjectType, IdType, InputValueType>, CacheRepo {
override val newObjectsFlow: Flow<ObjectType> by parentRepo::newObjectsFlow
override val updatedObjectsFlow: Flow<ObjectType> by parentRepo::updatedObjectsFlow
override val deletedObjectsIdsFlow: Flow<IdType> by parentRepo::deletedObjectsIdsFlow
val deletedObjectsFlowJob = parentRepo.deletedObjectsIdsFlow.onEach {
kvCache.unset(it)
}.launchIn(scope)
override suspend fun deleteById(ids: List<IdType>) = parentRepo.deleteById(ids)
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
val updated = parentRepo.update(values)
kvCache.unset(values.map { it.id })
kvCache.set(updated.associateBy { idGetter(it) })
return updated
}
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
return parentRepo.update(id, value) ?.also {
kvCache.unset(id)
kvCache.set(idGetter(it), it)
}
}
override suspend fun create(values: List<InputValueType>): List<ObjectType> {
val created = parentRepo.create(values)
kvCache.set(
created.associateBy { idGetter(it) }
)
return created
}
}
fun <ObjectType, IdType, InputType> WriteCRUDRepo<ObjectType, IdType, InputType>.caching(
kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope,
idGetter: (ObjectType) -> IdType
) = WriteCRUDCacheRepo(this, kvCache, scope, idGetter)
open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
idGetter: (ObjectType) -> IdType
@@ -27,8 +82,17 @@ open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
parentRepo,
kvCache,
idGetter
), CRUDRepo<ObjectType, IdType, InputValueType>, WriteCRUDRepo<ObjectType, IdType, InputValueType> by parentRepo {
protected val onNewJob = parentRepo.newObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
protected val onUpdatedJob = parentRepo.updatedObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
protected val onRemoveJob = parentRepo.deletedObjectsIdsFlow.onEach { kvCache.unset(it) }.launchIn(scope)
}
),
WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo(
parentRepo,
kvCache,
scope,
idGetter
),
CRUDRepo<ObjectType, IdType, InputValueType>
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope,
idGetter: (ObjectType) -> IdType
) = CRUDCacheRepo(this, kvCache, scope, idGetter)

View File

@@ -0,0 +1,3 @@
package dev.inmo.micro_utils.repos.cache
interface CacheRepo

View File

@@ -1,41 +1,6 @@
package dev.inmo.micro_utils.repos.cache
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
interface KVCache<K, V> : KeyValueRepo<K, V>
open class SimpleKVCache<K, V>(
protected val cachedValuesCount: Int,
private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) : KVCache<K, V>, KeyValueRepo<K, V> by kvParent {
protected open val cacheStack = ArrayList<K>(cachedValuesCount)
protected val syncMutex = Mutex()
protected suspend fun makeUnset(toUnset: List<K>) {
cacheStack.removeAll(toUnset)
kvParent.unset(toUnset)
}
override suspend fun set(toSet: Map<K, V>) {
syncMutex.withLock {
if (toSet.size > cachedValuesCount) {
cacheStack.clear()
kvParent.unset(getAllWithNextPaging { kvParent.keys(it) })
val keysToInclude = toSet.keys.drop(toSet.size - cachedValuesCount)
cacheStack.addAll(keysToInclude)
kvParent.set(keysToInclude.associateWith { toSet.getValue(it) })
} else {
makeUnset(cacheStack.take(toSet.size))
}
}
}
override suspend fun unset(toUnset: List<K>) {
syncMutex.withLock { makeUnset(toUnset) }
}
}
@Deprecated("Replaced", ReplaceWith("KVCache", "dev.inmo.micro_utils.repos.cache.cache.KVCache"))
typealias KVCache<K, V> = dev.inmo.micro_utils.repos.cache.cache.KVCache<K, V>
@Deprecated("Replaced", ReplaceWith("SimpleKVCache", "dev.inmo.micro_utils.repos.cache.cache.SimpleKVCache"))
typealias SimpleKVCache<K, V> = dev.inmo.micro_utils.repos.cache.cache.SimpleKVCache<K, V>

View File

@@ -1,23 +1,35 @@
package dev.inmo.micro_utils.repos.cache
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.KVCache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
open class ReadKeyValueCacheRepo<Key,Value>(
protected val parentRepo: ReadKeyValueRepo<Key, Value>,
protected val kvCache: KVCache<Key, Value>,
) : ReadKeyValueRepo<Key,Value> by parentRepo {
protected open val parentRepo: ReadKeyValueRepo<Key, Value>,
protected open val kvCache: KVCache<Key, Value>,
) : ReadKeyValueRepo<Key,Value> by parentRepo, CacheRepo {
override suspend fun get(k: Key): Value? = kvCache.get(k) ?: parentRepo.get(k) ?.also { kvCache.set(k, it) }
override suspend fun contains(key: Key): Boolean = kvCache.contains(key) || parentRepo.contains(key)
}
fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached(
kvCache: KVCache<Key, Value>
) = ReadKeyValueCacheRepo(this, kvCache)
open class KeyValueCacheRepo<Key,Value>(
parentRepo: KeyValueRepo<Key, Value>,
kvCache: KVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : ReadKeyValueCacheRepo<Key,Value>(parentRepo, kvCache), KeyValueRepo<Key,Value>, WriteKeyValueRepo<Key, Value> by parentRepo {
) : ReadKeyValueCacheRepo<Key,Value>(parentRepo, kvCache), KeyValueRepo<Key,Value>, WriteKeyValueRepo<Key, Value> by parentRepo, CacheRepo {
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope)
}
fun <Key, Value> KeyValueRepo<Key, Value>.cached(
kvCache: KVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = KeyValueCacheRepo(this, kvCache, scope)

View File

@@ -5,14 +5,15 @@ import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.utils.paginate
import dev.inmo.micro_utils.pagination.utils.reverse
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.KVCache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
open class ReadKeyValuesCacheRepo<Key,Value>(
protected val parentRepo: ReadKeyValuesRepo<Key, Value>,
protected val kvCache: KVCache<Key, List<Value>>
) : ReadKeyValuesRepo<Key,Value> by parentRepo {
protected open val parentRepo: ReadKeyValuesRepo<Key, Value>,
protected open val kvCache: KVCache<Key, List<Value>>
) : ReadKeyValuesRepo<Key,Value> by parentRepo, CacheRepo {
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
return kvCache.get(k) ?.paginate(
pagination.let { if (reversed) it.reverse(count(k)) else it }
@@ -29,12 +30,21 @@ open class ReadKeyValuesCacheRepo<Key,Value>(
override suspend fun contains(k: Key): Boolean = kvCache.contains(k) || parentRepo.contains(k)
}
fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
kvCache: KVCache<Key, List<Value>>
) = ReadKeyValuesCacheRepo(this, kvCache)
open class KeyValuesCacheRepo<Key,Value>(
parentRepo: KeyValuesRepo<Key, Value>,
kvCache: KVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : ReadKeyValuesCacheRepo<Key,Value>(parentRepo, kvCache), KeyValuesRepo<Key,Value>, WriteKeyValuesRepo<Key,Value> by parentRepo {
) : ReadKeyValuesCacheRepo<Key,Value>(parentRepo, kvCache), KeyValuesRepo<Key,Value>, WriteKeyValuesRepo<Key,Value> by parentRepo, CacheRepo {
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)) }.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.minus(it.second) ?: return@onEach) }.launchIn(scope)
protected val onDataClearedJob = parentRepo.onDataCleared.onEach { kvCache.unset(it) }.launchIn(scope)
}
fun <Key, Value> KeyValuesRepo<Key, Value>.cached(
kvCache: KVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = KeyValuesCacheRepo(this, kvCache, scope)

View File

@@ -0,0 +1,8 @@
package dev.inmo.micro_utils.repos.cache.cache
/**
* This interface declares that current type of [KVCache] will contains all the data all the time of its life
*/
interface FullKVCache<K, V> : KVCache<K, V> {
companion object
}

View File

@@ -0,0 +1,7 @@
package dev.inmo.micro_utils.repos.cache.cache
import dev.inmo.micro_utils.repos.*
interface KVCache<K, V> : KeyValueRepo<K, V> {
companion object
}

View File

@@ -0,0 +1,28 @@
package dev.inmo.micro_utils.repos.cache.cache
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.MapKeyValueRepo
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
open class SimpleFullKVCache<K, V>(
private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) : FullKVCache<K, V>, KeyValueRepo<K, V> by kvParent {
protected val syncMutex = Mutex()
override suspend fun set(toSet: Map<K, V>) {
syncMutex.withLock {
kvParent.set(toSet)
}
}
override suspend fun unset(toUnset: List<K>) {
syncMutex.withLock {
kvParent.unset(toUnset)
}
}
}
inline fun <K, V> FullKVCache(
kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) = SimpleFullKVCache<K, V>(kvParent)

View File

@@ -0,0 +1,42 @@
package dev.inmo.micro_utils.repos.cache.cache
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
open class SimpleKVCache<K, V>(
protected val cachedValuesCount: Int,
private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) : KVCache<K, V>, KeyValueRepo<K, V> by kvParent {
protected open val cacheQueue = ArrayDeque<K>(cachedValuesCount)
protected val syncMutex = Mutex()
protected suspend fun makeUnset(toUnset: List<K>) {
cacheQueue.removeAll(toUnset)
kvParent.unset(toUnset)
}
override suspend fun set(toSet: Map<K, V>) {
syncMutex.withLock {
for ((k, v) in toSet) {
if (cacheQueue.size >= cachedValuesCount) {
cacheQueue.removeFirstOrNull() ?.let {
kvParent.unset(it)
}
}
cacheQueue.addLast(k)
kvParent.set(k, v)
}
}
}
override suspend fun unset(toUnset: List<K>) {
syncMutex.withLock { makeUnset(toUnset) }
}
}
inline fun <K, V> KVCache(
cachedValuesCount: Int,
kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) = SimpleKVCache<K, V>(cachedValuesCount, kvParent)

View File

@@ -0,0 +1,95 @@
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<ObjectType, IdType>(
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
protected open val kvCache: FullKVCache<IdType, ObjectType>,
protected open val idGetter: (ObjectType) -> IdType
) : ReadCRUDRepo<ObjectType, IdType>, FullCacheRepo {
protected inline fun <T> doOrTakeAndActualize(
action: FullKVCache<IdType, ObjectType>.() -> Optional<T>,
actionElse: ReadCRUDRepo<ObjectType, IdType>.() -> T,
actualize: FullKVCache<IdType, ObjectType>.(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<ObjectType> = doOrTakeAndActualize(
{ values(pagination).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
{ getByPagination(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 <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached(
kvCache: FullKVCache<IdType, ObjectType>,
idGetter: (ObjectType) -> IdType
) = FullReadCRUDCacheRepo(this, kvCache, idGetter)
open class FullCRUDCacheRepo<ObjectType, IdType, InputValueType>(
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
kvCache: FullKVCache<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
idGetter: (ObjectType) -> IdType
) : FullReadCRUDCacheRepo<ObjectType, IdType>(
parentRepo,
kvCache,
idGetter
),
WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo(
parentRepo,
kvCache,
scope,
idGetter
),
CRUDRepo<ObjectType, IdType, InputValueType>
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
kvCache: FullKVCache<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
idGetter: (ObjectType) -> IdType
) = FullCRUDCacheRepo(this, kvCache, scope, idGetter)

View File

@@ -0,0 +1,5 @@
package dev.inmo.micro_utils.repos.cache.full
import dev.inmo.micro_utils.repos.cache.CacheRepo
interface FullCacheRepo : CacheRepo

View File

@@ -0,0 +1,104 @@
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.repos.*
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.pagination.getAll
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
open class FullReadKeyValueCacheRepo<Key,Value>(
protected open val parentRepo: ReadKeyValueRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, Value>,
) : ReadKeyValueRepo<Key, Value>, FullCacheRepo {
protected inline fun <T> doOrTakeAndActualize(
action: FullKVCache<Key, Value>.() -> Optional<T>,
actionElse: ReadKeyValueRepo<Key, Value>.() -> T,
actualize: FullKVCache<Key, Value>.(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()
kvCache.set(parentRepo.getAll { keys(it) }.toMap())
}
override suspend fun get(k: Key): Value? = doOrTakeAndActualize(
{ get(k) ?.optional ?: Optional.absent() },
{ get(k) },
{ set(k, it ?: return@doOrTakeAndActualize) }
)
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = doOrTakeAndActualize(
{ values(pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
{ values(pagination, reversed) },
{ 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(key: Key): Boolean = doOrTakeAndActualize(
{ contains(key).takeIf { it }.optionalOrAbsentIfNull },
{ contains(key) },
{ if (it) parentRepo.get(key) ?.also { kvCache.set(key, it) } }
)
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = doOrTakeAndActualize(
{ keys(pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
{ keys(pagination, reversed) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = doOrTakeAndActualize(
{ keys(v, pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
{ parentRepo.keys(v, pagination, reversed) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
}
fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached(
kvCache: FullKVCache<Key, Value>
) = FullReadKeyValueCacheRepo(this, kvCache)
open class FullWriteKeyValueCacheRepo<Key,Value>(
protected open val parentRepo: WriteKeyValueRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : WriteKeyValueRepo<Key, Value> by parentRepo, FullCacheRepo {
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope)
}
fun <Key, Value> WriteKeyValueRepo<Key, Value>.caching(
kvCache: FullKVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = FullWriteKeyValueCacheRepo(this, kvCache, scope)
open class FullKeyValueCacheRepo<Key,Value>(
parentRepo: KeyValueRepo<Key, Value>,
kvCache: FullKVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : FullWriteKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, scope),
KeyValueRepo<Key,Value>,
ReadKeyValueRepo<Key, Value> by FullReadKeyValueCacheRepo(parentRepo, kvCache) {
override suspend fun unsetWithValues(toUnset: List<Value>) = parentRepo.unsetWithValues(toUnset)
}
fun <Key, Value> KeyValueRepo<Key, Value>.cached(
kvCache: FullKVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = FullKeyValueCacheRepo(this, kvCache, scope)

View File

@@ -0,0 +1,154 @@
package dev.inmo.micro_utils.repos.cache.full
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.*
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
open class FullReadKeyValuesCacheRepo<Key,Value>(
protected open val parentRepo: ReadKeyValuesRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, List<Value>>,
) : ReadKeyValuesRepo<Key, Value>, FullCacheRepo {
protected inline fun <T> doOrTakeAndActualize(
action: FullKVCache<Key, List<Value>>.() -> Optional<T>,
actionElse: ReadKeyValuesRepo<Key, Value>.() -> T,
actualize: FullKVCache<Key, List<Value>>.(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 actualizeKey(k: Key) {
kvCache.set(k, parentRepo.getAll(k))
}
protected open suspend fun actualizeAll() {
doAllWithCurrentPaging { kvCache.keys(it).also { kvCache.unset(it.results) } }
kvCache.set(parentRepo.getAll())
}
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
return doOrTakeAndActualize(
{
get(k) ?.paginate(
pagination.let { if (reversed) it.reverse(count(k)) else it }
) ?.let {
if (reversed) it.copy(results = it.results.reversed()) else it
}.optionalOrAbsentIfNull
},
{ get(k, pagination, reversed) },
{ actualizeKey(k) }
)
}
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
return doOrTakeAndActualize(
{
kvCache.keys(pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull
},
{ parentRepo.keys(pagination, reversed) },
{ actualizeAll() }
)
}
override suspend fun count(): Long = doOrTakeAndActualize(
{ count().takeIf { it != 0L }.optionalOrAbsentIfNull },
{ count() },
{ if (it != 0L) actualizeAll() }
)
override suspend fun count(k: Key): Long = doOrTakeAndActualize(
{ count().takeIf { it != 0L }.optionalOrAbsentIfNull },
{ count() },
{ if (it != 0L) actualizeKey(k) }
)
override suspend fun contains(k: Key, v: Value): Boolean = doOrTakeAndActualize(
{ get(k) ?.contains(v).takeIf { it == true }.optionalOrAbsentIfNull },
{ contains(k, v) },
{ if (it) actualizeKey(k) }
)
override suspend fun contains(k: Key): Boolean = doOrTakeAndActualize(
{ contains(k).takeIf { it }.optionalOrAbsentIfNull },
{ contains(k) },
{ if (it) actualizeKey(k) }
)
override suspend fun keys(
v: Value,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = doOrTakeAndActualize(
{
val keys = getAllWithNextPaging { keys(it) }.filter { get(it) ?.contains(v) == true }.optionallyReverse(reversed)
if (keys.isNotEmpty()) {
keys.paginate(pagination.optionallyReverse(keys.size, reversed)).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull
} else {
Optional.absent()
}
},
{ parentRepo.keys(v, pagination, reversed) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
}
fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
kvCache: FullKVCache<Key, List<Value>>
) = FullReadKeyValuesCacheRepo(this, kvCache)
open class FullWriteKeyValuesCacheRepo<Key,Value>(
protected open val parentRepo: WriteKeyValuesRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : WriteKeyValuesRepo<Key, Value> by parentRepo, FullCacheRepo {
protected val onNewJob = parentRepo.onNewValue.onEach {
kvCache.set(
it.first,
kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)
)
}.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach {
kvCache.set(
it.first,
kvCache.get(it.first) ?.minus(it.second) ?: return@onEach
)
}.launchIn(scope)
}
fun <Key, Value> WriteKeyValuesRepo<Key, Value>.caching(
kvCache: FullKVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = FullWriteKeyValuesCacheRepo(this, kvCache, scope)
open class FullKeyValuesCacheRepo<Key,Value>(
parentRepo: KeyValuesRepo<Key, Value>,
kvCache: FullKVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : FullWriteKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, scope),
KeyValuesRepo<Key, Value>,
ReadKeyValuesRepo<Key, Value> by FullReadKeyValuesCacheRepo(parentRepo, kvCache) {
override suspend fun clearWithValue(v: Value) {
doAllWithCurrentPaging {
keys(v, it).also {
remove(it.results.associateWith { listOf(v) })
}
}
}
}
fun <Key, Value> KeyValuesRepo<Key, Value>.caching(
kvCache: FullKVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = FullKeyValuesCacheRepo(this, kvCache, scope)

View File

@@ -1,22 +1,51 @@
package dev.inmo.micro_utils.repos
import dev.inmo.micro_utils.common.*
@Suppress("UNCHECKED_CAST")
interface MapperRepo<FromKey, FromValue, ToKey, ToValue> {
val keyMapper: SimpleSuspendableMapper<FromKey, ToKey>
get() = simpleSuspendableMapper(
{ it.toInnerKey() },
{ it.toOutKey() }
)
val valueMapper: SimpleSuspendableMapper<FromValue, ToValue>
get() = simpleSuspendableMapper(
{ it.toInnerValue() },
{ it.toOutValue() }
)
suspend fun FromKey.toOutKey() = this as ToKey
suspend fun FromValue.toOutValue() = this as ToValue
suspend fun ToKey.toInnerKey() = this as FromKey
suspend fun ToValue.toInnerValue() = this as FromValue
companion object
}
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> mapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
) = object : MapperRepo<FromKey, FromValue, ToKey, ToValue> {
class SimpleMapperRepo<FromKey, FromValue, ToKey, ToValue>(
private val keyFromToTo: suspend FromKey.() -> ToKey,
private val valueFromToTo: suspend FromValue.() -> ToValue,
private val keyToToFrom: suspend ToKey.() -> FromKey,
private val valueToToFrom: suspend ToValue.() -> FromValue
) : MapperRepo<FromKey, FromValue, ToKey, ToValue> {
override suspend fun FromKey.toOutKey(): ToKey = keyFromToTo()
override suspend fun FromValue.toOutValue(): ToValue = valueFromToTo()
override suspend fun ToKey.toInnerKey(): FromKey = keyToToFrom()
override suspend fun ToValue.toInnerValue(): FromValue = valueToToFrom()
}
operator fun <FromKey, FromValue, ToKey, ToValue> MapperRepo.Companion.invoke(
keyFromToTo: suspend FromKey.() -> ToKey,
valueFromToTo: suspend FromValue.() -> ToValue,
keyToToFrom: suspend ToKey.() -> FromKey,
valueToToFrom: suspend ToValue.() -> FromValue
) = SimpleMapperRepo(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> mapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
) = MapperRepo(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)

View File

@@ -48,6 +48,10 @@ interface KeyValueRepo<Key, Value> : ReadKeyValueRepo<Key, Value>, WriteKeyValue
}
}
}
suspend fun clear() {
doAllWithCurrentPaging { keys(it).also { unset(it.results) } }
}
}
typealias StandardKeyValueRepo<Key,Value> = KeyValueRepo<Key, Value>

View File

@@ -0,0 +1,130 @@
package dev.inmo.micro_utils.repos.mappers
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
open class MapperReadCRUDRepo<FromId, FromRegistered, ToId, ToRegistered>(
private val to: ReadCRUDRepo<ToRegistered, ToId>,
mapper: MapperRepo<FromId, FromRegistered, ToId, ToRegistered>
) : ReadCRUDRepo<FromRegistered, FromId>, MapperRepo<FromId, FromRegistered, ToId, ToRegistered> by mapper {
override suspend fun getByPagination(
pagination: Pagination
): PaginationResult<FromRegistered> = to.getByPagination(
pagination
).let {
it.changeResultsUnchecked(
it.results.map { it.toInnerValue() }
)
}
override suspend fun count(): Long = to.count()
override suspend fun contains(id: FromId): Boolean = to.contains(id.toOutKey())
override suspend fun getById(id: FromId): FromRegistered? = to.getById(
id.toOutKey()
) ?.toInnerValue()
}
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> ReadCRUDRepo<ToValue, ToKey>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): ReadCRUDRepo<FromValue, FromKey> = MapperReadCRUDRepo(this, mapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadCRUDRepo<ToValue, ToKey>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): ReadCRUDRepo<FromValue, FromKey> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)
open class MapperWriteCRUDRepo<FromId, FromRegistered, FromInput, ToId, ToRegistered, ToInput>(
private val to: WriteCRUDRepo<ToRegistered, ToId, ToInput>,
mapper: MapperRepo<FromId, FromRegistered, ToId, ToRegistered>,
inputMapper: SimpleSuspendableMapper<FromInput, ToInput>
) : WriteCRUDRepo<FromRegistered, FromId, FromInput>,
MapperRepo<FromId, FromRegistered, ToId, ToRegistered> by mapper,
SimpleSuspendableMapper<FromInput, ToInput> by inputMapper {
override val newObjectsFlow: Flow<FromRegistered> = to.newObjectsFlow.map { it.toInnerValue() }
override val updatedObjectsFlow: Flow<FromRegistered> = to.updatedObjectsFlow.map { it.toInnerValue() }
override val deletedObjectsIdsFlow: Flow<FromId> = to.deletedObjectsIdsFlow.map { it.toInnerKey() }
override suspend fun deleteById(ids: List<FromId>) = to.deleteById(ids.map { it.toOutKey() })
override suspend fun update(
values: List<UpdatedValuePair<FromId, FromInput>>
): List<FromRegistered> = to.update(
values.map {
it.first.toOutKey() to convert(it.second)
}
).map { it.toInnerValue() }
override suspend fun update(
id: FromId,
value: FromInput
): FromRegistered? = to.update(id.toOutKey(), convert(value)) ?.toInnerValue()
override suspend fun create(values: List<FromInput>): List<FromRegistered> = to.create(
values.map {
convert(it)
}
).map {
it.toInnerValue()
}
}
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, FromInput, ToKey, ToValue, ToInput> WriteCRUDRepo<ToValue, ToKey, ToInput>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>,
simpleSuspendableMapper: SimpleSuspendableMapper<FromInput, ToInput>
): WriteCRUDRepo<FromValue, FromKey, FromInput> = MapperWriteCRUDRepo(this, mapper, simpleSuspendableMapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified FromInput, reified ToKey, reified ToValue, reified ToInput> WriteCRUDRepo<ToValue, ToKey, ToInput>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline inputFromToTo: suspend FromInput.() -> ToInput = { this as ToInput },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
noinline inputToToFrom: suspend ToInput.() -> FromInput = { this as FromInput },
): WriteCRUDRepo<FromValue, FromKey, FromInput> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom),
simpleSuspendableMapper({ inputToToFrom(it) }, { inputFromToTo(it) })
)
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
open class MapperCRUDRepo<FromId, FromRegistered, FromInput, ToId, ToRegistered, ToInput>(
private val to: CRUDRepo<ToRegistered, ToId, ToInput>,
private val mapper: MapperRepo<FromId, FromRegistered, ToId, ToRegistered>,
private val inputMapper: SimpleSuspendableMapper<FromInput, ToInput>
) : CRUDRepo<FromRegistered, FromId, FromInput>,
MapperRepo<FromId, FromRegistered, ToId, ToRegistered> by mapper,
ReadCRUDRepo<FromRegistered, FromId> by MapperReadCRUDRepo(to, mapper),
WriteCRUDRepo<FromRegistered, FromId, FromInput> by MapperWriteCRUDRepo(to, mapper, inputMapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, FromInput, ToKey, ToValue, ToInput> CRUDRepo<ToValue, ToKey, ToInput>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>,
simpleSuspendableMapper: SimpleSuspendableMapper<FromInput, ToInput>
): CRUDRepo<FromValue, FromKey, FromInput> = MapperCRUDRepo(this, mapper, simpleSuspendableMapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified FromInput, reified ToKey, reified ToValue, reified ToInput> CRUDRepo<ToValue, ToKey, ToInput>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline inputFromToTo: suspend FromInput.() -> ToInput = { this as ToInput },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
noinline inputToToFrom: suspend ToInput.() -> FromInput = { this as FromInput },
): CRUDRepo<FromValue, FromKey, FromInput> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom),
simpleSuspendableMapper({ inputToToFrom(it) }, { inputFromToTo(it) })
)

View File

@@ -77,10 +77,10 @@ inline fun <FromKey, FromValue, ToKey, ToValue> ReadKeyValueRepo<ToKey, ToValue>
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadKeyValueRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): ReadKeyValueRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)
@@ -122,10 +122,10 @@ inline fun <FromKey, FromValue, ToKey, ToValue> WriteKeyValueRepo<ToKey, ToValue
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteKeyValueRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): WriteKeyValueRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)
@@ -148,10 +148,10 @@ inline fun <FromKey, FromValue, ToKey, ToValue> KeyValueRepo<ToKey, ToValue>.wit
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> KeyValueRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): KeyValueRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)

View File

@@ -75,10 +75,10 @@ inline fun <FromKey, FromValue, ToKey, ToValue> ReadKeyValuesRepo<ToKey, ToValue
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadKeyValuesRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): ReadKeyValuesRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)
@@ -128,10 +128,10 @@ inline fun <FromKey, FromValue, ToKey, ToValue> WriteKeyValuesRepo<ToKey, ToValu
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteKeyValuesRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): WriteKeyValuesRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)
@@ -154,10 +154,10 @@ inline fun <FromKey, FromValue, ToKey, ToValue> KeyValuesRepo<ToKey, ToValue>.wi
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> KeyValuesRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): KeyValuesRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)

View File

@@ -34,7 +34,7 @@ class FileReadKeyValueRepo(
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<File> {
val count = count()
val resultPagination = if (reversed) pagination.reverse(count) else pagination
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndex) ?: return emptyPaginationResult()
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndexExclusive) ?: return emptyPaginationResult()
if (reversed) {
filesPaths.reverse()
}
@@ -47,7 +47,7 @@ class FileReadKeyValueRepo(
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<String> {
val count = count()
val resultPagination = if (reversed) pagination.reverse(count) else pagination
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndex) ?: return emptyPaginationResult()
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndexExclusive) ?: return emptyPaginationResult()
if (reversed) {
filesPaths.reverse()
}
@@ -99,7 +99,9 @@ class FileWriteKeyValueRepo(
override val onValueRemoved: Flow<String> = _onValueRemoved.asSharedFlow()
init {
folder.mkdirs()
if (!folder.mkdirs() && !folder.exists()) {
error("Unable to create folder ${folder.absolutePath}")
}
filesChangedProcessingScope ?.let {
it.launch {
try {
@@ -144,15 +146,17 @@ class FileWriteKeyValueRepo(
}
override suspend fun set(toSet: Map<String, File>) {
supervisorScope {
toSet.map { (filename, fileSource) ->
launch {
val file = File(folder, filename)
val scope = CoroutineScope(currentCoroutineContext())
toSet.map { (filename, fileSource) ->
scope.launch {
val file = File(folder, filename)
file.delete()
fileSource.copyTo(file, overwrite = true)
_onNewValue.emit(filename to file)
file.delete()
fileSource.copyTo(file, overwrite = true)
if (!file.exists()) {
error("Can't create file $file with new content")
}
_onNewValue.emit(filename to file)
}
}.joinAll()
}

View File

@@ -56,25 +56,25 @@ class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> (
override suspend fun create(values: List<InputValue>): List<ObjectType> = unifiedRequester.unipost(
buildStandardUrl(baseUrl, createRouting),
BodyPair(listInputSerializer, values),
Pair(listInputSerializer, values),
listObjectsSerializer
)
override suspend fun update(id: IdType, value: InputValue): ObjectType? = unifiedRequester.unipost(
buildStandardUrl(baseUrl, updateRouting),
BodyPair(inputUpdateSerializer, id to value),
Pair(inputUpdateSerializer, id to value),
objectsNullableSerializer
)
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValue>>): List<ObjectType> = unifiedRequester.unipost(
buildStandardUrl(baseUrl, updateManyRouting),
BodyPair(listInputUpdateSerializer, values),
Pair(listInputUpdateSerializer, values),
listObjectsSerializer
)
override suspend fun deleteById(ids: List<IdType>) = unifiedRequester.unipost(
buildStandardUrl(baseUrl, deleteByIdRouting),
BodyPair(listIdsSerializer, ids),
Pair(listIdsSerializer, ids),
Unit.serializer()
)
}

View File

@@ -45,7 +45,7 @@ class KtorWriteStandardKeyValueRepo<K, V> (
baseUrl,
setRoute
),
BodyPair(keyValueMapSerializer, toSet),
Pair(keyValueMapSerializer, toSet),
Unit.serializer()
)
@@ -54,7 +54,7 @@ class KtorWriteStandardKeyValueRepo<K, V> (
baseUrl,
unsetRoute,
),
BodyPair(keysListSerializer, toUnset),
Pair(keysListSerializer, toUnset),
Unit.serializer()
)
@@ -63,7 +63,7 @@ class KtorWriteStandardKeyValueRepo<K, V> (
baseUrl,
unsetWithValuesRoute,
),
BodyPair(valuesListSerializer, toUnset),
Pair(valuesListSerializer, toUnset),
Unit.serializer()
)
}

View File

@@ -47,7 +47,7 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
baseUrl,
removeRoute,
),
BodyPair(keyValueMapSerializer, toRemove),
Pair(keyValueMapSerializer, toRemove),
Unit.serializer(),
)
@@ -56,7 +56,7 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
baseUrl,
addRoute,
),
BodyPair(keyValueMapSerializer, toAdd),
Pair(keyValueMapSerializer, toAdd),
Unit.serializer(),
)
override suspend fun clear(k: Key) = unifiedRequester.unipost(
@@ -64,7 +64,7 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
baseUrl,
clearRoute,
),
BodyPair(keySerializer, k),
Pair(keySerializer, k),
Unit.serializer(),
)
@@ -73,7 +73,7 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
baseUrl,
clearWithValueRoute,
),
BodyPair(valueSerializer, v),
Pair(valueSerializer, v),
Unit.serializer(),
)
@@ -82,7 +82,7 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
baseUrl,
setRoute,
),
BodyPair(keyValueMapSerializer, toSet),
Pair(keyValueMapSerializer, toSet),
Unit.serializer(),
)
}