mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-10-31 12:10:29 +00:00 
			
		
		
		
	Compare commits
	
		
			17 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ce15ff4e0a | |||
| 382b956beb | |||
| 36deab4909 | |||
| 550fc59d9d | |||
| 9100a57458 | |||
| 74d9bbccd9 | |||
| 75e602a349 | |||
| e73644db10 | |||
| 148e6bdae7 | |||
| 1b540199f0 | |||
| 30b70e9984 | |||
| c1557cff27 | |||
| 2d662f91b3 | |||
| c4a08e52e5 | |||
| 08c371c142 | |||
| 8e62dd460c | |||
| 1f9302dc94 | 
							
								
								
									
										21
									
								
								.github/workflows/dokka_push.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.github/workflows/dokka_push.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | name: Publish KDocs | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     branches: | ||||||
|  |       - master | ||||||
|  | jobs: | ||||||
|  |   publishing: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v2 | ||||||
|  |       - uses: actions/setup-java@v1 | ||||||
|  |         with: | ||||||
|  |           java-version: 1.8 | ||||||
|  |       - name: Build | ||||||
|  |         run: ./gradlew dokkaHtml | ||||||
|  |       - name: Publish KDocs | ||||||
|  |         uses: peaceiris/actions-gh-pages@v3 | ||||||
|  |         with: | ||||||
|  |           github_token: ${{ secrets.GITHUB_TOKEN }} | ||||||
|  |           publish_dir: ./dokka/build/dokka/html | ||||||
|  |           publish_branch: kdocs | ||||||
							
								
								
									
										40
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,45 @@ | |||||||
| # Changelog | # Changelog | ||||||
|  |  | ||||||
|  | ## 0.4.33 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |     * `Ktor`: `1.5.2` -> `1.5.3` | ||||||
|  | * `Coroutines` | ||||||
|  |     * Add `WeakJob` workaround: | ||||||
|  |         * `CoroutineScope#weakLaunch` | ||||||
|  |         * `CoroutineScope#weakAsync` | ||||||
|  |  | ||||||
|  | ## 0.4.32 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |     * `Kotlin Exposed`: `0.29.1` -> `0.30.1` | ||||||
|  |  | ||||||
|  | ## 0.4.31 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |     * `Kotlin`: `1.4.31` -> `1.4.32` | ||||||
|  | * `Pagination`: | ||||||
|  |     * New extensions `PaginationResult.changeResultsUnchecked` and `PaginationResult.changeResults` for mapping results | ||||||
|  |     with the same parameters, but different data | ||||||
|  |     * Extension `PaginationResult.thisPageIfNotEmpty` now is typed and will return `PaginationResult?` with the same | ||||||
|  |     generic type as income `PaginationResult` | ||||||
|  |     * New extension `PaginationResult.currentPageIfNotEmpty` - shortcut for `PaginationResult.thisPageIfNotEmpty` | ||||||
|  |     * New common functions. They were created as replacements for currently available for more comfortable work | ||||||
|  |     with repos pagination: | ||||||
|  |         * `doForAll` | ||||||
|  |         * `doForAllWithNextPaging` | ||||||
|  |         * `doForAllWithCurrentPaging` | ||||||
|  |         * `getAll` | ||||||
|  |         * `getAllWithNextPaging` | ||||||
|  |         * `getAllWithCurrentPaging` | ||||||
|  | * `Coroutines`: | ||||||
|  |     * Rewrite `subscribeSafelyWithoutExceptions` | ||||||
|  |         * Now `subscribeSafelyWithoutExceptions` will use default handler instead of skipping | ||||||
|  |     * New extension `subscribeSafelySkippingExceptions` | ||||||
|  | * `Repos` | ||||||
|  |     * New subproject `repos.cache` - this subproject will contain repos with data caching mechanisms | ||||||
|  |     * Most old `doForAll` methods have been deprecated | ||||||
|  |  | ||||||
| ## 0.4.30 | ## 0.4.30 | ||||||
|  |  | ||||||
| * `Versions`: | * `Versions`: | ||||||
|   | |||||||
| @@ -25,13 +25,25 @@ inline fun <T> Flow<T>.subscribeSafely( | |||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Use [subscribeSafelyWithoutExceptions], but all exceptions inside of [safely] will be skipped |  * Use [subscribeSafelyWithoutExceptions], but all exceptions will be passed to [defaultSafelyExceptionHandler] | ||||||
|  */ |  */ | ||||||
| inline fun <T> Flow<T>.subscribeSafelyWithoutExceptions( | inline fun <T> Flow<T>.subscribeSafelyWithoutExceptions( | ||||||
|     scope: CoroutineScope, |     scope: CoroutineScope, | ||||||
|     noinline block: suspend (T) -> Unit |     noinline block: suspend (T) -> Unit | ||||||
| ) = subscribeSafely( | ) = subscribe(scope) { | ||||||
|     scope, |     safelyWithoutExceptions { | ||||||
|     {}, |         block(it) | ||||||
|     block |     } | ||||||
| ) | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Use [subscribeSafelyWithoutExceptions], but all exceptions inside of [safely] will be skipped | ||||||
|  |  */ | ||||||
|  | inline fun <T> Flow<T>.subscribeSafelySkippingExceptions( | ||||||
|  |     scope: CoroutineScope, | ||||||
|  |     noinline block: suspend (T) -> Unit | ||||||
|  | ) = subscribe(scope) { | ||||||
|  |     safelyWithoutExceptions({ /* skip exceptions */ }) { | ||||||
|  |         block(it) | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -0,0 +1,31 @@ | |||||||
|  | package dev.inmo.micro_utils.coroutines | ||||||
|  |  | ||||||
|  | import kotlinx.coroutines.* | ||||||
|  | import kotlin.coroutines.CoroutineContext | ||||||
|  | import kotlin.coroutines.EmptyCoroutineContext | ||||||
|  |  | ||||||
|  | private fun CoroutineScope.createWeakSubScope() = CoroutineScope(coroutineContext.minusKey(Job)).also { newScope -> | ||||||
|  |     coroutineContext.job.invokeOnCompletion { newScope.cancel() } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun CoroutineScope.weakLaunch( | ||||||
|  |     context: CoroutineContext = EmptyCoroutineContext, | ||||||
|  |     start: CoroutineStart = CoroutineStart.DEFAULT, | ||||||
|  |     block: suspend CoroutineScope.() -> Unit | ||||||
|  | ): Job { | ||||||
|  |     val scope = createWeakSubScope() | ||||||
|  |     val job = scope.launch(context, start, block) | ||||||
|  |     job.invokeOnCompletion { scope.cancel() } | ||||||
|  |     return job | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <T> CoroutineScope.weakAsync( | ||||||
|  |     context: CoroutineContext = EmptyCoroutineContext, | ||||||
|  |     start: CoroutineStart = CoroutineStart.DEFAULT, | ||||||
|  |     block: suspend CoroutineScope.() -> T | ||||||
|  | ): Deferred<T> { | ||||||
|  |     val scope = createWeakSubScope() | ||||||
|  |     val deferred = scope.async(context, start, block) | ||||||
|  |     deferred.invokeOnCompletion { scope.cancel() } | ||||||
|  |     return deferred | ||||||
|  | } | ||||||
| @@ -0,0 +1,40 @@ | |||||||
|  | package dev.inmo.micro_utils.coroutines | ||||||
|  |  | ||||||
|  | import kotlinx.coroutines.* | ||||||
|  | import org.junit.Test | ||||||
|  |  | ||||||
|  | class WeakJob { | ||||||
|  |     @Test | ||||||
|  |     fun `test that weak jobs works correctly`() { | ||||||
|  |         val scope = CoroutineScope(Dispatchers.Default) | ||||||
|  |         lateinit var weakLaunchJob: Job | ||||||
|  |         lateinit var weakAsyncJob: Job | ||||||
|  |         scope.launchSynchronously { | ||||||
|  |             val completeDeferred = Job() | ||||||
|  |             coroutineScope { | ||||||
|  |                 weakLaunchJob = weakLaunch { | ||||||
|  |                     while (isActive) { | ||||||
|  |                         delay(100L) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 weakAsyncJob = weakAsync { | ||||||
|  |                     while (isActive) { | ||||||
|  |                         delay(100L) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 coroutineContext.job.invokeOnCompletion { | ||||||
|  |                     scope.launch { | ||||||
|  |                         delay(1000L) | ||||||
|  |                         completeDeferred.complete() | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 launch { delay(1000L); cancel() } | ||||||
|  |             } | ||||||
|  |             completeDeferred.join() | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         assert(!weakLaunchJob.isActive) | ||||||
|  |         assert(!weakAsyncJob.isActive) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -6,12 +6,12 @@ kotlin.incremental.js=true | |||||||
| android.useAndroidX=true | android.useAndroidX=true | ||||||
| android.enableJetifier=true | android.enableJetifier=true | ||||||
|  |  | ||||||
| kotlin_version=1.4.31 | kotlin_version=1.4.32 | ||||||
| kotlin_coroutines_version=1.4.3 | kotlin_coroutines_version=1.4.3 | ||||||
| kotlin_serialisation_core_version=1.1.0 | kotlin_serialisation_core_version=1.1.0 | ||||||
| kotlin_exposed_version=0.29.1 | kotlin_exposed_version=0.30.1 | ||||||
|  |  | ||||||
| ktor_version=1.5.2 | ktor_version=1.5.3 | ||||||
|  |  | ||||||
| klockVersion=2.0.7 | klockVersion=2.0.7 | ||||||
|  |  | ||||||
| @@ -39,10 +39,10 @@ crypto_js_version=4.0.0 | |||||||
|  |  | ||||||
| # Dokka | # Dokka | ||||||
|  |  | ||||||
| dokka_version=1.4.20 | dokka_version=1.4.30 | ||||||
|  |  | ||||||
| # Project data | # Project data | ||||||
|  |  | ||||||
| group=dev.inmo | group=dev.inmo | ||||||
| version=0.4.30 | version=0.4.33 | ||||||
| android_code_version=34 | android_code_version=37 | ||||||
|   | |||||||
| @@ -12,6 +12,22 @@ data class PaginationResult<T>( | |||||||
|  |  | ||||||
| fun <T> emptyPaginationResult() = PaginationResult<T>(0, 0, emptyList(), 0) | fun <T> emptyPaginationResult() = PaginationResult<T>(0, 0, emptyList(), 0) | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @return New [PaginationResult] with [data] without checking of data sizes equality | ||||||
|  |  */ | ||||||
|  | fun <I, O> PaginationResult<I>.changeResultsUnchecked( | ||||||
|  |     data: List<O> | ||||||
|  | ): PaginationResult<O> = PaginationResult(page, pagesNumber, data, size) | ||||||
|  | /** | ||||||
|  |  * @return New [PaginationResult] with [data] <b>with</b> checking of data sizes equality | ||||||
|  |  */ | ||||||
|  | fun <I, O> PaginationResult<I>.changeResults( | ||||||
|  |     data: List<O> | ||||||
|  | ): PaginationResult<O> { | ||||||
|  |     require(data.size == results.size) | ||||||
|  |     return changeResultsUnchecked(data) | ||||||
|  | } | ||||||
|  |  | ||||||
| fun <T> List<T>.createPaginationResult( | fun <T> List<T>.createPaginationResult( | ||||||
|     pagination: Pagination, |     pagination: Pagination, | ||||||
|     commonObjectsNumber: Long |     commonObjectsNumber: Long | ||||||
|   | |||||||
| @@ -21,8 +21,10 @@ inline fun PaginationResult<*>.nextPageIfNotEmpty() = if (results.isNotEmpty()) | |||||||
| } | } | ||||||
|  |  | ||||||
| @Suppress("NOTHING_TO_INLINE") | @Suppress("NOTHING_TO_INLINE") | ||||||
| inline fun PaginationResult<*>.thisPageIfNotEmpty(): Pagination? = if (results.isNotEmpty()) { | inline fun <T> PaginationResult<T>.thisPageIfNotEmpty(): PaginationResult<T>? = if (results.isNotEmpty()) { | ||||||
|     this |     this | ||||||
| } else { | } else { | ||||||
|     null |     null | ||||||
| } | } | ||||||
|  |  | ||||||
|  | inline fun <T> PaginationResult<T>.currentPageIfNotEmpty() = thisPageIfNotEmpty() | ||||||
|   | |||||||
| @@ -0,0 +1,35 @@ | |||||||
|  | package dev.inmo.micro_utils.pagination.utils | ||||||
|  |  | ||||||
|  | import dev.inmo.micro_utils.pagination.* | ||||||
|  |  | ||||||
|  | suspend fun <T> doForAll( | ||||||
|  |     initialPagination: Pagination = FirstPagePagination(), | ||||||
|  |     paginationMapper: (PaginationResult<T>) -> Pagination?, | ||||||
|  |     block: suspend (Pagination) -> PaginationResult<T> | ||||||
|  | ) { | ||||||
|  |     doWithPagination(initialPagination) { | ||||||
|  |         block(it).let(paginationMapper) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | suspend fun <T> doForAllWithNextPaging( | ||||||
|  |     initialPagination: Pagination = FirstPagePagination(), | ||||||
|  |     block: suspend (Pagination) -> PaginationResult<T> | ||||||
|  | ) { | ||||||
|  |     doForAll( | ||||||
|  |         initialPagination, | ||||||
|  |         { it.nextPageIfNotEmpty() }, | ||||||
|  |         block | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | suspend fun <T> doAllWithCurrentPaging( | ||||||
|  |     initialPagination: Pagination = FirstPagePagination(), | ||||||
|  |     block: suspend (Pagination) -> PaginationResult<T> | ||||||
|  | ) { | ||||||
|  |     doForAll( | ||||||
|  |         initialPagination, | ||||||
|  |         { it.currentPageIfNotEmpty() }, | ||||||
|  |         block | ||||||
|  |     ) | ||||||
|  | } | ||||||
| @@ -0,0 +1,35 @@ | |||||||
|  | package dev.inmo.micro_utils.pagination.utils | ||||||
|  |  | ||||||
|  | import dev.inmo.micro_utils.pagination.* | ||||||
|  |  | ||||||
|  | suspend fun <T> getAll( | ||||||
|  |     initialPagination: Pagination = FirstPagePagination(), | ||||||
|  |     paginationMapper: (PaginationResult<T>) -> Pagination?, | ||||||
|  |     block: suspend (Pagination) -> PaginationResult<T> | ||||||
|  | ): List<T> { | ||||||
|  |     val results = mutableListOf<T>() | ||||||
|  |     doForAll(initialPagination, paginationMapper) { | ||||||
|  |         block(it).also { | ||||||
|  |             results.addAll(it.results) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return results.toList() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | suspend fun <T> getAllWithNextPaging( | ||||||
|  |     initialPagination: Pagination = FirstPagePagination(), | ||||||
|  |     block: suspend (Pagination) -> PaginationResult<T> | ||||||
|  | ): List<T> = getAll( | ||||||
|  |     initialPagination, | ||||||
|  |     { it.nextPageIfNotEmpty() }, | ||||||
|  |     block | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | suspend fun <T> getAllWithCurrentPaging( | ||||||
|  |     initialPagination: Pagination = FirstPagePagination(), | ||||||
|  |     block: suspend (Pagination) -> PaginationResult<T> | ||||||
|  | ): List<T> = getAll( | ||||||
|  |     initialPagination, | ||||||
|  |     { it.currentPageIfNotEmpty() }, | ||||||
|  |     block | ||||||
|  | ) | ||||||
							
								
								
									
										18
									
								
								repos/cache/build.gradle
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								repos/cache/build.gradle
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | plugins { | ||||||
|  |     id "org.jetbrains.kotlin.multiplatform" | ||||||
|  |     id "org.jetbrains.kotlin.plugin.serialization" | ||||||
|  |     id "com.android.library" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | apply from: "$mppProjectWithSerializationPresetPath" | ||||||
|  |  | ||||||
|  | kotlin { | ||||||
|  |     sourceSets { | ||||||
|  |         commonMain { | ||||||
|  |             dependencies { | ||||||
|  |                 api internalProject("micro_utils.repos.common") | ||||||
|  |                 api internalProject("micro_utils.repos.inmemory") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CRUDCacheRepo.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CRUDCacheRepo.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | package dev.inmo.micro_utils.repos.cache | ||||||
|  |  | ||||||
|  | import dev.inmo.micro_utils.repos.* | ||||||
|  | import kotlinx.coroutines.CoroutineScope | ||||||
|  | import kotlinx.coroutines.Dispatchers | ||||||
|  | import kotlinx.coroutines.flow.launchIn | ||||||
|  | import kotlinx.coroutines.flow.onEach | ||||||
|  |  | ||||||
|  | open class CRUDCacheRepo<ObjectType, IdType, InputValueType>( | ||||||
|  |     protected val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>, | ||||||
|  |     protected val kvCache: KVCache<IdType, ObjectType>, | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default), | ||||||
|  |     protected val idGetter: (ObjectType) -> IdType | ||||||
|  | ) : CRUDRepo<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) | ||||||
|  |  | ||||||
|  |     override suspend fun getById(id: IdType): ObjectType? = kvCache.get(id) ?: (parentRepo.getById(id) ?.also { | ||||||
|  |         kvCache.set(id, it) | ||||||
|  |     }) | ||||||
|  |  | ||||||
|  |     override suspend fun contains(id: IdType): Boolean = kvCache.contains(id) || parentRepo.contains(id) | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KVCache.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KVCache.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | 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) } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValueCacheRepo.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValueCacheRepo.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | package dev.inmo.micro_utils.repos.cache | ||||||
|  |  | ||||||
|  | import dev.inmo.micro_utils.repos.* | ||||||
|  | import kotlinx.coroutines.CoroutineScope | ||||||
|  | import kotlinx.coroutines.Dispatchers | ||||||
|  | import kotlinx.coroutines.flow.* | ||||||
|  | import kotlinx.coroutines.sync.Mutex | ||||||
|  | import kotlinx.coroutines.sync.withLock | ||||||
|  |  | ||||||
|  | open class KeyValueCacheRepo<Key,Value>( | ||||||
|  |     protected val parentRepo: KeyValueRepo<Key, Value>, | ||||||
|  |     protected val kvCache: KVCache<Key, Value>, | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default) | ||||||
|  | ) : KeyValueRepo<Key,Value> by parentRepo { | ||||||
|  |     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) | ||||||
|  |  | ||||||
|  |     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) | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValuesCacheRepo.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/KeyValuesCacheRepo.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | 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.pagination.utils.paginate | ||||||
|  | import dev.inmo.micro_utils.pagination.utils.reverse | ||||||
|  | import dev.inmo.micro_utils.repos.* | ||||||
|  | import kotlinx.coroutines.CoroutineScope | ||||||
|  | import kotlinx.coroutines.Dispatchers | ||||||
|  | import kotlinx.coroutines.flow.* | ||||||
|  | import kotlinx.coroutines.sync.Mutex | ||||||
|  | import kotlinx.coroutines.sync.withLock | ||||||
|  |  | ||||||
|  | open class KeyValuesCacheRepo<Key,Value>( | ||||||
|  |     protected val parentRepo: KeyValuesRepo<Key, Value>, | ||||||
|  |     protected val kvCache: KVCache<Key, List<Value>>, | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default) | ||||||
|  | ) : KeyValuesRepo<Key,Value> by parentRepo { | ||||||
|  |     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) | ||||||
|  |  | ||||||
|  |     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 } | ||||||
|  |         ) ?.let { | ||||||
|  |             if (reversed) it.copy(results = it.results.reversed()) else it | ||||||
|  |         } ?: parentRepo.get(k, pagination, reversed) | ||||||
|  |     } | ||||||
|  |     override suspend fun getAll(k: Key, reversed: Boolean): List<Value> { | ||||||
|  |         return kvCache.get(k) ?.let { | ||||||
|  |             if (reversed) it.reversed() else it | ||||||
|  |         } ?: parentRepo.getAll(k, reversed) | ||||||
|  |     } | ||||||
|  |     override suspend fun contains(k: Key, v: Value): Boolean = kvCache.get(k) ?.contains(v) ?: parentRepo.contains(k, v) | ||||||
|  |     override suspend fun contains(k: Key): Boolean = kvCache.contains(k) || parentRepo.contains(k) | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								repos/cache/src/main/AndroidManifest.xml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								repos/cache/src/main/AndroidManifest.xml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <manifest package="dev.inmo.micro_utils.repos.cache"/> | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| package dev.inmo.micro_utils.repos | package dev.inmo.micro_utils.repos | ||||||
|  |  | ||||||
| import dev.inmo.micro_utils.pagination.* | import dev.inmo.micro_utils.pagination.* | ||||||
|  | import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging | ||||||
| import kotlinx.coroutines.flow.Flow | import kotlinx.coroutines.flow.Flow | ||||||
|  |  | ||||||
| interface ReadOneToManyKeyValueRepo<Key, Value> : Repo { | interface ReadOneToManyKeyValueRepo<Key, Value> : Repo { | ||||||
| @@ -12,14 +13,12 @@ interface ReadOneToManyKeyValueRepo<Key, Value> : Repo { | |||||||
|     suspend fun count(k: Key): Long |     suspend fun count(k: Key): Long | ||||||
|     suspend fun count(): Long |     suspend fun count(): Long | ||||||
|  |  | ||||||
|     suspend fun getAll(k: Key, reversed: Boolean = false): List<Value> = mutableListOf<Value>().also { list -> |     suspend fun getAll(k: Key, reversed: Boolean = false): List<Value> { | ||||||
|         doWithPagination { |         val results = getAllWithNextPaging { get(k, it) } | ||||||
|             get(k, it).also { |         return if (reversed) { | ||||||
|                 list.addAll(it.results) |             results.reversed() | ||||||
|             }.nextPageIfNotEmpty() |         } else { | ||||||
|         } |             results | ||||||
|         if (reversed) { |  | ||||||
|             list.reverse() |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package dev.inmo.micro_utils.repos | package dev.inmo.micro_utils.repos | ||||||
|  |  | ||||||
| import dev.inmo.micro_utils.pagination.* | import dev.inmo.micro_utils.pagination.* | ||||||
|  | import dev.inmo.micro_utils.pagination.utils.doAllWithCurrentPaging | ||||||
| import kotlinx.coroutines.flow.Flow | import kotlinx.coroutines.flow.Flow | ||||||
|  |  | ||||||
| interface ReadStandardKeyValueRepo<Key, Value> : Repo { | interface ReadStandardKeyValueRepo<Key, Value> : Repo { | ||||||
| @@ -41,7 +42,7 @@ suspend inline fun <Key, Value> WriteStandardKeyValueRepo<Key, Value>.unsetWithV | |||||||
|  |  | ||||||
| interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value>, WriteStandardKeyValueRepo<Key, Value> { | interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value>, WriteStandardKeyValueRepo<Key, Value> { | ||||||
|     override suspend fun unsetWithValues(toUnset: List<Value>) = toUnset.forEach { v -> |     override suspend fun unsetWithValues(toUnset: List<Value>) = toUnset.forEach { v -> | ||||||
|         doWithPagination { |         doAllWithCurrentPaging { | ||||||
|             keys(v, it).also { |             keys(v, it).also { | ||||||
|                 unset(it.results) |                 unset(it.results) | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -1,31 +1,33 @@ | |||||||
| package dev.inmo.micro_utils.repos.pagination | package dev.inmo.micro_utils.repos.pagination | ||||||
|  |  | ||||||
| import dev.inmo.micro_utils.pagination.* | import dev.inmo.micro_utils.pagination.* | ||||||
|  | import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging | ||||||
|  | import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging | ||||||
| import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo | import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo | ||||||
|  |  | ||||||
|  | @Deprecated("Will be removed soon due to redundancy. Can be replaced with other doForAll extensions") | ||||||
| suspend inline fun <T, ID, REPO : ReadStandardCRUDRepo<T, ID>> REPO.doForAll( | suspend inline fun <T, ID, REPO : ReadStandardCRUDRepo<T, ID>> REPO.doForAll( | ||||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") |     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||||
|     methodCaller: suspend REPO.(Pagination) -> PaginationResult<T>, |     crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<T>, | ||||||
|     block: (List<T>) -> Unit |     crossinline block: (List<T>) -> Unit | ||||||
| ) { | ) { | ||||||
|     doWithPagination { |     doForAllWithNextPaging { | ||||||
|         methodCaller(it).also { |         methodCaller(it).also { | ||||||
|             block(it.results) |             block(it.results) | ||||||
|         }.nextPageIfNotEmpty() |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @Deprecated("Will be removed soon due to redundancy. Can be replaced with other doForAll extensions") | ||||||
| suspend inline fun <T, ID, REPO : ReadStandardCRUDRepo<T, ID>> REPO.doForAll( | suspend inline fun <T, ID, REPO : ReadStandardCRUDRepo<T, ID>> REPO.doForAll( | ||||||
|     block: (List<T>) -> Unit |     crossinline block: (List<T>) -> Unit | ||||||
| ) = doForAll({ getByPagination(it) }, block) | ) = doForAllWithNextPaging { | ||||||
|  |     getByPagination(it).also { block(it.results) } | ||||||
|  | } | ||||||
|  |  | ||||||
| suspend inline fun <T, ID, REPO : ReadStandardCRUDRepo<T, ID>> REPO.getAll( | suspend inline fun <T, ID, REPO : ReadStandardCRUDRepo<T, ID>> REPO.getAll( | ||||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") |     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||||
|     methodCaller: suspend REPO.(Pagination) -> PaginationResult<T> |     crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<T> | ||||||
| ): List<T> { | ): List<T> = getAllWithNextPaging { | ||||||
|     val resultList = mutableListOf<T>() |     methodCaller(this, it) | ||||||
|     doForAll(methodCaller) { |  | ||||||
|         resultList.addAll(it) |  | ||||||
|     } |  | ||||||
|     return resultList |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,8 +1,10 @@ | |||||||
| package dev.inmo.micro_utils.repos.pagination | package dev.inmo.micro_utils.repos.pagination | ||||||
|  |  | ||||||
| import dev.inmo.micro_utils.pagination.* | import dev.inmo.micro_utils.pagination.* | ||||||
|  | import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging | ||||||
| import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo | import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo | ||||||
|  |  | ||||||
|  | @Deprecated("Will be removed soon due to redundancy") | ||||||
| suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.doForAll( | suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.doForAll( | ||||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") |     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||||
|     methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>, |     methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>, | ||||||
| @@ -15,17 +17,17 @@ suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REP | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @Deprecated("Will be removed soon due to redundancy") | ||||||
| suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.doForAll( | suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.doForAll( | ||||||
|     block: (List<Pair<Key, Value>>) -> Unit |     block: (List<Pair<Key, Value>>) -> Unit | ||||||
| ) = doForAll({ keys(it, false) }, block) | ) = doForAll({ keys(it, false) }, block) | ||||||
|  |  | ||||||
| suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.getAll( | suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.getAll( | ||||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") |     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||||
|     methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key> |     crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key> | ||||||
| ): List<Pair<Key, Value>> { | ): List<Pair<Key, Value>> = getAllWithNextPaging { | ||||||
|     val resultList = mutableListOf<Pair<Key, Value>>() |     val result = methodCaller(it) | ||||||
|     doForAll(methodCaller) { |     result.changeResultsUnchecked( | ||||||
|         resultList.addAll(it) |         result.results.mapNotNull { it to (get(it) ?: return@mapNotNull null) } | ||||||
|     } |     ) | ||||||
|     return resultList |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,8 +1,10 @@ | |||||||
| package dev.inmo.micro_utils.repos.pagination | package dev.inmo.micro_utils.repos.pagination | ||||||
|  |  | ||||||
| import dev.inmo.micro_utils.pagination.* | import dev.inmo.micro_utils.pagination.* | ||||||
|  | import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging | ||||||
| import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo | import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo | ||||||
|  |  | ||||||
|  | @Deprecated("Will be removed soon due to redundancy") | ||||||
| suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.doForAll( | suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.doForAll( | ||||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") |     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||||
|     methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>, |     methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>, | ||||||
| @@ -25,17 +27,19 @@ suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> RE | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @Deprecated("Will be removed soon due to redundancy") | ||||||
| suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.doForAll( | suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.doForAll( | ||||||
|     block: (List<Pair<Key, List<Value>>>) -> Unit |     block: (List<Pair<Key, List<Value>>>) -> Unit | ||||||
| ) = doForAll({ keys(it, false) }, block) | ) = doForAll({ keys(it, false) }, block) | ||||||
|  |  | ||||||
| suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.getAll( | suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.getAll( | ||||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") |     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||||
|     methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key> |     crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key> | ||||||
| ): List<Pair<Key, List<Value>>> { | ): List<Pair<Key, List<Value>>> = getAllWithNextPaging { | ||||||
|     val resultList = mutableListOf<Pair<Key, List<Value>>>() |     val keysResult = methodCaller(it) | ||||||
|     doForAll(methodCaller) { |     keysResult.changeResultsUnchecked( | ||||||
|         resultList.addAll(it) |         keysResult.results.map { k -> | ||||||
|  |             k to getAll(k) | ||||||
|         } |         } | ||||||
|     return resultList |     ) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ String[] includes = [ | |||||||
|     ":pagination:ktor:server", |     ":pagination:ktor:server", | ||||||
|     ":mime_types", |     ":mime_types", | ||||||
|     ":repos:common", |     ":repos:common", | ||||||
|  |     ":repos:cache", | ||||||
|     ":repos:exposed", |     ":repos:exposed", | ||||||
|     ":repos:inmemory", |     ":repos:inmemory", | ||||||
|     ":repos:ktor:client", |     ":repos:ktor:client", | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user