mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-10-29 19:20:26 +00:00 
			
		
		
		
	Compare commits
	
		
			54 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 040dd517d8 | |||
| ffc2d23be7 | |||
| 49b009e59b | |||
| 778b6a555b | |||
| b3730998e9 | |||
| 837758aebe | |||
| 6ac4149aa1 | |||
| 278584ae6a | |||
| 126f9d5f41 | |||
| ce15ff4e0a | |||
| 382b956beb | |||
| 36deab4909 | |||
| 550fc59d9d | |||
| 9100a57458 | |||
| 74d9bbccd9 | |||
| 75e602a349 | |||
| e73644db10 | |||
| 148e6bdae7 | |||
| 1b540199f0 | |||
| 30b70e9984 | |||
| c1557cff27 | |||
| 2d662f91b3 | |||
| c4a08e52e5 | |||
| 08c371c142 | |||
| 8e62dd460c | |||
| 1f9302dc94 | |||
| 16f445f699 | |||
| b4abd564ec | |||
| 14ffafb0a7 | |||
| e0cc780887 | |||
| ab7d277167 | |||
| 8308c1df4d | |||
| 12c29f5180 | |||
| 2963098870 | |||
| 9f56b0a26d | |||
| 75851312fd | |||
| 3a3be138a5 | |||
| fb7d1f18b0 | |||
| d1c6c7696a | |||
| 5f38d9635d | |||
| 8185ea87b1 | |||
| 98bd07d025 | |||
| c502c70a21 | |||
| d933dc532b | |||
| 42594f0656 | |||
| 4b83ca19c3 | |||
| bab13f5e83 | |||
| 14c5f5a26c | |||
| b26a4f24d4 | |||
| 498ec673dc | |||
| 5c67ab6aec | |||
| f78359b5d7 | |||
| dbce612cb2 | |||
| 4322ffdb0a | 
							
								
								
									
										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 | ||||
							
								
								
									
										16
									
								
								.github/workflows/gradle_build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/gradle_build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,16 +0,0 @@ | ||||
| name: Build | ||||
|  | ||||
| on: [push] | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up JDK 1.8 | ||||
|         uses: actions/setup-java@v1 | ||||
|         with: | ||||
|           java-version: 1.8 | ||||
|       - name: Build with Gradle | ||||
|         run: ./gradlew build | ||||
							
								
								
									
										24
									
								
								.github/workflows/packages_push.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/packages_push.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
|  | ||||
| name: Publish package to GitHub Packages | ||||
| on: [push] | ||||
| jobs: | ||||
|   publishing: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - uses: actions/setup-java@v1 | ||||
|         with: | ||||
|           java-version: 1.8 | ||||
|       - name: Rewrite version | ||||
|         run: | | ||||
|           branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`" | ||||
|           cat gradle.properties | sed -e "s/^version=\([0-9\.]*\)/version=\1-branch_$branch-build${{ github.run_number }}/" > gradle.properties.tmp | ||||
|           rm gradle.properties | ||||
|           mv gradle.properties.tmp gradle.properties | ||||
|       - name: Build | ||||
|         run: ./gradlew build | ||||
|       - name: Publish | ||||
|         run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository -x signJsPublication -x signJvmPublication -x signKotlinMultiplatformPublication -x signMetadataPublication -x signAndroidDebugPublication -x signAndroidReleasePublication -x signKotlinMultiplatformPublication | ||||
|         env: | ||||
|           GITHUBPACKAGES_USER: ${{ github.actor }} | ||||
|           GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} | ||||
							
								
								
									
										94
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										94
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,99 @@ | ||||
| # Changelog | ||||
|  | ||||
| ## 0.4.35 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Kotlin Exposed`: `0.30.1` -> `0.30.2` | ||||
| * `Serialization`: | ||||
|     * `TypedSerializer`: | ||||
|         * Project has been inited | ||||
|  | ||||
| ## 0.4.34 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `uuid`: `0.2.3` -> `0.2.4` | ||||
| * `Repos`: | ||||
|     * `AbstractExposedCRUDRepo` now implements `StandardCRUDRepo` | ||||
|     * `AbstractMutableAndroidCRUDRepo` now implements `StandardCRUDRepo` | ||||
|  | ||||
| ## 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 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Klock`: `2.0.6` -> `2.0.7` | ||||
| * `Pagination`: | ||||
|     * New variable `defaultPaginationPageSize` has been added to be able to change default pagination size | ||||
|     * Add new value `firstPageWithOneElementPagination`  | ||||
|  | ||||
| ## 0.4.29 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Coroutines`: `1.4.2` -> `1.4.3` | ||||
| * `Repos`: | ||||
|     * `Common` | ||||
|         * `Android`: | ||||
|             * New `blockingReadableTransaction`/`blockingWritableTransaction` | ||||
|                 * Android databases realizations now use blocking transactions where it is possible | ||||
|             * Several improvements in transactions work | ||||
|  | ||||
| ## 0.4.28 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Kotlin`: `1.4.30` -> `1.4.31` | ||||
|     * `Ktor`: `1.5.1` -> `1.5.2` | ||||
| * `Coroutines` | ||||
|     * Add `createActionsActor`/`createSafeActionsActor` and `doWithSuspending` | ||||
|  | ||||
| ## 0.4.27 | ||||
|  | ||||
| * `Repos` | ||||
|     * `Exposed` | ||||
|         * Fix in `AbstractExposedWriteCRUDRepo` | ||||
|  | ||||
| ## 0.4.26 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Serialization`: `1.0.1` -> `1.1.0` | ||||
|  | ||||
| ## 0.4.25 | ||||
|  | ||||
| * `Matrix`: | ||||
|   | ||||
| @@ -0,0 +1,46 @@ | ||||
| package dev.inmo.micro_utils.coroutines | ||||
|  | ||||
| import kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.channels.Channel | ||||
| import kotlin.coroutines.* | ||||
|  | ||||
| interface ActorAction<T> { | ||||
|     suspend operator fun invoke(): T | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Planned to use with [doWithSuspending]. Will execute incoming lambdas sequentially | ||||
|  * | ||||
|  * @see actor | ||||
|  */ | ||||
| fun CoroutineScope.createActionsActor() = actor<suspend () -> Unit> { | ||||
|     it() | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Planned to use with [doWithSuspending]. Will execute incoming lambdas sequentially | ||||
|  * | ||||
|  * @see safeActor | ||||
|  */ | ||||
| inline fun CoroutineScope.createSafeActionsActor( | ||||
|     noinline onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler | ||||
| ) = safeActor<suspend () -> Unit>(Channel.UNLIMITED, onException) { | ||||
|     it() | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Must be use with actor created by [createActionsActor] or [createSafeActionsActor]. Will send lambda which will | ||||
|  * execute [action] and return result. | ||||
|  * | ||||
|  * @see suspendCoroutine | ||||
|  * @see safely | ||||
|  */ | ||||
| suspend fun <T> Channel<suspend () -> Unit>.doWithSuspending( | ||||
|     action: ActorAction<T> | ||||
| ) = suspendCoroutine<T> { | ||||
|     offer { | ||||
|         safely({ e -> it.resumeWithException(e) }) { | ||||
|             it.resume(action()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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( | ||||
|     scope: CoroutineScope, | ||||
|     noinline block: suspend (T) -> Unit | ||||
| ) = subscribeSafely( | ||||
|     scope, | ||||
|     {}, | ||||
|     block | ||||
| ) | ||||
| ) = subscribe(scope) { | ||||
|     safelyWithoutExceptions { | ||||
|         block(it) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 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,18 +6,18 @@ kotlin.incremental.js=true | ||||
| android.useAndroidX=true | ||||
| android.enableJetifier=true | ||||
|  | ||||
| kotlin_version=1.4.30 | ||||
| kotlin_coroutines_version=1.4.2 | ||||
| kotlin_serialisation_core_version=1.0.1 | ||||
| kotlin_exposed_version=0.29.1 | ||||
| kotlin_version=1.4.32 | ||||
| kotlin_coroutines_version=1.4.3 | ||||
| kotlin_serialisation_core_version=1.1.0 | ||||
| kotlin_exposed_version=0.30.2 | ||||
|  | ||||
| ktor_version=1.5.1 | ||||
| ktor_version=1.5.3 | ||||
|  | ||||
| klockVersion=2.0.6 | ||||
| klockVersion=2.0.7 | ||||
|  | ||||
| github_release_plugin_version=2.2.12 | ||||
|  | ||||
| uuidVersion=0.2.3 | ||||
| uuidVersion=0.2.4 | ||||
|  | ||||
| # ANDROID | ||||
|  | ||||
| @@ -39,10 +39,10 @@ crypto_js_version=4.0.0 | ||||
|  | ||||
| # Dokka | ||||
|  | ||||
| dokka_version=1.4.20 | ||||
| dokka_version=1.4.30 | ||||
|  | ||||
| # Project data | ||||
|  | ||||
| group=dev.inmo | ||||
| version=0.4.25 | ||||
| android_code_version=29 | ||||
| version=0.4.35 | ||||
| android_code_version=39 | ||||
|   | ||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
|   | ||||
| @@ -12,6 +12,22 @@ data class PaginationResult<T>( | ||||
|  | ||||
| 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( | ||||
|     pagination: Pagination, | ||||
|     commonObjectsNumber: Long | ||||
|   | ||||
| @@ -7,14 +7,17 @@ const val defaultMediumPageSize = 5 | ||||
| const val defaultLargePageSize = 10 | ||||
| const val defaultExtraLargePageSize = 15 | ||||
|  | ||||
| var defaultPaginationPageSize = defaultMediumPageSize | ||||
|  | ||||
| @Suppress("NOTHING_TO_INLINE", "FunctionName") | ||||
| inline fun FirstPagePagination(size: Int = defaultMediumPageSize) = | ||||
| inline fun FirstPagePagination(size: Int = defaultPaginationPageSize) = | ||||
|     SimplePagination( | ||||
|         page = 0, | ||||
|         size = size | ||||
|     ) | ||||
|  | ||||
| val emptyPagination = Pagination(0, 0) | ||||
| val firstPageWithOneElementPagination = FirstPagePagination(1) | ||||
|  | ||||
| @Suppress("NOTHING_TO_INLINE") | ||||
| inline fun Pagination.nextPage() = | ||||
|   | ||||
| @@ -21,8 +21,10 @@ inline fun PaginationResult<*>.nextPageIfNotEmpty() = if (results.isNotEmpty()) | ||||
| } | ||||
|  | ||||
| @Suppress("NOTHING_TO_INLINE") | ||||
| inline fun PaginationResult<*>.thisPageIfNotEmpty(): Pagination? = if (results.isNotEmpty()) { | ||||
| inline fun <T> PaginationResult<T>.thisPageIfNotEmpty(): PaginationResult<T>? = if (results.isNotEmpty()) { | ||||
|     this | ||||
| } else { | ||||
|     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 | ||||
| ) | ||||
| @@ -36,6 +36,6 @@ class PaginatedIterable<T>( | ||||
| @Suppress("NOTHING_TO_INLINE") | ||||
| inline fun <T> makeIterable( | ||||
|     noinline countGetter: () -> Long, | ||||
|     pageSize: Int = defaultMediumPageSize, | ||||
|     pageSize: Int = defaultPaginationPageSize, | ||||
|     noinline paginationResultGetter: Pagination.() -> PaginationResult<T> | ||||
| ): Iterable<T> = PaginatedIterable(pageSize, countGetter, paginationResultGetter) | ||||
|   | ||||
| @@ -18,6 +18,6 @@ val Pagination.asUrlQueryArrayParts | ||||
| val Map<String, String?>.extractPagination: Pagination | ||||
|     get() = SimplePagination( | ||||
|         get(paginationPageKey) ?.toIntOrNull() ?: 0, | ||||
|         get(paginationSizeKey) ?.toIntOrNull() ?: defaultMediumPageSize | ||||
|         get(paginationSizeKey) ?.toIntOrNull() ?: defaultPaginationPageSize | ||||
|     ) | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import io.ktor.http.Parameters | ||||
| val Parameters.extractPagination: Pagination | ||||
|     get() = SimplePagination( | ||||
|         get("page") ?.toIntOrNull() ?: 0, | ||||
|         get("size") ?.toIntOrNull() ?: defaultMediumPageSize | ||||
|         get("size") ?.toIntOrNull() ?: defaultPaginationPageSize | ||||
|     ) | ||||
|  | ||||
| val ApplicationCall.extractPagination: Pagination | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| {"bintrayConfig":{"repo":"MicroUtils","packageName":"${project.name}","packageVcs":"https://github.com/InsanusMokrassar/MicroUtils","autoPublish":true,"overridePublish":true},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror/src/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"It is set of projects with micro tools for avoiding of routines coding","url":"https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror","vcsUrl":"ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git","includeGpgSigning":true,"publishToMavenCentral":true,"developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}]}} | ||||
| @@ -12,11 +12,11 @@ publishing { | ||||
|         pom { | ||||
|             description = "It is set of projects with micro tools for avoiding of routines coding" | ||||
|             name = "${project.name}" | ||||
|             url = "https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror" | ||||
|             url = "https://github.com/InsanusMokrassar/MicroUtils/" | ||||
|  | ||||
|             scm { | ||||
|                 developerConnection = "scm:git:[fetch=]ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git[push=]ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git" | ||||
|                 url = "ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git" | ||||
|                 developerConnection = "scm:git:[fetch=]https://github.com/InsanusMokrassar/MicroUtils.git[push=]https://github.com/InsanusMokrassar/MicroUtils.git" | ||||
|                 url = "https://github.com/InsanusMokrassar/MicroUtils.git" | ||||
|             } | ||||
|  | ||||
|             developers { | ||||
| @@ -40,22 +40,23 @@ publishing { | ||||
|                  | ||||
|                     license { | ||||
|                         name = "Apache Software License 2.0" | ||||
|                         url = "https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror/src/master/LICENSE" | ||||
|                         url = "https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE" | ||||
|                     } | ||||
|                  | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         repositories { | ||||
|             if ((project.hasProperty('GITHUBPACKAGES_USER') || System.getenv('GITHUBPACKAGES_USER') != null) && (project.hasProperty('GITHUBPACKAGES_PASSWORD') || System.getenv('GITHUBPACKAGES_PASSWORD') != null)) { | ||||
|                 maven { | ||||
|                 name = "bintray" | ||||
|                 url = uri("https://api.bintray.com/maven/${project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')}/MicroUtils/${project.name}/;publish=1;override=1") | ||||
|                     name = "GithubPackages" | ||||
|                     url = uri("https://maven.pkg.github.com/InsanusMokrassar/MicroUtils") | ||||
|                     credentials { | ||||
|                     username = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER') | ||||
|                     password = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY') | ||||
|                         username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER') | ||||
|                         password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD') | ||||
|                     } | ||||
|                 } | ||||
|              | ||||
|             } | ||||
|             if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { | ||||
|                 maven { | ||||
|                     name = "sonatype" | ||||
|                     url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") | ||||
| @@ -64,9 +65,8 @@ publishing { | ||||
|                         password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') | ||||
|                     } | ||||
|                 } | ||||
|              | ||||
|             } | ||||
|          | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										1
									
								
								publish.kpsb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								publish.kpsb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"It is set of projects with micro tools for avoiding of routines coding","url":"https://github.com/InsanusMokrassar/MicroUtils/","vcsUrl":"https://github.com/InsanusMokrassar/MicroUtils.git","includeGpgSigning":true,"developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/MicroUtils"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}]}} | ||||
							
								
								
									
										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 | ||||
|  | ||||
| import dev.inmo.micro_utils.pagination.* | ||||
| import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging | ||||
| import kotlinx.coroutines.flow.Flow | ||||
|  | ||||
| interface ReadOneToManyKeyValueRepo<Key, Value> : Repo { | ||||
| @@ -12,14 +13,12 @@ interface ReadOneToManyKeyValueRepo<Key, Value> : Repo { | ||||
|     suspend fun count(k: Key): Long | ||||
|     suspend fun count(): Long | ||||
|  | ||||
|     suspend fun getAll(k: Key, reversed: Boolean = false): List<Value> = mutableListOf<Value>().also { list -> | ||||
|         doWithPagination { | ||||
|             get(k, it).also { | ||||
|                 list.addAll(it.results) | ||||
|             }.nextPageIfNotEmpty() | ||||
|         } | ||||
|         if (reversed) { | ||||
|             list.reverse() | ||||
|     suspend fun getAll(k: Key, reversed: Boolean = false): List<Value> { | ||||
|         val results = getAllWithNextPaging { get(k, it) } | ||||
|         return if (reversed) { | ||||
|             results.reversed() | ||||
|         } else { | ||||
|             results | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package dev.inmo.micro_utils.repos | ||||
|  | ||||
| import dev.inmo.micro_utils.pagination.* | ||||
| import dev.inmo.micro_utils.pagination.utils.doAllWithCurrentPaging | ||||
| import kotlinx.coroutines.flow.Flow | ||||
|  | ||||
| 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> { | ||||
|     override suspend fun unsetWithValues(toUnset: List<Value>) = toUnset.forEach { v -> | ||||
|         doWithPagination { | ||||
|         doAllWithCurrentPaging { | ||||
|             keys(v, it).also { | ||||
|                 unset(it.results) | ||||
|             } | ||||
|   | ||||
| @@ -1,31 +1,33 @@ | ||||
| package dev.inmo.micro_utils.repos.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 | ||||
|  | ||||
| @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( | ||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||
|     methodCaller: suspend REPO.(Pagination) -> PaginationResult<T>, | ||||
|     block: (List<T>) -> Unit | ||||
|     crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<T>, | ||||
|     crossinline block: (List<T>) -> Unit | ||||
| ) { | ||||
|     doWithPagination { | ||||
|     doForAllWithNextPaging { | ||||
|         methodCaller(it).also { | ||||
|             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( | ||||
|     block: (List<T>) -> Unit | ||||
| ) = doForAll({ getByPagination(it) }, block) | ||||
|     crossinline block: (List<T>) -> Unit | ||||
| ) = doForAllWithNextPaging { | ||||
|     getByPagination(it).also { block(it.results) } | ||||
| } | ||||
|  | ||||
| suspend inline fun <T, ID, REPO : ReadStandardCRUDRepo<T, ID>> REPO.getAll( | ||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||
|     methodCaller: suspend REPO.(Pagination) -> PaginationResult<T> | ||||
| ): List<T> { | ||||
|     val resultList = mutableListOf<T>() | ||||
|     doForAll(methodCaller) { | ||||
|         resultList.addAll(it) | ||||
|     } | ||||
|     return resultList | ||||
|     crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<T> | ||||
| ): List<T> = getAllWithNextPaging { | ||||
|     methodCaller(this, it) | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,10 @@ | ||||
| package dev.inmo.micro_utils.repos.pagination | ||||
|  | ||||
| import dev.inmo.micro_utils.pagination.* | ||||
| import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging | ||||
| 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( | ||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||
|     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( | ||||
|     block: (List<Pair<Key, Value>>) -> Unit | ||||
| ) = doForAll({ keys(it, false) }, block) | ||||
|  | ||||
| suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.getAll( | ||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||
|     methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key> | ||||
| ): List<Pair<Key, Value>> { | ||||
|     val resultList = mutableListOf<Pair<Key, Value>>() | ||||
|     doForAll(methodCaller) { | ||||
|         resultList.addAll(it) | ||||
|     } | ||||
|     return resultList | ||||
|     crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key> | ||||
| ): List<Pair<Key, Value>> = getAllWithNextPaging { | ||||
|     val result = methodCaller(it) | ||||
|     result.changeResultsUnchecked( | ||||
|         result.results.mapNotNull { it to (get(it) ?: return@mapNotNull null) } | ||||
|     ) | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,10 @@ | ||||
| package dev.inmo.micro_utils.repos.pagination | ||||
|  | ||||
| import dev.inmo.micro_utils.pagination.* | ||||
| import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging | ||||
| 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( | ||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||
|     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( | ||||
|     block: (List<Pair<Key, List<Value>>>) -> Unit | ||||
| ) = doForAll({ keys(it, false) }, block) | ||||
|  | ||||
| suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.getAll( | ||||
|     @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") | ||||
|     methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key> | ||||
| ): List<Pair<Key, List<Value>>> { | ||||
|     val resultList = mutableListOf<Pair<Key, List<Value>>>() | ||||
|     doForAll(methodCaller) { | ||||
|         resultList.addAll(it) | ||||
|     crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key> | ||||
| ): List<Pair<Key, List<Value>>> = getAllWithNextPaging { | ||||
|     val keysResult = methodCaller(it) | ||||
|     keysResult.changeResultsUnchecked( | ||||
|         keysResult.results.map { k -> | ||||
|             k to getAll(k) | ||||
|         } | ||||
|     return resultList | ||||
|     ) | ||||
| } | ||||
|   | ||||
| @@ -26,20 +26,39 @@ fun SQLiteDatabase.createTable( | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun Cursor.getString(columnName: String) = getString( | ||||
|     getColumnIndex(columnName) | ||||
| /** | ||||
|  * @throws IllegalArgumentException | ||||
|  */ | ||||
| fun Cursor.getString(columnName: String): String = getString( | ||||
|     getColumnIndexOrThrow(columnName) | ||||
| ) | ||||
|  | ||||
| fun Cursor.getLong(columnName: String) = getLong( | ||||
|     getColumnIndex(columnName) | ||||
| /** | ||||
|  * @throws IllegalArgumentException | ||||
|  */ | ||||
| fun Cursor.getShort(columnName: String): Short = getShort( | ||||
|     getColumnIndexOrThrow(columnName) | ||||
| ) | ||||
|  | ||||
| fun Cursor.getInt(columnName: String) = getInt( | ||||
|     getColumnIndex(columnName) | ||||
| /** | ||||
|  * @throws IllegalArgumentException | ||||
|  */ | ||||
| fun Cursor.getLong(columnName: String): Long = getLong( | ||||
|     getColumnIndexOrThrow(columnName) | ||||
| ) | ||||
|  | ||||
| fun Cursor.getDouble(columnName: String) = getDouble( | ||||
|     getColumnIndex(columnName) | ||||
| /** | ||||
|  * @throws IllegalArgumentException | ||||
|  */ | ||||
| fun Cursor.getInt(columnName: String): Int = getInt( | ||||
|     getColumnIndexOrThrow(columnName) | ||||
| ) | ||||
|  | ||||
| /** | ||||
|  * @throws IllegalArgumentException | ||||
|  */ | ||||
| fun Cursor.getDouble(columnName: String): Double = getDouble( | ||||
|     getColumnIndexOrThrow(columnName) | ||||
| ) | ||||
|  | ||||
| fun SQLiteDatabase.select( | ||||
|   | ||||
| @@ -32,7 +32,9 @@ private object ContextsPool { | ||||
|  | ||||
|     suspend fun <T> use(block: suspend (CoroutineContext) -> T): T = acquireContext().let { | ||||
|         try { | ||||
|             safely { | ||||
|                 block(it) | ||||
|             } | ||||
|         } finally { | ||||
|             freeContext(it) | ||||
|         } | ||||
| @@ -48,11 +50,12 @@ class TransactionContext( | ||||
| } | ||||
|  | ||||
| suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T): T { | ||||
|     return coroutineContext[TransactionContext] ?.let { | ||||
|         withContext(it.databaseContext) { | ||||
|     coroutineContext[TransactionContext] ?.let { | ||||
|         return withContext(it.databaseContext) { | ||||
|             block() | ||||
|         } | ||||
|     } ?: ContextsPool.use { context -> | ||||
|     } | ||||
|     return ContextsPool.use { context -> | ||||
|         withContext(TransactionContext(context) + context) { | ||||
|             beginTransaction() | ||||
|             safely( | ||||
| @@ -70,18 +73,18 @@ suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline fun <T> SQLiteDatabase.inlineTransaction(block: SQLiteDatabase.() -> T): T { | ||||
| inline fun <T> SQLiteDatabase.inlineTransaction(crossinline block: SQLiteDatabase.() -> T): T { | ||||
|     return when { | ||||
|         inTransaction() -> block() | ||||
|         else -> { | ||||
|             beginTransaction() | ||||
|             try { | ||||
|                 block().also { | ||||
|                     setTransactionSuccessful() | ||||
|                 } | ||||
|                 block().also { setTransactionSuccessful() } | ||||
|             } finally { | ||||
|                 endTransaction() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun <T> SQLiteDatabase.blockingTransaction(block: SQLiteDatabase.() -> T): T = inlineTransaction(block) | ||||
|   | ||||
| @@ -31,6 +31,20 @@ class StandardSQLHelper( | ||||
|     suspend fun <T> readableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.readableTransaction(block) | ||||
| } | ||||
|  | ||||
| fun <T> SQLiteOpenHelper.blockingWritableTransaction(block: SQLiteDatabase.() -> T): T { | ||||
|     return writableDatabase.blockingTransaction(block) | ||||
| } | ||||
| fun <T> SQLiteOpenHelper.blockingReadableTransaction(block: SQLiteDatabase.() -> T): T { | ||||
|     return readableDatabase.blockingTransaction(block) | ||||
| } | ||||
|  | ||||
| fun <T> StandardSQLHelper.blockingWritableTransaction(block: SQLiteDatabase.() -> T): T { | ||||
|     return sqlOpenHelper.blockingWritableTransaction(block) | ||||
| } | ||||
| fun <T> StandardSQLHelper.blockingReadableTransaction(block: SQLiteDatabase.() -> T): T { | ||||
|     return sqlOpenHelper.blockingReadableTransaction(block) | ||||
| } | ||||
|  | ||||
| suspend fun <T> SQLiteOpenHelper.writableTransaction(block: suspend SQLiteDatabase.() -> T): T { | ||||
|     return writableDatabase.transaction(block) | ||||
| } | ||||
|   | ||||
| @@ -20,7 +20,7 @@ abstract class AbstractAndroidCRUDRepo<ObjectType, IdType>( | ||||
|         it.count | ||||
|     }.toLong() | ||||
|  | ||||
|     override suspend fun contains(id: IdType): Boolean = helper.readableTransaction { | ||||
|     override suspend fun contains(id: IdType): Boolean = helper.blockingReadableTransaction { | ||||
|         select( | ||||
|             tableName, | ||||
|             null, | ||||
|   | ||||
| @@ -8,7 +8,8 @@ import kotlinx.coroutines.flow.* | ||||
| abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType>( | ||||
|     helper: StandardSQLHelper | ||||
| ) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>, | ||||
|     AbstractAndroidCRUDRepo<ObjectType, IdType>(helper) { | ||||
|     AbstractAndroidCRUDRepo<ObjectType, IdType>(helper), | ||||
|     StandardCRUDRepo<ObjectType, IdType, InputValueType> { | ||||
|     protected val newObjectsChannel = MutableSharedFlow<ObjectType>(64) | ||||
|     protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(64) | ||||
|     protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(64) | ||||
| @@ -19,9 +20,10 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType | ||||
|     protected abstract suspend fun InputValueType.asContentValues(id: IdType? = null): ContentValues | ||||
|  | ||||
|     override suspend fun create(values: List<InputValueType>): List<ObjectType> { | ||||
|         val indexes = helper.writableTransaction { | ||||
|             values.map { | ||||
|                 insert(tableName, null, it.asContentValues()) | ||||
|         val valuesContentValues = values.map { it.asContentValues() } | ||||
|         val indexes = helper.blockingWritableTransaction { | ||||
|             valuesContentValues.map { | ||||
|                 insert(tableName, null, it) | ||||
|             } | ||||
|         } | ||||
|         return helper.readableTransaction { | ||||
| @@ -47,7 +49,7 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType | ||||
|  | ||||
|     override suspend fun deleteById(ids: List<IdType>) { | ||||
|         val deleted = mutableListOf<IdType>() | ||||
|         helper.writableTransaction { | ||||
|         helper.blockingWritableTransaction { | ||||
|             ids.forEach { id -> | ||||
|                 delete(tableName, "$idColumnName=?", arrayOf(id.asId)).also { | ||||
|                     if (it > 0) { | ||||
| @@ -64,7 +66,7 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType | ||||
|     override suspend fun update(id: IdType, value: InputValueType): ObjectType? { | ||||
|         val asContentValues = value.asContentValues(id) | ||||
|         if (asContentValues.keySet().isNotEmpty()) { | ||||
|             helper.writableTransaction { | ||||
|             helper.blockingWritableTransaction { | ||||
|                 update( | ||||
|                     tableName, | ||||
|                     asContentValues, | ||||
| @@ -79,11 +81,12 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType | ||||
|     } | ||||
|  | ||||
|     override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> { | ||||
|         val contentValues = values.map { (id, value) -> id to value.asContentValues(id) } | ||||
|         helper.writableTransaction { | ||||
|             values.forEach { (id, value) -> | ||||
|             contentValues.forEach { (id, contentValues) -> | ||||
|                 update( | ||||
|                     tableName, | ||||
|                     value.asContentValues(id), | ||||
|                     contentValues, | ||||
|                     "$idColumnName=?", | ||||
|                     arrayOf(id.asId) | ||||
|                 ) | ||||
| @@ -98,5 +101,5 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override suspend fun count(): Long = helper.readableTransaction { select(tableName).use { it.count.toLong() } } | ||||
|     override suspend fun count(): Long = helper.blockingReadableTransaction { select(tableName).use { it.count.toLong() } } | ||||
| } | ||||
| @@ -42,8 +42,7 @@ class OneToManyAndroidRepo<Key, Value>( | ||||
|     private fun String.asKey(): Key = internalSerialFormat.decodeFromString(keySerializer, this) | ||||
|  | ||||
|     init { | ||||
|         runBlocking(DatabaseCoroutineContext) { | ||||
|             helper.writableTransaction { | ||||
|         helper.blockingWritableTransaction { | ||||
|             createTable( | ||||
|                 tableName, | ||||
|                 internalId to internalIdType, | ||||
| @@ -52,11 +51,10 @@ class OneToManyAndroidRepo<Key, Value>( | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     override suspend fun add(toAdd: Map<Key, List<Value>>) { | ||||
|         val added = mutableListOf<Pair<Key, Value>>() | ||||
|         helper.writableTransaction { | ||||
|         helper.blockingWritableTransaction { | ||||
|             toAdd.forEach { (k, values) -> | ||||
|                 values.forEach { v -> | ||||
|                     insert( | ||||
| @@ -78,7 +76,7 @@ class OneToManyAndroidRepo<Key, Value>( | ||||
|     } | ||||
|  | ||||
|     override suspend fun clear(k: Key) { | ||||
|         helper.writableTransaction { | ||||
|         helper.blockingWritableTransaction { | ||||
|             delete(tableName, "$idColumnName=?", arrayOf(k.asId())) | ||||
|         }.also { | ||||
|             if (it > 0) { | ||||
| @@ -88,7 +86,7 @@ class OneToManyAndroidRepo<Key, Value>( | ||||
|     } | ||||
|  | ||||
|     override suspend fun set(toSet: Map<Key, List<Value>>) { | ||||
|         val (clearedKeys, inserted) = helper.writableTransaction { | ||||
|         val (clearedKeys, inserted) = helper.blockingWritableTransaction { | ||||
|             toSet.mapNotNull { (k, _) -> | ||||
|                 if (delete(tableName, "$idColumnName=?", arrayOf(k.asId())) > 0) { | ||||
|                     k | ||||
| @@ -110,13 +108,13 @@ class OneToManyAndroidRepo<Key, Value>( | ||||
|         inserted.forEach { newPair -> _onNewValue.emit(newPair) } | ||||
|     } | ||||
|  | ||||
|     override suspend fun contains(k: Key): Boolean = helper.readableTransaction { | ||||
|     override suspend fun contains(k: Key): Boolean = helper.blockingReadableTransaction { | ||||
|         select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.asId()), limit = FirstPagePagination(1).limitClause()).use { | ||||
|             it.count > 0 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override suspend fun contains(k: Key, v: Value): Boolean = helper.readableTransaction { | ||||
|     override suspend fun contains(k: Key, v: Value): Boolean = helper.blockingReadableTransaction { | ||||
|         select( | ||||
|             tableName, | ||||
|             selection = "$idColumnName=? AND $valueColumnName=?", | ||||
| @@ -127,7 +125,7 @@ class OneToManyAndroidRepo<Key, Value>( | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override suspend fun count(): Long =helper.readableTransaction { | ||||
|     override suspend fun count(): Long =helper.blockingReadableTransaction { | ||||
|         select( | ||||
|             tableName | ||||
|         ).use { | ||||
| @@ -135,7 +133,7 @@ class OneToManyAndroidRepo<Key, Value>( | ||||
|         } | ||||
|     }.toLong() | ||||
|  | ||||
|     override suspend fun count(k: Key): Long = helper.readableTransaction { | ||||
|     override suspend fun count(k: Key): Long = helper.blockingReadableTransaction { | ||||
|         select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.asId()), limit = FirstPagePagination(1).limitClause()).use { | ||||
|             it.count | ||||
|         } | ||||
| @@ -147,7 +145,7 @@ class OneToManyAndroidRepo<Key, Value>( | ||||
|         reversed: Boolean | ||||
|     ): PaginationResult<Value> = count(k).let { count -> | ||||
|         val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination } | ||||
|         helper.readableTransaction { | ||||
|         helper.blockingReadableTransaction { | ||||
|             select( | ||||
|                 tableName, | ||||
|                 selection = "$idColumnName=?", | ||||
| @@ -173,7 +171,7 @@ class OneToManyAndroidRepo<Key, Value>( | ||||
|         reversed: Boolean | ||||
|     ): PaginationResult<Key> = count().let { count -> | ||||
|         val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination } | ||||
|         helper.readableTransaction { | ||||
|         helper.blockingReadableTransaction { | ||||
|             select( | ||||
|                 tableName, | ||||
|                 limit = resultPagination.limitClause() | ||||
| @@ -198,7 +196,7 @@ class OneToManyAndroidRepo<Key, Value>( | ||||
|         reversed: Boolean | ||||
|     ): PaginationResult<Key> = count().let { count -> | ||||
|         val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination } | ||||
|         helper.readableTransaction { | ||||
|         helper.blockingReadableTransaction { | ||||
|             select( | ||||
|                 tableName, | ||||
|                 selection = "$valueColumnName=?", | ||||
| @@ -220,7 +218,7 @@ class OneToManyAndroidRepo<Key, Value>( | ||||
|     } | ||||
|  | ||||
|     override suspend fun remove(toRemove: Map<Key, List<Value>>) { | ||||
|         helper.writableTransaction { | ||||
|         helper.blockingWritableTransaction { | ||||
|             toRemove.flatMap { (k, vs) -> | ||||
|                 vs.mapNotNullA { v -> | ||||
|                     if (delete(tableName, "$idColumnName=? AND $valueColumnName=?", arrayOf(k.asId(), v.asValue())) > 0) { | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| package dev.inmo.micro_utils.repos.exposed | ||||
|  | ||||
| import dev.inmo.micro_utils.repos.StandardCRUDRepo | ||||
|  | ||||
| abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>( | ||||
|     flowsChannelsSize: Int = 0, | ||||
|     tableName: String = "" | ||||
| @@ -8,4 +10,5 @@ abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>( | ||||
|         flowsChannelsSize, | ||||
|         tableName | ||||
|     ), | ||||
|     ExposedCRUDRepo<ObjectType, IdType> | ||||
|     ExposedCRUDRepo<ObjectType, IdType>, | ||||
|     StandardCRUDRepo<ObjectType, IdType, InputValueType> | ||||
|   | ||||
| @@ -60,9 +60,11 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>( | ||||
|             } | ||||
|         }.let { | ||||
|             if (it > 0) { | ||||
|                 transaction(db = database) { | ||||
|                     select { | ||||
|                         selectById(this, id) | ||||
|                     }.limit(1).firstOrNull() ?.asObject | ||||
|                 } | ||||
|             } else { | ||||
|                 null | ||||
|             } | ||||
|   | ||||
							
								
								
									
										7
									
								
								serialization/typed_serializer/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								serialization/typed_serializer/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| plugins { | ||||
|     id "org.jetbrains.kotlin.multiplatform" | ||||
|     id "org.jetbrains.kotlin.plugin.serialization" | ||||
|     id "com.android.library" | ||||
| } | ||||
|  | ||||
| apply from: "$mppProjectWithSerializationPresetPath" | ||||
| @@ -0,0 +1,74 @@ | ||||
| package dev.inmo.micro_utils.serialization.typed_serializer | ||||
|  | ||||
| import kotlinx.serialization.* | ||||
| import kotlinx.serialization.builtins.serializer | ||||
| import kotlinx.serialization.descriptors.* | ||||
| import kotlinx.serialization.encoding.* | ||||
| import kotlin.reflect.KClass | ||||
|  | ||||
| open class TypedSerializer<T : Any>( | ||||
|     kClass: KClass<T>, | ||||
|     presetSerializers: Map<String, KSerializer<out T>> = emptyMap() | ||||
| ) : KSerializer<T> { | ||||
|     protected val serializers = presetSerializers.toMutableMap() | ||||
|     @InternalSerializationApi | ||||
|     override open val descriptor: SerialDescriptor = buildSerialDescriptor( | ||||
|         "TextSourceSerializer", | ||||
|         SerialKind.CONTEXTUAL | ||||
|     ) { | ||||
|         element("type", String.serializer().descriptor) | ||||
|         element("value", ContextualSerializer(kClass).descriptor) | ||||
|     } | ||||
|  | ||||
|     @InternalSerializationApi | ||||
|     override open fun deserialize(decoder: Decoder): T { | ||||
|         return decoder.decodeStructure(descriptor) { | ||||
|             var type: String? = null | ||||
|             lateinit var result: T | ||||
|             while (true) { | ||||
|                 when (val index = decodeElementIndex(descriptor)) { | ||||
|                     0 -> type = decodeStringElement(descriptor, 0) | ||||
|                     1 -> { | ||||
|                         require(type != null) { "Type is null, but it is expected that was inited already" } | ||||
|                         result = decodeSerializableElement( | ||||
|                             descriptor, | ||||
|                             1, | ||||
|                             serializers.getValue(type) | ||||
|                         ) | ||||
|                     } | ||||
|                     CompositeDecoder.DECODE_DONE -> break | ||||
|                     else -> error("Unexpected index: $index") | ||||
|                 } | ||||
|             } | ||||
|             result | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @InternalSerializationApi | ||||
|     protected open fun <O: T> CompositeEncoder.encode(value: O) { | ||||
|         encodeSerializableElement(descriptor, 1, value::class.serializer() as KSerializer<O>, value) | ||||
|     } | ||||
|  | ||||
|     @InternalSerializationApi | ||||
|     override open fun serialize(encoder: Encoder, value: T) { | ||||
|         encoder.encodeStructure(descriptor) { | ||||
|             val valueSerializer = value::class.serializer() | ||||
|             val type = serializers.keys.first { serializers[it] == valueSerializer } | ||||
|             encodeStringElement(descriptor, 0, type) | ||||
|             encode(value) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     open fun <O: T> include(type: String, serializer: KSerializer<O>) { | ||||
|         serializers[type] = serializer | ||||
|     } | ||||
|  | ||||
|     open fun exclude(type: String) { | ||||
|         serializers.remove(type) | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline fun <reified T : Any> TypedSerializer( | ||||
|     presetSerializers: Map<String, KSerializer<out T>> = emptyMap() | ||||
| ) = TypedSerializer(T::class, presetSerializers) | ||||
| @@ -0,0 +1,40 @@ | ||||
| package dev.inmo.micro_utils.serialization.typed_serializer | ||||
|  | ||||
| import kotlinx.serialization.Serializable | ||||
| import kotlinx.serialization.builtins.ListSerializer | ||||
| import kotlinx.serialization.json.Json | ||||
| import kotlin.random.Random | ||||
| import kotlin.test.Test | ||||
| import kotlin.test.assertEquals | ||||
|  | ||||
| class TypedSerializerTests { | ||||
|     interface Example { | ||||
|         val number: Number | ||||
|     } | ||||
|     val serialFormat = Json {  } | ||||
|  | ||||
|     @Serializable | ||||
|     data class Example1(override val number: Long) : Example | ||||
|  | ||||
|     @Serializable | ||||
|     data class Example2(override val number: Double) : Example | ||||
|  | ||||
|     @Test | ||||
|     fun testThatSerializerWorksCorrectly() { | ||||
|         val serializer = TypedSerializer( | ||||
|             mapOf( | ||||
|                 "long" to Example1.serializer(), | ||||
|                 "double" to Example2.serializer() | ||||
|             ) | ||||
|         ) | ||||
|  | ||||
|         val value1 = Example1(Random.nextLong()) | ||||
|         val value2 = Example2(Random.nextDouble()) | ||||
|  | ||||
|         val list = listOf(value1, value2) | ||||
|         val serialized = serialFormat.encodeToString(ListSerializer(serializer), list) | ||||
|         val deserialized = serialFormat.decodeFromString(ListSerializer(serializer), serialized) | ||||
|  | ||||
|         assertEquals(list, deserialized) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1 @@ | ||||
| <manifest package="dev.inmo.micro_utils.serialization.typed_serializer"/> | ||||
| @@ -11,6 +11,7 @@ String[] includes = [ | ||||
|     ":pagination:ktor:server", | ||||
|     ":mime_types", | ||||
|     ":repos:common", | ||||
|     ":repos:cache", | ||||
|     ":repos:exposed", | ||||
|     ":repos:inmemory", | ||||
|     ":repos:ktor:client", | ||||
| @@ -25,6 +26,7 @@ String[] includes = [ | ||||
|     ":android:alerts:recyclerview", | ||||
|     ":serialization:base64", | ||||
|     ":serialization:encapsulator", | ||||
|     ":serialization:typed_serializer", | ||||
|  | ||||
|     ":dokka" | ||||
| ] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user