mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-11-04 06:00:22 +00:00 
			
		
		
		
	Compare commits
	
		
			86 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 827cf32c1b | |||
| 98ad6dbeb2 | |||
| 63c8f642ec | |||
| 3bfe64f797 | |||
| ec98029467 | |||
| ab58478686 | |||
| 90247667d1 | |||
| e661185534 | |||
| d73e4e8e1f | |||
| a6905e73cb | |||
| 93b054d55e | |||
| 5db4c5c717 | |||
| 5e04521929 | |||
| 30440b4ed1 | |||
| 09bb90604d | |||
| 4d55ec6f36 | |||
| f373524f34 | |||
| 0398a7bebd | |||
| fa8a5bcd97 | |||
| 758a92410b | |||
| 77f56c5dda | |||
| 72f2fe3cc3 | |||
| 7e0e520f03 | |||
| 92c4784e42 | |||
| a634229dc0 | |||
| de3f36ef2a | |||
| c623a265ee | |||
| e75125f6df | |||
| 4901a8844c | |||
| a1854b68d8 | |||
| aee4a6243b | |||
| 4c0cb73d69 | |||
| c6eab182f6 | |||
| 3e3fbd97eb | |||
| feb695caa7 | |||
| 1bd46d9651 | |||
| 32eabb6b36 | |||
| 9bfe6dc6d8 | |||
| 3f366aeea4 | |||
| 4338fd46f2 | |||
| 36974f5b49 | |||
| d48f767408 | |||
| bd2558e852 | |||
| 9f7c963cd5 | |||
| 2db0eadbfe | |||
| 580c7b8842 | |||
| 88fba347ea | |||
| c8c5255e62 | |||
| 01a1a2760a | |||
| 6d34df8d48 | |||
| b124ea65e1 | |||
| f3214347a9 | |||
| 6b8724b59f | |||
| ac3e378edf | |||
| 5d31fd1c91 | |||
| 046fe1ec08 | |||
| 721873c843 | |||
| f6ffbfc10a | |||
| 4e91649e0a | |||
| 4f7f8abec4 | |||
| 6370562dbc | |||
| 94e2e67522 | |||
| 1d8330015d | |||
| e5017b0258 | |||
| cd412ca31b | |||
| d2b6473095 | |||
| ab42507275 | |||
| dcef844e20 | |||
| 3244708c1b | |||
| 73ac1f1741 | |||
| f104e9f352 | |||
| 393c9a7d06 | |||
| ea497ea488 | |||
| 
						 | 
					5add89cad0 | ||
| 2db447d2ef | |||
| 31c83813e6 | |||
| 27483a282d | |||
| 48b816aa22 | |||
| 0065f94f52 | |||
| ccc0002eb2 | |||
| 15a2eee141 | |||
| b9faac71e5 | |||
| 0a4465de33 | |||
| f9dfd09628 | |||
| 8638d7afce | |||
| 8311793a43 | 
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -14,3 +14,8 @@ local.properties
 | 
				
			|||||||
kotlin-js-store
 | 
					kotlin-js-store
 | 
				
			||||||
 | 
					
 | 
				
			||||||
publishing.sh
 | 
					publishing.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local.*
 | 
				
			||||||
 | 
					local/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.kotlin/
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										143
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,5 +1,148 @@
 | 
				
			|||||||
# Changelog
 | 
					# Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.21.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `KSP`:
 | 
				
			||||||
 | 
					    * Module has been initialized
 | 
				
			||||||
 | 
					    * `Generator`:
 | 
				
			||||||
 | 
					        * Module has been initialized
 | 
				
			||||||
 | 
					    * `Sealed`:
 | 
				
			||||||
 | 
					        * Module has been initialized
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.21.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**THIS UPDATE CONTAINS BREAKING CHANGES IN `safely*`-ORIENTED FUNCTIONS**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Coroutines`:
 | 
				
			||||||
 | 
					    * **All `safely` functions lost their `supervisorScope` in favor to wrapping `runCatching`**
 | 
				
			||||||
 | 
					        * `runCatchingSafely` is the main handling function of all `safely` functions
 | 
				
			||||||
 | 
					        * `launchSafely*` and `asyncSafely*` blocks lost `CoroutineScope` as their receiver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.52
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Coroutines`:
 | 
				
			||||||
 | 
					    * Small rework of weak jobs: add `WeakScope` factory, rename old weal extensions and add kdocs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.51
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Android Fragment`: `1.7.0` -> `1.7.1`
 | 
				
			||||||
 | 
					* `Pagination`:
 | 
				
			||||||
 | 
					    * Add `Pagination.nextPageIfTrue` and `Pagination.thisPageIftrue` extensions to get the page according to condition
 | 
				
			||||||
 | 
					    pass status
 | 
				
			||||||
 | 
					    * Add `PaginationResult.nextPageIfNotEmptyOrLastPage` and `PaginationResult.thisPageIfNotEmptyOrLastPage`
 | 
				
			||||||
 | 
					    * Change all `doForAll` and `getAll` extensions fo pagination to work basing on `nextPageIfNotEmptyOrLastPage` and
 | 
				
			||||||
 | 
					    `thisPageIfNotEmptyOrLastPage`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.50
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Coroutines`: `1.8.0` -> `1.8.1`
 | 
				
			||||||
 | 
					    * `KSLog`: `1.3.3` -> `1.3.4`
 | 
				
			||||||
 | 
					    * `Exposed`: `0.50.0` -> `0.50.1`
 | 
				
			||||||
 | 
					    * `Ktor`: `2.3.10` -> `2.3.11`
 | 
				
			||||||
 | 
					* A lot of inline functions became common functions due to inline with only noinline callbacks in arguments leads to
 | 
				
			||||||
 | 
					low performance
 | 
				
			||||||
 | 
					* `Coroutines`:
 | 
				
			||||||
 | 
					    * `SmartMutex`, `SmartSemaphore` and `SmartRWLocker` as their user changed their state flow to `SpecialMutableStateFlow`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.49
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Repos`:
 | 
				
			||||||
 | 
					    * `Common`:
 | 
				
			||||||
 | 
					        * All `Repo`s get `diff` extensions
 | 
				
			||||||
 | 
					        * `KeyValueRepo` and `KeyValuesRepo` get `applyDiff` extension
 | 
				
			||||||
 | 
					        * Add new extensions `on*` flows for crud repos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.48
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Android Core KTX`: `1.13.0` -> `1.13.1`
 | 
				
			||||||
 | 
					    * `AndroidX Fragment`: `1.6.2` -> `1.7.0`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.47
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Exposed`: `0.49.0` -> `0.50.0`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.46
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Common`:
 | 
				
			||||||
 | 
					    * Now this repo depends on `klock`
 | 
				
			||||||
 | 
					    * Add new object-serializer `DateTimeSerializer` for `klock` serializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.45
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Android Core KTX`: `1.12.0` -> `1.13.0`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.44
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Compose`: `1.6.1` -> `1.6.2`
 | 
				
			||||||
 | 
					    * `Koin`: `3.5.4` -> `3.5.6`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.43
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Ktor`: `2.3.9` -> `2.3.10`
 | 
				
			||||||
 | 
					    * `Koin`: `3.5.3` -> `3.5.4`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.42
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Repos`:
 | 
				
			||||||
 | 
					    * `Generator`:
 | 
				
			||||||
 | 
					        * Improvements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.41
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Repos`:
 | 
				
			||||||
 | 
					    * `Exposed`:
 | 
				
			||||||
 | 
					        * `AbstractExposedKeyValueRepo`, `ExposedKeyValueRepo`, `AbstractExposedKeyValuesRepo`, `ExposedKeyValuesRepo` got opportunity to setup some part of their flows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.40
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `KSLog`: `1.3.2` -> `1.3.3`
 | 
				
			||||||
 | 
					    * `Exposed`: `0.48.0` -> `0.49.0`
 | 
				
			||||||
 | 
					    * `UUID`: `0.8.2` -> `0.8.4`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.39
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Kotlin`: `1.9.22` -> `1.9.23`
 | 
				
			||||||
 | 
					    * `Korlibs`: `5.3.2` -> `5.4.0`
 | 
				
			||||||
 | 
					    * `Okio`: `3.8.0` -> `3.9.0`
 | 
				
			||||||
 | 
					    * `Compose`: `1.6.0` -> `1.6.1`
 | 
				
			||||||
 | 
					    * `ComposeMaterial3`: `1.2.0` -> `1.2.1`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.38
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Ktor`: `2.3.8` -> `2.3.9`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.37
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Compose`: `1.5.12` -> `1.6.0`
 | 
				
			||||||
 | 
					    * `Exposed`: `0.47.0` -> `0.48.0`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.36
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Serialization`: `1.6.2` -> `1.6.3`
 | 
				
			||||||
 | 
					    * `Korlibs`: `5.3.1` -> `5.3.2`
 | 
				
			||||||
 | 
					* `Repos`:
 | 
				
			||||||
 | 
					    * `Cache`:
 | 
				
			||||||
 | 
					        * Improve work and functionality of `actualizeAll` and subsequent functions
 | 
				
			||||||
 | 
					        * All internal repos `invalidate`/`actualizeAll` now use common `actualizeAll` functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 0.20.35
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `Versions`:
 | 
				
			||||||
 | 
					    * `Coroutines`: `1.7.3` -> `1.8.0`
 | 
				
			||||||
 | 
					    * `Material3`: `1.1.2` -> `1.2.0`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 0.20.34
 | 
					## 0.20.34
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* `Repos`:
 | 
					* `Repos`:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,9 +7,6 @@ import androidx.compose.foundation.gestures.*
 | 
				
			|||||||
import androidx.compose.foundation.layout.*
 | 
					import androidx.compose.foundation.layout.*
 | 
				
			||||||
import androidx.compose.foundation.text.KeyboardActions
 | 
					import androidx.compose.foundation.text.KeyboardActions
 | 
				
			||||||
import androidx.compose.foundation.text.KeyboardOptions
 | 
					import androidx.compose.foundation.text.KeyboardOptions
 | 
				
			||||||
import androidx.compose.material.ContentAlpha
 | 
					 | 
				
			||||||
import androidx.compose.material.IconButton
 | 
					 | 
				
			||||||
import androidx.compose.material.ProvideTextStyle
 | 
					 | 
				
			||||||
import androidx.compose.material.icons.Icons
 | 
					import androidx.compose.material.icons.Icons
 | 
				
			||||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
 | 
					import androidx.compose.material.icons.filled.KeyboardArrowDown
 | 
				
			||||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
 | 
					import androidx.compose.material.icons.filled.KeyboardArrowUp
 | 
				
			||||||
@@ -75,7 +72,7 @@ fun NumberPicker(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    val coercedAnimatedOffset = animatedOffset.value % halvedNumbersColumnHeightPx
 | 
					    val coercedAnimatedOffset = animatedOffset.value % halvedNumbersColumnHeightPx
 | 
				
			||||||
    val animatedStateValue = animatedStateValue(animatedOffset.value)
 | 
					    val animatedStateValue = animatedStateValue(animatedOffset.value)
 | 
				
			||||||
    val disabledArrowsColor = arrowsColor.copy(alpha = ContentAlpha.disabled)
 | 
					    val disabledArrowsColor = arrowsColor.copy(alpha = 0f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val inputFieldShown = if (allowUseManualInput) {
 | 
					    val inputFieldShown = if (allowUseManualInput) {
 | 
				
			||||||
        remember { mutableStateOf(false) }
 | 
					        remember { mutableStateOf(false) }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ import androidx.compose.animation.core.exponentialDecay
 | 
				
			|||||||
import androidx.compose.foundation.gestures.*
 | 
					import androidx.compose.foundation.gestures.*
 | 
				
			||||||
import androidx.compose.foundation.layout.*
 | 
					import androidx.compose.foundation.layout.*
 | 
				
			||||||
import androidx.compose.foundation.rememberScrollState
 | 
					import androidx.compose.foundation.rememberScrollState
 | 
				
			||||||
import androidx.compose.material.ContentAlpha
 | 
					 | 
				
			||||||
import androidx.compose.material.icons.Icons
 | 
					import androidx.compose.material.icons.Icons
 | 
				
			||||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
 | 
					import androidx.compose.material.icons.filled.KeyboardArrowDown
 | 
				
			||||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
 | 
					import androidx.compose.material.icons.filled.KeyboardArrowUp
 | 
				
			||||||
@@ -62,9 +61,7 @@ fun <T> SetPicker(
 | 
				
			|||||||
        (index - ceil(animatedOffset.value / halvedNumbersColumnHeightPx).toInt())
 | 
					        (index - ceil(animatedOffset.value / halvedNumbersColumnHeightPx).toInt())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    val coercedAnimatedOffset = animatedOffset.value % halvedNumbersColumnHeightPx
 | 
					    val coercedAnimatedOffset = animatedOffset.value % halvedNumbersColumnHeightPx
 | 
				
			||||||
    val boxOffset = (indexAnimatedOffset * halvedNumbersColumnHeightPx) - coercedAnimatedOffset
 | 
					    val disabledArrowsColor = arrowsColor.copy(alpha = 0f)
 | 
				
			||||||
    val disabledArrowsColor = arrowsColor.copy(alpha = ContentAlpha.disabled)
 | 
					 | 
				
			||||||
    val scrollState = rememberScrollState()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Column(
 | 
					    Column(
 | 
				
			||||||
        modifier = modifier
 | 
					        modifier = modifier
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,11 @@ apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
kotlin {
 | 
					kotlin {
 | 
				
			||||||
    sourceSets {
 | 
					    sourceSets {
 | 
				
			||||||
 | 
					        commonMain {
 | 
				
			||||||
 | 
					            dependencies {
 | 
				
			||||||
 | 
					                api libs.klock
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        jvmMain {
 | 
					        jvmMain {
 | 
				
			||||||
            dependencies {
 | 
					            dependencies {
 | 
				
			||||||
                api project(":micro_utils.coroutines")
 | 
					                api project(":micro_utils.coroutines")
 | 
				
			||||||
@@ -15,23 +20,11 @@ kotlin {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        androidMain {
 | 
					        androidMain {
 | 
				
			||||||
            dependencies {
 | 
					            dependencies {
 | 
				
			||||||
                api project(":micro_utils.coroutines")
 | 
					 | 
				
			||||||
                api libs.android.fragment
 | 
					                api libs.android.fragment
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            dependsOn jvmMain
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        linuxX64Main {
 | 
					        nativeMain {
 | 
				
			||||||
            dependencies {
 | 
					 | 
				
			||||||
                api libs.okio
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        mingwX64Main {
 | 
					 | 
				
			||||||
            dependencies {
 | 
					 | 
				
			||||||
                api libs.okio
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        linuxArm64Main {
 | 
					 | 
				
			||||||
            dependencies {
 | 
					            dependencies {
 | 
				
			||||||
                api libs.okio
 | 
					                api libs.okio
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
<manifest/>
 | 
					 | 
				
			||||||
@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					package dev.inmo.micro_utils.common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import korlibs.time.DateTime
 | 
				
			||||||
 | 
					import kotlinx.serialization.KSerializer
 | 
				
			||||||
 | 
					import kotlinx.serialization.Serializer
 | 
				
			||||||
 | 
					import kotlinx.serialization.builtins.serializer
 | 
				
			||||||
 | 
					import kotlinx.serialization.descriptors.SerialDescriptor
 | 
				
			||||||
 | 
					import kotlinx.serialization.encoding.Decoder
 | 
				
			||||||
 | 
					import kotlinx.serialization.encoding.Encoder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Serializes [DateTime] as its raw [DateTime.unixMillis] and deserializes in the same way
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					object DateTimeSerializer : KSerializer<DateTime> {
 | 
				
			||||||
 | 
					    override val descriptor: SerialDescriptor
 | 
				
			||||||
 | 
					        get() = Double.serializer().descriptor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun deserialize(decoder: Decoder): DateTime {
 | 
				
			||||||
 | 
					        return DateTime(decoder.decodeDouble())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun serialize(encoder: Encoder, value: DateTime) {
 | 
				
			||||||
 | 
					        encoder.encodeDouble(value.unixMillis)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,36 +0,0 @@
 | 
				
			|||||||
package dev.inmo.micro_utils.common
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import okio.FileSystem
 | 
					 | 
				
			||||||
import okio.Path
 | 
					 | 
				
			||||||
import okio.use
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
actual typealias MPPFile = Path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @suppress
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
actual val MPPFile.filename: FileName
 | 
					 | 
				
			||||||
    get() = FileName(toString())
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @suppress
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
actual val MPPFile.filesize: Long
 | 
					 | 
				
			||||||
    get() = FileSystem.SYSTEM.openReadOnly(this).use {
 | 
					 | 
				
			||||||
        it.size()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @suppress
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
 | 
					 | 
				
			||||||
    get() = {
 | 
					 | 
				
			||||||
        FileSystem.SYSTEM.read(this) {
 | 
					 | 
				
			||||||
            readByteArray()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @suppress
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
 | 
					 | 
				
			||||||
    get() = {
 | 
					 | 
				
			||||||
        bytesAllocatorSync()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package dev.inmo.micro_utils.common
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import kotlinx.cinterop.*
 | 
					 | 
				
			||||||
import platform.posix.snprintf
 | 
					 | 
				
			||||||
import platform.posix.sprintf
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@OptIn(ExperimentalForeignApi::class)
 | 
					 | 
				
			||||||
actual fun Float.fixed(signs: Int): Float {
 | 
					 | 
				
			||||||
    return memScoped {
 | 
					 | 
				
			||||||
        val buff = allocArray<ByteVar>(Float.SIZE_BYTES * 2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        sprintf(buff, "%.${signs}f", this@fixed)
 | 
					 | 
				
			||||||
        buff.toKString().toFloat()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@OptIn(ExperimentalForeignApi::class)
 | 
					 | 
				
			||||||
actual fun Double.fixed(signs: Int): Double {
 | 
					 | 
				
			||||||
    return memScoped {
 | 
					 | 
				
			||||||
        val buff = allocArray<ByteVar>(Double.SIZE_BYTES * 2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        sprintf(buff, "%.${signs}f", this@fixed)
 | 
					 | 
				
			||||||
        buff.toKString().toDouble()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,36 +0,0 @@
 | 
				
			|||||||
package dev.inmo.micro_utils.common
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import okio.FileSystem
 | 
					 | 
				
			||||||
import okio.Path
 | 
					 | 
				
			||||||
import okio.use
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
actual typealias MPPFile = Path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @suppress
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
actual val MPPFile.filename: FileName
 | 
					 | 
				
			||||||
    get() = FileName(toString())
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @suppress
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
actual val MPPFile.filesize: Long
 | 
					 | 
				
			||||||
    get() = FileSystem.SYSTEM.openReadOnly(this).use {
 | 
					 | 
				
			||||||
        it.size()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @suppress
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
 | 
					 | 
				
			||||||
    get() = {
 | 
					 | 
				
			||||||
        FileSystem.SYSTEM.read(this) {
 | 
					 | 
				
			||||||
            readByteArray()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @suppress
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
 | 
					 | 
				
			||||||
    get() = {
 | 
					 | 
				
			||||||
        bytesAllocatorSync()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package dev.inmo.micro_utils.common
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import kotlinx.cinterop.*
 | 
					 | 
				
			||||||
import platform.posix.snprintf
 | 
					 | 
				
			||||||
import platform.posix.sprintf
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@OptIn(ExperimentalForeignApi::class)
 | 
					 | 
				
			||||||
actual fun Float.fixed(signs: Int): Float {
 | 
					 | 
				
			||||||
    return memScoped {
 | 
					 | 
				
			||||||
        val buff = allocArray<ByteVar>(Float.SIZE_BYTES * 2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        sprintf(buff, "%.${signs}f", this@fixed)
 | 
					 | 
				
			||||||
        buff.toKString().toFloat()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@OptIn(ExperimentalForeignApi::class)
 | 
					 | 
				
			||||||
actual fun Double.fixed(signs: Int): Double {
 | 
					 | 
				
			||||||
    return memScoped {
 | 
					 | 
				
			||||||
        val buff = allocArray<ByteVar>(Double.SIZE_BYTES * 2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        sprintf(buff, "%.${signs}f", this@fixed)
 | 
					 | 
				
			||||||
        buff.toKString().toDouble()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -22,7 +22,6 @@ kotlin {
 | 
				
			|||||||
            dependencies {
 | 
					            dependencies {
 | 
				
			||||||
                api libs.kt.coroutines.android
 | 
					                api libs.kt.coroutines.android
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            dependsOn(jvmMain)
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
<manifest/>
 | 
					 | 
				
			||||||
@@ -1,46 +0,0 @@
 | 
				
			|||||||
package dev.inmo.micro_utils.coroutines.compose
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import androidx.compose.runtime.MutableState
 | 
					 | 
				
			||||||
import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
 | 
					 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * This type works like [MutableState], [kotlinx.coroutines.flow.StateFlow] and [kotlinx.coroutines.flow.MutableSharedFlow].
 | 
					 | 
				
			||||||
 * Based on [SpecialMutableStateFlow]
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@Deprecated("Will be removed soon")
 | 
					 | 
				
			||||||
class FlowState<T>(
 | 
					 | 
				
			||||||
    initial: T,
 | 
					 | 
				
			||||||
    internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
 | 
					 | 
				
			||||||
) : MutableState<T>,
 | 
					 | 
				
			||||||
    SpecialMutableStateFlow<T>(initial, internalScope) {
 | 
					 | 
				
			||||||
    private var internalValue: T = initial
 | 
					 | 
				
			||||||
    override var value: T
 | 
					 | 
				
			||||||
        get() = internalValue
 | 
					 | 
				
			||||||
        set(value) {
 | 
					 | 
				
			||||||
            internalValue = value
 | 
					 | 
				
			||||||
            tryEmit(value)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun onChangeWithoutSync(value: T) {
 | 
					 | 
				
			||||||
        internalValue = value
 | 
					 | 
				
			||||||
        super.onChangeWithoutSync(value)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun component1(): T = value
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun component2(): (T) -> Unit = { tryEmit(it) }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun tryEmit(value: T): Boolean {
 | 
					 | 
				
			||||||
        internalValue = value
 | 
					 | 
				
			||||||
        return super.tryEmit(value)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override suspend fun emit(value: T) {
 | 
					 | 
				
			||||||
        internalValue = value
 | 
					 | 
				
			||||||
        super.emit(value)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//fun <T> MutableState<T>.asFlowState(scope: CoroutineScope = CoroutineScope(Dispatchers.Main)) = FlowState(this, scope)
 | 
					 | 
				
			||||||
							
								
								
									
										24
									
								
								coroutines/compose/src/jvmTest/kotlin/FlowStateTests.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								coroutines/compose/src/jvmTest/kotlin/FlowStateTests.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					import androidx.compose.material.Button
 | 
				
			||||||
 | 
					import androidx.compose.material.Text
 | 
				
			||||||
 | 
					import androidx.compose.runtime.collectAsState
 | 
				
			||||||
 | 
					import androidx.compose.ui.test.*
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
 | 
				
			||||||
 | 
					import org.jetbrains.annotations.TestOnly
 | 
				
			||||||
 | 
					import kotlin.test.Test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FlowStateTests {
 | 
				
			||||||
 | 
					    @OptIn(ExperimentalTestApi::class)
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    @TestOnly
 | 
				
			||||||
 | 
					    fun simpleTest() = runComposeUiTest {
 | 
				
			||||||
 | 
					        val flowState = SpecialMutableStateFlow(0)
 | 
				
			||||||
 | 
					        setContent {
 | 
				
			||||||
 | 
					            Button({ flowState.value++ }) { Text("Click") }
 | 
				
			||||||
 | 
					            Text(flowState.collectAsState().value.toString())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        onNodeWithText(0.toString()).assertExists()
 | 
				
			||||||
 | 
					        onNodeWithText("Click").performClick()
 | 
				
			||||||
 | 
					        onNodeWithText(1.toString()).assertExists()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					package dev.inmo.micro_utils.coroutines
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlin.coroutines.CoroutineContext
 | 
				
			||||||
 | 
					import kotlin.coroutines.coroutineContext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typealias ExceptionHandler<T> = suspend (Throwable) -> T
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This instance will be used in all calls of [safely] where exception handler has not been passed
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					var defaultSafelyExceptionHandler: ExceptionHandler<Nothing> = { throw it }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This instance will be used in all calls of [safelyWithoutExceptions] as an exception handler for [safely] call
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					var defaultSafelyWithoutExceptionHandler: ExceptionHandler<Unit> = {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        defaultSafelyExceptionHandler(it)
 | 
				
			||||||
 | 
					    } catch (e: Throwable) {
 | 
				
			||||||
 | 
					        // do nothing
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This key can (and will) be used to get [ContextSafelyExceptionHandler] from [coroutineContext] of suspend functions
 | 
				
			||||||
 | 
					 * and in [ContextSafelyExceptionHandler] for defining of its [CoroutineContext.Element.key]
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @see safelyWithContextExceptionHandler
 | 
				
			||||||
 | 
					 * @see ContextSafelyExceptionHandler
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					object ContextSafelyExceptionHandlerKey : CoroutineContext.Key<ContextSafelyExceptionHandler>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * [ExceptionHandler] wrapper which was created to make possible to use [handler] across all coroutines calls
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @see safelyWithContextExceptionHandler
 | 
				
			||||||
 | 
					 * @see ContextSafelyExceptionHandlerKey
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class ContextSafelyExceptionHandler(
 | 
				
			||||||
 | 
					    val handler: ExceptionHandler<Unit>
 | 
				
			||||||
 | 
					) : CoroutineContext.Element {
 | 
				
			||||||
 | 
					    override val key: CoroutineContext.Key<*>
 | 
				
			||||||
 | 
					        get() = ContextSafelyExceptionHandlerKey
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -16,8 +16,8 @@ class DoWithFirstBuilder<T>(
 | 
				
			|||||||
    operator fun plus(block: suspend CoroutineScope.() -> T) {
 | 
					    operator fun plus(block: suspend CoroutineScope.() -> T) {
 | 
				
			||||||
        deferreds.add(scope.async(start = CoroutineStart.LAZY, block = block))
 | 
					        deferreds.add(scope.async(start = CoroutineStart.LAZY, block = block))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    inline fun add(noinline block: suspend CoroutineScope.() -> T) = plus(block)
 | 
					    fun add(block: suspend CoroutineScope.() -> T) = plus(block)
 | 
				
			||||||
    inline fun include(noinline block: suspend CoroutineScope.() -> T) = plus(block)
 | 
					    fun include(block: suspend CoroutineScope.() -> T) = plus(block)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun build() = deferreds.toList()
 | 
					    fun build() = deferreds.toList()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -85,32 +85,32 @@ fun <T, M> Flow<T>.subscribeAsync(
 | 
				
			|||||||
    return job
 | 
					    return job
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline fun <T, M> Flow<T>.subscribeSafelyAsync(
 | 
					fun <T, M> Flow<T>.subscribeSafelyAsync(
 | 
				
			||||||
    scope: CoroutineScope,
 | 
					    scope: CoroutineScope,
 | 
				
			||||||
    noinline markerFactory: suspend (T) -> M,
 | 
					    markerFactory: suspend (T) -> M,
 | 
				
			||||||
    noinline onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,
 | 
					    onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,
 | 
				
			||||||
    noinline block: suspend (T) -> Unit
 | 
					    block: suspend (T) -> Unit
 | 
				
			||||||
) = subscribeAsync(scope, markerFactory) {
 | 
					) = subscribeAsync(scope, markerFactory) {
 | 
				
			||||||
    safely(onException) {
 | 
					    safely(onException) {
 | 
				
			||||||
        block(it)
 | 
					        block(it)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline fun <T, M> Flow<T>.subscribeSafelyWithoutExceptionsAsync(
 | 
					fun <T, M> Flow<T>.subscribeSafelyWithoutExceptionsAsync(
 | 
				
			||||||
    scope: CoroutineScope,
 | 
					    scope: CoroutineScope,
 | 
				
			||||||
    noinline markerFactory: suspend (T) -> M,
 | 
					    markerFactory: suspend (T) -> M,
 | 
				
			||||||
    noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
 | 
					    onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
 | 
				
			||||||
    noinline block: suspend (T) -> Unit
 | 
					    block: suspend (T) -> Unit
 | 
				
			||||||
) = subscribeAsync(scope, markerFactory) {
 | 
					) = subscribeAsync(scope, markerFactory) {
 | 
				
			||||||
    safelyWithoutExceptions(onException) {
 | 
					    safelyWithoutExceptions(onException) {
 | 
				
			||||||
        block(it)
 | 
					        block(it)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline fun <T, M> Flow<T>.subscribeSafelySkippingExceptionsAsync(
 | 
					fun <T, M> Flow<T>.subscribeSafelySkippingExceptionsAsync(
 | 
				
			||||||
    scope: CoroutineScope,
 | 
					    scope: CoroutineScope,
 | 
				
			||||||
    noinline markerFactory: suspend (T) -> M,
 | 
					    markerFactory: suspend (T) -> M,
 | 
				
			||||||
    noinline block: suspend (T) -> Unit
 | 
					    block: suspend (T) -> Unit
 | 
				
			||||||
) = subscribeAsync(scope, markerFactory) {
 | 
					) = subscribeAsync(scope, markerFactory) {
 | 
				
			||||||
    safelyWithoutExceptions({ /* do nothing */}) {
 | 
					    safelyWithoutExceptions({ /* do nothing */}) {
 | 
				
			||||||
        block(it)
 | 
					        block(it)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,46 +4,58 @@ import kotlinx.coroutines.*
 | 
				
			|||||||
import kotlin.coroutines.CoroutineContext
 | 
					import kotlin.coroutines.CoroutineContext
 | 
				
			||||||
import kotlin.coroutines.coroutineContext
 | 
					import kotlin.coroutines.coroutineContext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typealias ExceptionHandler<T> = suspend (Throwable) -> T
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * This instance will be used in all calls of [safely] where exception handler has not been passed
 | 
					 * Launching [block] in [runCatching]. In case of failure, it will:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * * Try to get [ContextSafelyExceptionHandler] from current [coroutineContext] and call its
 | 
				
			||||||
 | 
					 * [ContextSafelyExceptionHandler.handler] invoke. **Thrown exception from its handler
 | 
				
			||||||
 | 
					 * will pass out of [runCatchingSafely]**
 | 
				
			||||||
 | 
					 * * Execute [onException] inside of new [runCatching] and return its result. Throws exception
 | 
				
			||||||
 | 
					 * will be caught by [runCatching] and wrapped in [Result]
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return [Result] with result of [block] if no exceptions or [Result] from [onException] execution
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
var defaultSafelyExceptionHandler: ExceptionHandler<Nothing> = { throw it }
 | 
					suspend inline fun <T> runCatchingSafely(
 | 
				
			||||||
 | 
					    onException: ExceptionHandler<T>,
 | 
				
			||||||
/**
 | 
					    block: suspend () -> T
 | 
				
			||||||
 * This instance will be used in all calls of [safelyWithoutExceptions] as an exception handler for [safely] call
 | 
					): Result<T> {
 | 
				
			||||||
 */
 | 
					    return runCatching {
 | 
				
			||||||
var defaultSafelyWithoutExceptionHandler: ExceptionHandler<Unit> = {
 | 
					        block()
 | 
				
			||||||
    try {
 | 
					    }.onFailure {
 | 
				
			||||||
        defaultSafelyExceptionHandler(it)
 | 
					        coroutineContext[ContextSafelyExceptionHandlerKey] ?.handler ?.invoke(it)
 | 
				
			||||||
    } catch (e: Throwable) {
 | 
					        return runCatching {
 | 
				
			||||||
         // do nothing
 | 
					            onException(it)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					suspend inline fun <T, R> R.runCatchingSafely(
 | 
				
			||||||
 * This key can (and will) be used to get [ContextSafelyExceptionHandler] from [coroutineContext] of suspend functions
 | 
					    onException: ExceptionHandler<T>,
 | 
				
			||||||
 * and in [ContextSafelyExceptionHandler] for defining of its [CoroutineContext.Element.key]
 | 
					    block: suspend R.() -> T
 | 
				
			||||||
 *
 | 
					): Result<T> = runCatchingSafely<T>(onException) {
 | 
				
			||||||
 * @see safelyWithContextExceptionHandler
 | 
					    block()
 | 
				
			||||||
 * @see ContextSafelyExceptionHandler
 | 
					}
 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
object ContextSafelyExceptionHandlerKey : CoroutineContext.Key<ContextSafelyExceptionHandler>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * [ExceptionHandler] wrapper which was created to make possible to use [handler] across all coroutines calls
 | 
					 * Launching [runCatchingSafely] with [defaultSafelyExceptionHandler] as `onException` parameter
 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @see safelyWithContextExceptionHandler
 | 
					 | 
				
			||||||
 * @see ContextSafelyExceptionHandlerKey
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class ContextSafelyExceptionHandler(
 | 
					suspend inline fun <T> runCatchingSafely(
 | 
				
			||||||
    val handler: ExceptionHandler<Unit>
 | 
					    block: suspend () -> T
 | 
				
			||||||
) : CoroutineContext.Element {
 | 
					): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
 | 
				
			||||||
    override val key: CoroutineContext.Key<*>
 | 
					
 | 
				
			||||||
        get() = ContextSafelyExceptionHandlerKey
 | 
					suspend inline fun <T, R> R.runCatchingSafely(
 | 
				
			||||||
 | 
					    block: suspend R.() -> T
 | 
				
			||||||
 | 
					): Result<T> = runCatchingSafely<T> {
 | 
				
			||||||
 | 
					    block()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//suspend inline fun <T, R> T.runCatchingSafely(
 | 
				
			||||||
 | 
					//    onException: ExceptionHandler<R>,
 | 
				
			||||||
 | 
					//    block: suspend T.() -> R
 | 
				
			||||||
 | 
					//): Result<R> = runCatchingSafely(onException) {
 | 
				
			||||||
 | 
					//    block()
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @return [ContextSafelyExceptionHandler] from [coroutineContext] by key [ContextSafelyExceptionHandlerKey] if
 | 
					 * @return [ContextSafelyExceptionHandler] from [coroutineContext] by key [ContextSafelyExceptionHandlerKey] if
 | 
				
			||||||
 * exists
 | 
					 * exists
 | 
				
			||||||
@@ -51,7 +63,7 @@ class ContextSafelyExceptionHandler(
 | 
				
			|||||||
 * @see ContextSafelyExceptionHandler
 | 
					 * @see ContextSafelyExceptionHandler
 | 
				
			||||||
 * @see ContextSafelyExceptionHandlerKey
 | 
					 * @see ContextSafelyExceptionHandlerKey
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
suspend inline fun contextSafelyExceptionHandler() = coroutineContext[ContextSafelyExceptionHandlerKey]
 | 
					suspend fun contextSafelyExceptionHandler() = coroutineContext[ContextSafelyExceptionHandlerKey]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * This method will set new [coroutineContext] with [ContextSafelyExceptionHandler]. In case if [coroutineContext]
 | 
					 * This method will set new [coroutineContext] with [ContextSafelyExceptionHandler]. In case if [coroutineContext]
 | 
				
			||||||
@@ -64,7 +76,7 @@ suspend inline fun contextSafelyExceptionHandler() = coroutineContext[ContextSaf
 | 
				
			|||||||
suspend fun <T> safelyWithContextExceptionHandler(
 | 
					suspend fun <T> safelyWithContextExceptionHandler(
 | 
				
			||||||
    contextExceptionHandler: ExceptionHandler<Unit>,
 | 
					    contextExceptionHandler: ExceptionHandler<Unit>,
 | 
				
			||||||
    safelyExceptionHandler: ExceptionHandler<T> = defaultSafelyExceptionHandler,
 | 
					    safelyExceptionHandler: ExceptionHandler<T> = defaultSafelyExceptionHandler,
 | 
				
			||||||
    block: suspend CoroutineScope.() -> T
 | 
					    block: suspend () -> T
 | 
				
			||||||
): T {
 | 
					): T {
 | 
				
			||||||
    val contextSafelyExceptionHandler = contextSafelyExceptionHandler() ?.handler ?.let { oldHandler ->
 | 
					    val contextSafelyExceptionHandler = contextSafelyExceptionHandler() ?.handler ?.let { oldHandler ->
 | 
				
			||||||
        ContextSafelyExceptionHandler {
 | 
					        ContextSafelyExceptionHandler {
 | 
				
			||||||
@@ -78,57 +90,35 @@ suspend fun <T> safelyWithContextExceptionHandler(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * It will run [block] inside of [supervisorScope] to avoid problems with catching of exceptions
 | 
					 * Calls [runCatchingSafely] and getting the result via [Result.getOrThrow]
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Priorities of [ExceptionHandler]s:
 | 
					 * @see runCatchingSafely
 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * * [onException] In case if custom (will be used anyway if not [defaultSafelyExceptionHandler])
 | 
					 | 
				
			||||||
 * * [CoroutineContext.get] with [SafelyExceptionHandlerKey] as key
 | 
					 | 
				
			||||||
 * * [defaultSafelyExceptionHandler]
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Remember, that [ExceptionHandler] from [CoroutineContext.get] will be used anyway if it is available. After it will
 | 
					 | 
				
			||||||
 * be called [onException]
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param [onException] Will be called when happen exception inside of [block]. By default will throw exception - this
 | 
					 | 
				
			||||||
 * exception will be available for catching
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @see defaultSafelyExceptionHandler
 | 
					 | 
				
			||||||
 * @see safelyWithoutExceptions
 | 
					 | 
				
			||||||
 * @see safelyWithContextExceptionHandler
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
suspend inline fun <T> safely(
 | 
					suspend inline fun <T> safely(
 | 
				
			||||||
    noinline onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
 | 
					    onException: ExceptionHandler<T>,
 | 
				
			||||||
    noinline block: suspend CoroutineScope.() -> T
 | 
					    block: suspend () -> T
 | 
				
			||||||
): T {
 | 
					): T = runCatchingSafely(onException, block).getOrThrow()
 | 
				
			||||||
    return try {
 | 
					 | 
				
			||||||
        supervisorScope(block)
 | 
					 | 
				
			||||||
    } catch (e: Throwable) {
 | 
					 | 
				
			||||||
        coroutineContext[ContextSafelyExceptionHandlerKey] ?.handler ?.invoke(e)
 | 
					 | 
				
			||||||
        onException(e)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
suspend inline fun <T> runCatchingSafely(
 | 
					/**
 | 
				
			||||||
    noinline onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
 | 
					 * Calls [safely] with passing of [defaultSafelyExceptionHandler] as `onException`
 | 
				
			||||||
    noinline block: suspend CoroutineScope.() -> T
 | 
					 *
 | 
				
			||||||
): Result<T> = runCatching {
 | 
					 * @see runCatchingSafely
 | 
				
			||||||
    safely(onException, block)
 | 
					 */
 | 
				
			||||||
}
 | 
					suspend inline fun <T> safely(
 | 
				
			||||||
 | 
					    block: suspend () -> T
 | 
				
			||||||
 | 
					): T = safely(defaultSafelyExceptionHandler, block)
 | 
				
			||||||
 | 
					suspend inline fun <T, R> R.safely(
 | 
				
			||||||
 | 
					    block: suspend R.() -> T
 | 
				
			||||||
 | 
					): T = safely<T> { block() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
suspend inline fun <T, R> T.runCatchingSafely(
 | 
					@Deprecated("Renamed", ReplaceWith("runCatchingSafely(block)", "dev.inmo.micro_utils.coroutines.runCatchingSafely"))
 | 
				
			||||||
    noinline onException: ExceptionHandler<R> = defaultSafelyExceptionHandler,
 | 
					suspend fun <T> safelyWithResult(
 | 
				
			||||||
    noinline block: suspend T.() -> R
 | 
					    block: suspend () -> T
 | 
				
			||||||
): Result<R> = runCatching {
 | 
					 | 
				
			||||||
    safely(onException) { block() }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
suspend inline fun <T> safelyWithResult(
 | 
					 | 
				
			||||||
    noinline block: suspend CoroutineScope.() -> T
 | 
					 | 
				
			||||||
): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
 | 
					): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
 | 
				
			||||||
 | 
					@Deprecated("Renamed", ReplaceWith("this.runCatchingSafely(block)", "dev.inmo.micro_utils.coroutines.runCatchingSafely"))
 | 
				
			||||||
suspend inline fun <T, R> T.safelyWithResult(
 | 
					suspend fun <T, R> R.safelyWithResult(
 | 
				
			||||||
    noinline block: suspend T.() -> R
 | 
					    block: suspend R.() -> T
 | 
				
			||||||
): Result<R> = runCatchingSafely(defaultSafelyExceptionHandler, block)
 | 
					): Result<T> = safelyWithResult<T> { block() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Use this handler in cases you wish to include handling of exceptions by [defaultSafelyWithoutExceptionHandler] and
 | 
					 * Use this handler in cases you wish to include handling of exceptions by [defaultSafelyWithoutExceptionHandler] and
 | 
				
			||||||
@@ -147,21 +137,23 @@ val defaultSafelyWithoutExceptionHandlerWithNull: ExceptionHandler<Nothing?> = {
 | 
				
			|||||||
 * Shortcut for [safely] with exception handler, that as expected must return null in case of impossible creating of
 | 
					 * Shortcut for [safely] with exception handler, that as expected must return null in case of impossible creating of
 | 
				
			||||||
 * result from exception (instead of throwing it, by default always returns null)
 | 
					 * result from exception (instead of throwing it, by default always returns null)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
suspend inline fun <T> safelyWithoutExceptions(
 | 
					suspend fun <T> safelyWithoutExceptions(
 | 
				
			||||||
    noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
 | 
					    onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
 | 
				
			||||||
    noinline block: suspend CoroutineScope.() -> T
 | 
					    block: suspend () -> T
 | 
				
			||||||
): T? = safely(onException, block)
 | 
					): T? = runCatchingSafely(onException, block).getOrNull()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
suspend inline fun <T> runCatchingSafelyWithoutExceptions(
 | 
					suspend fun <T> runCatchingSafelyWithoutExceptions(
 | 
				
			||||||
    noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
 | 
					    onException: ExceptionHandler<T?> = defaultSafelyExceptionHandler,
 | 
				
			||||||
    noinline block: suspend CoroutineScope.() -> T
 | 
					    block: suspend () -> T
 | 
				
			||||||
): Result<T?> = runCatching {
 | 
					): Result<T?> = runCatchingSafely(onException, block).let {
 | 
				
			||||||
    safelyWithoutExceptions(onException, block)
 | 
					    if (it.isFailure) return Result.success<T?>(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline fun CoroutineScope(
 | 
					fun CoroutineScopeWithDefaultFallback(
 | 
				
			||||||
    context: CoroutineContext,
 | 
					    context: CoroutineContext,
 | 
				
			||||||
    noinline defaultExceptionsHandler: ExceptionHandler<Unit>
 | 
					    defaultExceptionsHandler: ExceptionHandler<Unit>
 | 
				
			||||||
) = CoroutineScope(
 | 
					) = CoroutineScope(
 | 
				
			||||||
    context + ContextSafelyExceptionHandler(defaultExceptionsHandler)
 | 
					    context + ContextSafelyExceptionHandler(defaultExceptionsHandler)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,38 +4,46 @@ import kotlinx.coroutines.*
 | 
				
			|||||||
import kotlin.coroutines.CoroutineContext
 | 
					import kotlin.coroutines.CoroutineContext
 | 
				
			||||||
import kotlin.coroutines.EmptyCoroutineContext
 | 
					import kotlin.coroutines.EmptyCoroutineContext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline fun CoroutineScope.launchSafely(
 | 
					fun CoroutineScope.launchSafely(
 | 
				
			||||||
    context: CoroutineContext = EmptyCoroutineContext,
 | 
					    context: CoroutineContext = EmptyCoroutineContext,
 | 
				
			||||||
    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
					    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
				
			||||||
    noinline onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,
 | 
					    onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,
 | 
				
			||||||
    noinline block: suspend CoroutineScope.() -> Unit
 | 
					    block: suspend CoroutineScope.() -> Unit
 | 
				
			||||||
) = launch(context, start) {
 | 
					) = launch(context, start) {
 | 
				
			||||||
    safely(onException, block)
 | 
					    runCatchingSafely(onException) {
 | 
				
			||||||
 | 
					        block()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline fun CoroutineScope.launchSafelyWithoutExceptions(
 | 
					fun CoroutineScope.launchSafelyWithoutExceptions(
 | 
				
			||||||
    context: CoroutineContext = EmptyCoroutineContext,
 | 
					    context: CoroutineContext = EmptyCoroutineContext,
 | 
				
			||||||
    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
					    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
				
			||||||
    noinline onException: ExceptionHandler<Unit?> = defaultSafelyWithoutExceptionHandlerWithNull,
 | 
					    onException: ExceptionHandler<Unit?> = defaultSafelyWithoutExceptionHandlerWithNull,
 | 
				
			||||||
    noinline block: suspend CoroutineScope.() -> Unit
 | 
					    block: suspend CoroutineScope.() -> Unit
 | 
				
			||||||
) = launch(context, start) {
 | 
					) = launch(context, start) {
 | 
				
			||||||
    safelyWithoutExceptions(onException, block)
 | 
					    runCatchingSafelyWithoutExceptions(onException) {
 | 
				
			||||||
 | 
					        block()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline fun <T> CoroutineScope.asyncSafely(
 | 
					fun <T> CoroutineScope.asyncSafely(
 | 
				
			||||||
    context: CoroutineContext = EmptyCoroutineContext,
 | 
					    context: CoroutineContext = EmptyCoroutineContext,
 | 
				
			||||||
    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
					    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
				
			||||||
    noinline onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
 | 
					    onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
 | 
				
			||||||
    noinline block: suspend CoroutineScope.() -> T
 | 
					    block: suspend CoroutineScope.() -> T
 | 
				
			||||||
) = async(context, start) {
 | 
					) = async(context, start) {
 | 
				
			||||||
    safely(onException, block)
 | 
					    runCatchingSafely(onException) {
 | 
				
			||||||
 | 
					        block()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline fun <T> CoroutineScope.asyncSafelyWithoutExceptions(
 | 
					fun <T> CoroutineScope.asyncSafelyWithoutExceptions(
 | 
				
			||||||
    context: CoroutineContext = EmptyCoroutineContext,
 | 
					    context: CoroutineContext = EmptyCoroutineContext,
 | 
				
			||||||
    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
					    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
				
			||||||
    noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
 | 
					    onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
 | 
				
			||||||
    noinline block: suspend CoroutineScope.() -> T
 | 
					    block: suspend CoroutineScope.() -> T
 | 
				
			||||||
) = async(context, start) {
 | 
					) = async(context, start) {
 | 
				
			||||||
    safelyWithoutExceptions(onException, block)
 | 
					    runCatchingSafelyWithoutExceptions(onException) {
 | 
				
			||||||
 | 
					        block()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,7 +44,7 @@ sealed interface SmartMutex {
 | 
				
			|||||||
     * @param locked Preset state of [isLocked] and its internal [_lockStateFlow]
 | 
					     * @param locked Preset state of [isLocked] and its internal [_lockStateFlow]
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    class Mutable(locked: Boolean = false) : SmartMutex {
 | 
					    class Mutable(locked: Boolean = false) : SmartMutex {
 | 
				
			||||||
        private val _lockStateFlow = MutableStateFlow<Boolean>(locked)
 | 
					        private val _lockStateFlow = SpecialMutableStateFlow<Boolean>(locked)
 | 
				
			||||||
        override val lockStateFlow: StateFlow<Boolean> = _lockStateFlow.asStateFlow()
 | 
					        override val lockStateFlow: StateFlow<Boolean> = _lockStateFlow.asStateFlow()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private val internalChangesMutex = Mutex()
 | 
					        private val internalChangesMutex = Mutex()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,7 +45,7 @@ sealed interface SmartSemaphore {
 | 
				
			|||||||
     * @param locked Preset state of [freePermits] and its internal [_freePermitsStateFlow]
 | 
					     * @param locked Preset state of [freePermits] and its internal [_freePermitsStateFlow]
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    class Mutable(private val permits: Int, acquiredPermits: Int = 0) : SmartSemaphore {
 | 
					    class Mutable(private val permits: Int, acquiredPermits: Int = 0) : SmartSemaphore {
 | 
				
			||||||
        private val _freePermitsStateFlow = MutableStateFlow<Int>(permits - acquiredPermits)
 | 
					        private val _freePermitsStateFlow = SpecialMutableStateFlow<Int>(permits - acquiredPermits)
 | 
				
			||||||
        override val permitsStateFlow: StateFlow<Int> = _freePermitsStateFlow.asStateFlow()
 | 
					        override val permitsStateFlow: StateFlow<Int> = _freePermitsStateFlow.asStateFlow()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private val internalChangesMutex = Mutex(false)
 | 
					        private val internalChangesMutex = Mutex(false)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,76 +11,60 @@ import kotlinx.coroutines.flow.MutableStateFlow
 | 
				
			|||||||
import kotlinx.coroutines.flow.StateFlow
 | 
					import kotlinx.coroutines.flow.StateFlow
 | 
				
			||||||
import kotlinx.coroutines.internal.SynchronizedObject
 | 
					import kotlinx.coroutines.internal.SynchronizedObject
 | 
				
			||||||
import kotlinx.coroutines.internal.synchronized
 | 
					import kotlinx.coroutines.internal.synchronized
 | 
				
			||||||
 | 
					import kotlin.coroutines.CoroutineContext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Works like [StateFlow], but guarantee that latest value update will always be delivered to
 | 
					 * Works like [StateFlow], but guarantee that latest value update will always be delivered to
 | 
				
			||||||
 * each active subscriber
 | 
					 * each active subscriber
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
open class SpecialMutableStateFlow<T>(
 | 
					open class SpecialMutableStateFlow<T>(
 | 
				
			||||||
    initialValue: T,
 | 
					    initialValue: T
 | 
				
			||||||
    internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
 | 
					 | 
				
			||||||
) : MutableStateFlow<T>, FlowCollector<T>, MutableSharedFlow<T> {
 | 
					) : MutableStateFlow<T>, FlowCollector<T>, MutableSharedFlow<T> {
 | 
				
			||||||
    @OptIn(InternalCoroutinesApi::class)
 | 
					    @OptIn(InternalCoroutinesApi::class)
 | 
				
			||||||
    private val syncObject = SynchronizedObject()
 | 
					    private val syncObject = SynchronizedObject()
 | 
				
			||||||
    protected val internalSharedFlow: MutableSharedFlow<T> = MutableSharedFlow(
 | 
					    protected val sharingFlow: MutableSharedFlow<T> = MutableSharedFlow(
 | 
				
			||||||
        replay = 0,
 | 
					 | 
				
			||||||
        extraBufferCapacity = 2,
 | 
					 | 
				
			||||||
        onBufferOverflow = BufferOverflow.DROP_OLDEST
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    protected val publicSharedFlow: MutableSharedFlow<T> = MutableSharedFlow(
 | 
					 | 
				
			||||||
        replay = 1,
 | 
					        replay = 1,
 | 
				
			||||||
        extraBufferCapacity = 1,
 | 
					        extraBufferCapacity = 1,
 | 
				
			||||||
        onBufferOverflow = BufferOverflow.DROP_OLDEST
 | 
					        onBufferOverflow = BufferOverflow.DROP_OLDEST
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected var _value: T = initialValue
 | 
					    @OptIn(InternalCoroutinesApi::class)
 | 
				
			||||||
    override var value: T
 | 
					    override var value: T = initialValue
 | 
				
			||||||
        get() = _value
 | 
					 | 
				
			||||||
        set(value) {
 | 
					        set(value) {
 | 
				
			||||||
            doOnChangeAction(value)
 | 
					            synchronized(syncObject) {
 | 
				
			||||||
 | 
					                if (field != value) {
 | 
				
			||||||
 | 
					                    field = value
 | 
				
			||||||
 | 
					                    sharingFlow.tryEmit(value)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
    protected val job = internalSharedFlow.subscribe(internalScope) {
 | 
					 | 
				
			||||||
        doOnChangeAction(it)
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override val replayCache: List<T>
 | 
					    override val replayCache: List<T>
 | 
				
			||||||
        get() = publicSharedFlow.replayCache
 | 
					        get() = sharingFlow.replayCache
 | 
				
			||||||
    override val subscriptionCount: StateFlow<Int>
 | 
					    override val subscriptionCount: StateFlow<Int>
 | 
				
			||||||
        get() = publicSharedFlow.subscriptionCount
 | 
					        get() = sharingFlow.subscriptionCount
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init {
 | 
				
			||||||
 | 
					        sharingFlow.tryEmit(initialValue)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @OptIn(InternalCoroutinesApi::class)
 | 
					 | 
				
			||||||
    override fun compareAndSet(expect: T, update: T): Boolean {
 | 
					    override fun compareAndSet(expect: T, update: T): Boolean {
 | 
				
			||||||
        return synchronized(syncObject) {
 | 
					        if (expect == value) {
 | 
				
			||||||
            if (expect == _value && update != _value) {
 | 
					            value = update
 | 
				
			||||||
                doOnChangeAction(update)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            expect == _value
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected open fun onChangeWithoutSync(value: T) {
 | 
					 | 
				
			||||||
        _value = value
 | 
					 | 
				
			||||||
        publicSharedFlow.tryEmit(value)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    @OptIn(InternalCoroutinesApi::class)
 | 
					 | 
				
			||||||
    protected open fun doOnChangeAction(value: T) {
 | 
					 | 
				
			||||||
        synchronized(syncObject) {
 | 
					 | 
				
			||||||
            if (_value != value) {
 | 
					 | 
				
			||||||
                onChangeWithoutSync(value)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        return expect == value
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ExperimentalCoroutinesApi
 | 
					    @ExperimentalCoroutinesApi
 | 
				
			||||||
    override fun resetReplayCache() = publicSharedFlow.resetReplayCache()
 | 
					    override fun resetReplayCache() = sharingFlow.resetReplayCache()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun tryEmit(value: T): Boolean {
 | 
					    override fun tryEmit(value: T): Boolean {
 | 
				
			||||||
        return internalSharedFlow.tryEmit(value)
 | 
					        return compareAndSet(this.value, value)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun emit(value: T) {
 | 
					    override suspend fun emit(value: T) {
 | 
				
			||||||
        internalSharedFlow.emit(value)
 | 
					        compareAndSet(this.value, value)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun collect(collector: FlowCollector<T>) = publicSharedFlow.collect(collector)
 | 
					    override suspend fun collect(collector: FlowCollector<T>) = sharingFlow.collect(collector)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,8 +10,12 @@ fun CoroutineScope.LinkedSupervisorJob(
 | 
				
			|||||||
    additionalContext: CoroutineContext? = null
 | 
					    additionalContext: CoroutineContext? = null
 | 
				
			||||||
) = coroutineContext.LinkedSupervisorJob(additionalContext)
 | 
					) = coroutineContext.LinkedSupervisorJob(additionalContext)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun CoroutineScope.LinkedSupervisorScope(
 | 
					
 | 
				
			||||||
 | 
					fun CoroutineContext.LinkedSupervisorScope(
 | 
				
			||||||
    additionalContext: CoroutineContext? = null
 | 
					    additionalContext: CoroutineContext? = null
 | 
				
			||||||
) = CoroutineScope(
 | 
					) = CoroutineScope(
 | 
				
			||||||
    coroutineContext + LinkedSupervisorJob(additionalContext)
 | 
					    this + LinkedSupervisorJob(additionalContext)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					fun CoroutineScope.LinkedSupervisorScope(
 | 
				
			||||||
 | 
					    additionalContext: CoroutineContext? = null
 | 
				
			||||||
 | 
					) = coroutineContext.LinkedSupervisorScope(additionalContext)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,28 +4,71 @@ import kotlinx.coroutines.*
 | 
				
			|||||||
import kotlin.coroutines.CoroutineContext
 | 
					import kotlin.coroutines.CoroutineContext
 | 
				
			||||||
import kotlin.coroutines.EmptyCoroutineContext
 | 
					import kotlin.coroutines.EmptyCoroutineContext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun CoroutineScope.createWeakSubScope() = CoroutineScope(coroutineContext.minusKey(Job)).also { newScope ->
 | 
					/**
 | 
				
			||||||
    coroutineContext.job.invokeOnCompletion { newScope.cancel() }
 | 
					 * Created [CoroutineScope] which will [launch] listening of [context] job completing and drop itself. Current weak
 | 
				
			||||||
 | 
					 * scope **will not** be attached to [context] directly. So, this [CoroutineScope] will not prevent parent one from
 | 
				
			||||||
 | 
					 * cancelling if it is launched with [supervisorScope] or [coroutineScope], but still will follow closing status
 | 
				
			||||||
 | 
					 * of parent [Job]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun WeakScope(
 | 
				
			||||||
 | 
					    context: CoroutineContext
 | 
				
			||||||
 | 
					) = CoroutineScope(context.minusKey(Job) + Job()).also { newScope ->
 | 
				
			||||||
 | 
					    newScope.launch {
 | 
				
			||||||
 | 
					        context.job.join()
 | 
				
			||||||
 | 
					        newScope.cancel()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun CoroutineScope.weakLaunch(
 | 
					/**
 | 
				
			||||||
 | 
					 * Created [CoroutineScope] which will [launch] listening of [scope] [CoroutineContext] job completing and drop itself. Current weak
 | 
				
			||||||
 | 
					 * scope **will not** be attached to [scope] [CoroutineContext] directly. So, this [CoroutineScope] will not prevent parent one from
 | 
				
			||||||
 | 
					 * cancelling if it is launched with [supervisorScope] or [coroutineScope], but still will follow closing status
 | 
				
			||||||
 | 
					 * of parent [Job]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun WeakScope(
 | 
				
			||||||
 | 
					    scope: CoroutineScope
 | 
				
			||||||
 | 
					) = WeakScope(scope.coroutineContext)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * [this] [CoroutineScope] will be used as base for [WeakScope]. Other parameters ([context], [start], [block])
 | 
				
			||||||
 | 
					 * will be used to [launch] [Job]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun CoroutineScope.launchWeak(
 | 
				
			||||||
    context: CoroutineContext = EmptyCoroutineContext,
 | 
					    context: CoroutineContext = EmptyCoroutineContext,
 | 
				
			||||||
    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
					    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
				
			||||||
    block: suspend CoroutineScope.() -> Unit
 | 
					    block: suspend CoroutineScope.() -> Unit
 | 
				
			||||||
): Job {
 | 
					): Job {
 | 
				
			||||||
    val scope = createWeakSubScope()
 | 
					    val scope = WeakScope(this)
 | 
				
			||||||
    val job = scope.launch(context, start, block)
 | 
					    val job = scope.launch(context, start, block)
 | 
				
			||||||
    job.invokeOnCompletion { scope.cancel() }
 | 
					    job.invokeOnCompletion { scope.cancel() }
 | 
				
			||||||
    return job
 | 
					    return job
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun <T> CoroutineScope.weakAsync(
 | 
					/**
 | 
				
			||||||
 | 
					 * [this] [CoroutineScope] will be used as base for [WeakScope]. Other parameters ([context], [start], [block])
 | 
				
			||||||
 | 
					 * will be used to create [async] [Deferred]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun <T> CoroutineScope.asyncWeak(
 | 
				
			||||||
    context: CoroutineContext = EmptyCoroutineContext,
 | 
					    context: CoroutineContext = EmptyCoroutineContext,
 | 
				
			||||||
    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
					    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
				
			||||||
    block: suspend CoroutineScope.() -> T
 | 
					    block: suspend CoroutineScope.() -> T
 | 
				
			||||||
): Deferred<T> {
 | 
					): Deferred<T> {
 | 
				
			||||||
    val scope = createWeakSubScope()
 | 
					    val scope = WeakScope(this)
 | 
				
			||||||
    val deferred = scope.async(context, start, block)
 | 
					    val deferred = scope.async(context, start, block)
 | 
				
			||||||
    deferred.invokeOnCompletion { scope.cancel() }
 | 
					    deferred.invokeOnCompletion { scope.cancel() }
 | 
				
			||||||
    return deferred
 | 
					    return deferred
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Deprecated("Renamed", ReplaceWith("launchWeak(context, start, block)", "dev.inmo.micro_utils.coroutines.launchWeak"))
 | 
				
			||||||
 | 
					fun CoroutineScope.weakLaunch(
 | 
				
			||||||
 | 
					    context: CoroutineContext = EmptyCoroutineContext,
 | 
				
			||||||
 | 
					    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
				
			||||||
 | 
					    block: suspend CoroutineScope.() -> Unit
 | 
				
			||||||
 | 
					): Job = launchWeak(context, start, block)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Deprecated("Renamed", ReplaceWith("asyncWeak(context, start, block)", "dev.inmo.micro_utils.coroutines.asyncWeak"))
 | 
				
			||||||
 | 
					fun <T> CoroutineScope.weakAsync(
 | 
				
			||||||
 | 
					    context: CoroutineContext = EmptyCoroutineContext,
 | 
				
			||||||
 | 
					    start: CoroutineStart = CoroutineStart.DEFAULT,
 | 
				
			||||||
 | 
					    block: suspend CoroutineScope.() -> T
 | 
				
			||||||
 | 
					): Deferred<T> = asyncWeak(context, start, block)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.coroutines.asDeferred
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.coroutines.subscribe
 | 
				
			||||||
 | 
					import kotlinx.coroutines.Job
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.first
 | 
				
			||||||
 | 
					import kotlinx.coroutines.test.runTest
 | 
				
			||||||
 | 
					import kotlin.test.Test
 | 
				
			||||||
 | 
					import kotlin.test.assertEquals
 | 
				
			||||||
 | 
					import kotlin.test.assertTrue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SpecialMutableStateFlowTests {
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun simpleTest() = runTest {
 | 
				
			||||||
 | 
					        val specialMutableStateFlow = SpecialMutableStateFlow(0)
 | 
				
			||||||
 | 
					        specialMutableStateFlow.value = 1
 | 
				
			||||||
 | 
					        specialMutableStateFlow.first { it == 1 }
 | 
				
			||||||
 | 
					        assertEquals(1, specialMutableStateFlow.value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun specialTest() = runTest {
 | 
				
			||||||
 | 
					        val specialMutableStateFlow = SpecialMutableStateFlow(0)
 | 
				
			||||||
 | 
					        lateinit var subscriberJob: Job
 | 
				
			||||||
 | 
					        subscriberJob = specialMutableStateFlow.subscribe(this) {
 | 
				
			||||||
 | 
					            when (it) {
 | 
				
			||||||
 | 
					                1 -> specialMutableStateFlow.value = 2
 | 
				
			||||||
 | 
					                2 -> subscriberJob.cancel()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        specialMutableStateFlow.value = 1
 | 
				
			||||||
 | 
					        subscriberJob.join()
 | 
				
			||||||
 | 
					        assertEquals(2, specialMutableStateFlow.value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										64
									
								
								coroutines/src/commonTest/kotlin/WeakJobTests.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								coroutines/src/commonTest/kotlin/WeakJobTests.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					import dev.inmo.micro_utils.coroutines.asyncWeak
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.coroutines.launchWeak
 | 
				
			||||||
 | 
					import kotlinx.coroutines.*
 | 
				
			||||||
 | 
					import kotlinx.coroutines.test.runTest
 | 
				
			||||||
 | 
					import kotlin.test.Test
 | 
				
			||||||
 | 
					import kotlin.test.assertTrue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WeakJobTests {
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testWeakJob() = runTest {
 | 
				
			||||||
 | 
					        var commonJobDone = false
 | 
				
			||||||
 | 
					        var weakJobStarted = false
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            coroutineScope {
 | 
				
			||||||
 | 
					                launch {
 | 
				
			||||||
 | 
					                    delay(1000)
 | 
				
			||||||
 | 
					                    commonJobDone = true
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                asyncWeak {
 | 
				
			||||||
 | 
					                    weakJobStarted = true
 | 
				
			||||||
 | 
					                    delay(100500L)
 | 
				
			||||||
 | 
					                    error("This must never happen")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }.await()
 | 
				
			||||||
 | 
					        } catch (error: Throwable) {
 | 
				
			||||||
 | 
					            assertTrue(error is CancellationException)
 | 
				
			||||||
 | 
					            assertTrue(commonJobDone)
 | 
				
			||||||
 | 
					            assertTrue(weakJobStarted)
 | 
				
			||||||
 | 
					            return@runTest
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        error("Cancellation exception has not been thrown")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testThatWeakJobsWorksCorrectly() = runTest {
 | 
				
			||||||
 | 
					        val scope = CoroutineScope(Dispatchers.Default)
 | 
				
			||||||
 | 
					        lateinit var weakLaunchJob: Job
 | 
				
			||||||
 | 
					        lateinit var weakAsyncJob: Job
 | 
				
			||||||
 | 
					        val completeDeferred = Job()
 | 
				
			||||||
 | 
					        coroutineScope {
 | 
				
			||||||
 | 
					            weakLaunchJob = launchWeak {
 | 
				
			||||||
 | 
					                while (isActive) {
 | 
				
			||||||
 | 
					                    delay(100L)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            weakAsyncJob = asyncWeak {
 | 
				
			||||||
 | 
					                while (isActive) {
 | 
				
			||||||
 | 
					                    delay(100L)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            coroutineContext.job.invokeOnCompletion {
 | 
				
			||||||
 | 
					                scope.launch {
 | 
				
			||||||
 | 
					                    delay(1000L)
 | 
				
			||||||
 | 
					                    completeDeferred.complete()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            launch { delay(1000L); cancel() }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        completeDeferred.join()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(!weakLaunchJob.isActive)
 | 
				
			||||||
 | 
					        assertTrue(!weakAsyncJob.isActive)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,40 +0,0 @@
 | 
				
			|||||||
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)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
package dev.inmo.micro_utils.fsm.common
 | 
					package dev.inmo.micro_utils.fsm.common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dev.inmo.micro_utils.common.Optional
 | 
					import dev.inmo.micro_utils.common.Optional
 | 
				
			||||||
import dev.inmo.micro_utils.common.onPresented
 | 
					 | 
				
			||||||
import dev.inmo.micro_utils.coroutines.*
 | 
					import dev.inmo.micro_utils.coroutines.*
 | 
				
			||||||
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
 | 
					import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
 | 
				
			||||||
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
 | 
					import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
 | 
				
			||||||
@@ -118,15 +117,17 @@ open class DefaultStatesMachine <T: State>(
 | 
				
			|||||||
     * [launchStateHandling] will returns some [State] then [statesManager] [StatesManager.update] will be used, otherwise
 | 
					     * [launchStateHandling] will returns some [State] then [statesManager] [StatesManager.update] will be used, otherwise
 | 
				
			||||||
     * [StatesManager.endChain].
 | 
					     * [StatesManager.endChain].
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    override fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions {
 | 
					    override fun start(scope: CoroutineScope): Job {
 | 
				
			||||||
        (statesManager.getActiveStates().asFlow() + statesManager.onStartChain).subscribeSafelyWithoutExceptions(this) {
 | 
					        val supervisorScope = scope.LinkedSupervisorScope()
 | 
				
			||||||
            launch { performStateUpdate(Optional.absent(), it, scope.LinkedSupervisorScope()) }
 | 
					        supervisorScope.launchSafelyWithoutExceptions {
 | 
				
			||||||
 | 
					            (statesManager.getActiveStates().asFlow() + statesManager.onStartChain).subscribeSafelyWithoutExceptions(supervisorScope) {
 | 
				
			||||||
 | 
					                supervisorScope.launch { performStateUpdate(Optional.absent(), it, supervisorScope) }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        statesManager.onChainStateUpdated.subscribeSafelyWithoutExceptions(this) {
 | 
					            statesManager.onChainStateUpdated.subscribeSafelyWithoutExceptions(supervisorScope) {
 | 
				
			||||||
            launch { performStateUpdate(Optional.presented(it.first), it.second, scope.LinkedSupervisorScope()) }
 | 
					                supervisorScope.launch { performStateUpdate(Optional.presented(it.first), it.second, supervisorScope) }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        statesManager.onEndChain.subscribeSafelyWithoutExceptions(this) { removedState ->
 | 
					            statesManager.onEndChain.subscribeSafelyWithoutExceptions(supervisorScope) { removedState ->
 | 
				
			||||||
            launch {
 | 
					                supervisorScope.launch {
 | 
				
			||||||
                    statesJobsMutex.withLock {
 | 
					                    statesJobsMutex.withLock {
 | 
				
			||||||
                        val stateInMap = statesJobs.keys.firstOrNull { stateInMap -> stateInMap == removedState }
 | 
					                        val stateInMap = statesJobs.keys.firstOrNull { stateInMap -> stateInMap == removedState }
 | 
				
			||||||
                        if (stateInMap === removedState) {
 | 
					                        if (stateInMap === removedState) {
 | 
				
			||||||
@@ -137,6 +138,9 @@ open class DefaultStatesMachine <T: State>(
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return supervisorScope.coroutineContext.job
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Just calls [StatesManager.startChain] of [statesManager]
 | 
					     * Just calls [StatesManager.startChain] of [statesManager]
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,5 +15,5 @@ crypto_js_version=4.1.1
 | 
				
			|||||||
# Project data
 | 
					# Project data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
group=dev.inmo
 | 
					group=dev.inmo
 | 
				
			||||||
version=0.20.34
 | 
					version=0.21.1
 | 
				
			||||||
android_code_version=240
 | 
					android_code_version=260
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,41 +1,41 @@
 | 
				
			|||||||
[versions]
 | 
					[versions]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
kt = "1.9.22"
 | 
					kt = "1.9.23"
 | 
				
			||||||
kt-serialization = "1.6.2"
 | 
					kt-serialization = "1.6.3"
 | 
				
			||||||
kt-coroutines = "1.7.3"
 | 
					kt-coroutines = "1.8.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
kslog = "1.3.2"
 | 
					kslog = "1.3.4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jb-compose = "1.5.12"
 | 
					jb-compose = "1.6.2"
 | 
				
			||||||
jb-exposed = "0.47.0"
 | 
					jb-exposed = "0.50.1"
 | 
				
			||||||
jb-dokka = "1.9.10"
 | 
					jb-dokka = "1.9.20"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
korlibs = "5.3.1"
 | 
					korlibs = "5.4.0"
 | 
				
			||||||
uuid = "0.8.2"
 | 
					uuid = "0.8.4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ktor = "2.3.8"
 | 
					ktor = "2.3.11"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
gh-release = "2.5.2"
 | 
					gh-release = "2.5.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
koin = "3.5.3"
 | 
					koin = "3.5.6"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
okio = "3.8.0"
 | 
					okio = "3.9.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ksp = "1.9.22-1.0.17"
 | 
					ksp = "1.9.23-1.0.20"
 | 
				
			||||||
kotlin-poet = "1.16.0"
 | 
					kotlin-poet = "1.16.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
versions = "0.51.0"
 | 
					versions = "0.51.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
android-gradle = "8.2.2"
 | 
					android-gradle = "8.2.0"
 | 
				
			||||||
dexcount = "4.0.0"
 | 
					dexcount = "4.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
android-coreKtx = "1.12.0"
 | 
					android-coreKtx = "1.13.1"
 | 
				
			||||||
android-recyclerView = "1.3.2"
 | 
					android-recyclerView = "1.3.2"
 | 
				
			||||||
android-appCompat = "1.6.1"
 | 
					android-appCompat = "1.6.1"
 | 
				
			||||||
android-fragment = "1.6.2"
 | 
					android-fragment = "1.7.1"
 | 
				
			||||||
android-espresso = "3.5.1"
 | 
					android-espresso = "3.5.1"
 | 
				
			||||||
android-test = "1.1.5"
 | 
					android-test = "1.1.5"
 | 
				
			||||||
android-compose-material3 = "1.1.2"
 | 
					android-compose-material3 = "1.2.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
android-props-minSdk = "21"
 | 
					android-props-minSdk = "21"
 | 
				
			||||||
android-props-compileSdk = "34"
 | 
					android-props-compileSdk = "34"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,5 @@
 | 
				
			|||||||
distributionBase=GRADLE_USER_HOME
 | 
					distributionBase=GRADLE_USER_HOME
 | 
				
			||||||
distributionPath=wrapper/dists
 | 
					distributionPath=wrapper/dists
 | 
				
			||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
 | 
					distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
 | 
				
			||||||
zipStoreBase=GRADLE_USER_HOME
 | 
					zipStoreBase=GRADLE_USER_HOME
 | 
				
			||||||
zipStorePath=wrapper/dists
 | 
					zipStorePath=wrapper/dists
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								ksp/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								ksp/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					plugins {
 | 
				
			||||||
 | 
					    id "org.jetbrains.kotlin.jvm"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					apply from: "$publishJvmOnlyPath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					repositories {
 | 
				
			||||||
 | 
					    mavenCentral()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dependencies {
 | 
				
			||||||
 | 
					    api project(":micro_utils.common")
 | 
				
			||||||
 | 
					    api libs.kotlin.poet
 | 
				
			||||||
 | 
					    api libs.ksp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					java {
 | 
				
			||||||
 | 
					    sourceCompatibility = JavaVersion.VERSION_17
 | 
				
			||||||
 | 
					    targetCompatibility = JavaVersion.VERSION_17
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										49
									
								
								ksp/generator/src/main/kotlin/FilesWorkaround.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								ksp/generator/src/main/kotlin/FilesWorkaround.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					package dev.inmo.micro_ksp.generator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.devtools.ksp.symbol.KSClassDeclaration
 | 
				
			||||||
 | 
					import com.google.devtools.ksp.symbol.KSFile
 | 
				
			||||||
 | 
					import com.squareup.kotlinpoet.FileSpec
 | 
				
			||||||
 | 
					import java.io.File
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun KSClassDeclaration.writeFile(
 | 
				
			||||||
 | 
					    prefix: String = "",
 | 
				
			||||||
 | 
					    suffix: String = "",
 | 
				
			||||||
 | 
					    relatedPath: String = "",
 | 
				
			||||||
 | 
					    force: Boolean = false,
 | 
				
			||||||
 | 
					    fileSpecBuilder: () -> FileSpec
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    val containingFile = containingFile!!
 | 
				
			||||||
 | 
					    File(
 | 
				
			||||||
 | 
					        File(
 | 
				
			||||||
 | 
					            File(containingFile.filePath).parent,
 | 
				
			||||||
 | 
					            relatedPath
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        "$prefix${simpleName.asString()}$suffix.kt"
 | 
				
			||||||
 | 
					    ).takeIf { force || !it.exists() } ?.apply {
 | 
				
			||||||
 | 
					        parentFile.mkdirs()
 | 
				
			||||||
 | 
					        writer().use { writer ->
 | 
				
			||||||
 | 
					            fileSpecBuilder().writeTo(writer)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun KSFile.writeFile(
 | 
				
			||||||
 | 
					    prefix: String = "",
 | 
				
			||||||
 | 
					    suffix: String = "",
 | 
				
			||||||
 | 
					    relatedPath: String = "",
 | 
				
			||||||
 | 
					    force: Boolean = false,
 | 
				
			||||||
 | 
					    fileSpecBuilder: () -> FileSpec
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    File(
 | 
				
			||||||
 | 
					        File(
 | 
				
			||||||
 | 
					            File(filePath).parent,
 | 
				
			||||||
 | 
					            relatedPath
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        "$prefix${fileName.dropLastWhile { it != '.' }.removeSuffix(".")}$suffix.kt"
 | 
				
			||||||
 | 
					    ).takeIf { force || !it.exists() } ?.apply {
 | 
				
			||||||
 | 
					        parentFile.mkdirs()
 | 
				
			||||||
 | 
					        writer().use { writer ->
 | 
				
			||||||
 | 
					            fileSpecBuilder().writeTo(writer)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								ksp/sealed/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								ksp/sealed/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: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
 | 
				
			||||||
							
								
								
									
										21
									
								
								ksp/sealed/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								ksp/sealed/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					plugins {
 | 
				
			||||||
 | 
					    id "org.jetbrains.kotlin.jvm"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					apply from: "$publishJvmOnlyPath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					repositories {
 | 
				
			||||||
 | 
					    mavenCentral()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dependencies {
 | 
				
			||||||
 | 
					    api project(":micro_utils.ksp.generator")
 | 
				
			||||||
 | 
					    api project(":micro_utils.ksp.sealed")
 | 
				
			||||||
 | 
					    api libs.kotlin.poet
 | 
				
			||||||
 | 
					    api libs.ksp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					java {
 | 
				
			||||||
 | 
					    sourceCompatibility = JavaVersion.VERSION_17
 | 
				
			||||||
 | 
					    targetCompatibility = JavaVersion.VERSION_17
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										110
									
								
								ksp/sealed/generator/src/main/kotlin/Processor.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								ksp/sealed/generator/src/main/kotlin/Processor.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					package dev.inmo.micro_utils.ksp.sealed.generator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.devtools.ksp.KspExperimental
 | 
				
			||||||
 | 
					import com.google.devtools.ksp.getAnnotationsByType
 | 
				
			||||||
 | 
					import com.google.devtools.ksp.processing.CodeGenerator
 | 
				
			||||||
 | 
					import com.google.devtools.ksp.processing.Resolver
 | 
				
			||||||
 | 
					import com.google.devtools.ksp.processing.SymbolProcessor
 | 
				
			||||||
 | 
					import com.google.devtools.ksp.symbol.*
 | 
				
			||||||
 | 
					import com.squareup.kotlinpoet.AnnotationSpec
 | 
				
			||||||
 | 
					import com.squareup.kotlinpoet.ClassName
 | 
				
			||||||
 | 
					import com.squareup.kotlinpoet.CodeBlock
 | 
				
			||||||
 | 
					import com.squareup.kotlinpoet.FileSpec
 | 
				
			||||||
 | 
					import com.squareup.kotlinpoet.FunSpec
 | 
				
			||||||
 | 
					import com.squareup.kotlinpoet.KModifier
 | 
				
			||||||
 | 
					import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
 | 
				
			||||||
 | 
					import com.squareup.kotlinpoet.PropertySpec
 | 
				
			||||||
 | 
					import com.squareup.kotlinpoet.asTypeName
 | 
				
			||||||
 | 
					import com.squareup.kotlinpoet.ksp.toClassName
 | 
				
			||||||
 | 
					import dev.inmo.micro_ksp.generator.writeFile
 | 
				
			||||||
 | 
					import dev.inmo.microutils.kps.sealed.GenerateSealedWorkaround
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Processor(
 | 
				
			||||||
 | 
					    private val codeGenerator: CodeGenerator
 | 
				
			||||||
 | 
					) : SymbolProcessor {
 | 
				
			||||||
 | 
					    private fun KSClassDeclaration.resolveSubclasses(): List<KSClassDeclaration> {
 | 
				
			||||||
 | 
					        return (getSealedSubclasses().flatMap {
 | 
				
			||||||
 | 
					            it.resolveSubclasses()
 | 
				
			||||||
 | 
					        }.ifEmpty {
 | 
				
			||||||
 | 
					            sequenceOf(this)
 | 
				
			||||||
 | 
					        }).toList()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @OptIn(KspExperimental::class)
 | 
				
			||||||
 | 
					    private fun FileSpec.Builder.generateSealedWorkaround(
 | 
				
			||||||
 | 
					        ksClassDeclaration: KSClassDeclaration,
 | 
				
			||||||
 | 
					        resolver: Resolver
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        val subClasses = ksClassDeclaration.resolveSubclasses().distinct()
 | 
				
			||||||
 | 
					        val subClassesNames = subClasses.filter {
 | 
				
			||||||
 | 
					            when (it.classKind) {
 | 
				
			||||||
 | 
					                ClassKind.ENUM_ENTRY,
 | 
				
			||||||
 | 
					                ClassKind.OBJECT -> true
 | 
				
			||||||
 | 
					                ClassKind.INTERFACE,
 | 
				
			||||||
 | 
					                ClassKind.CLASS,
 | 
				
			||||||
 | 
					                ClassKind.ENUM_CLASS,
 | 
				
			||||||
 | 
					                ClassKind.ANNOTATION_CLASS -> false
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }.filter {
 | 
				
			||||||
 | 
					            it.getAnnotationsByType(GenerateSealedWorkaround.Exclude::class).count() == 0
 | 
				
			||||||
 | 
					        }.sortedBy {
 | 
				
			||||||
 | 
					            (it.getAnnotationsByType(GenerateSealedWorkaround.Order::class).firstOrNull()) ?.order ?: 0
 | 
				
			||||||
 | 
					        }.map {
 | 
				
			||||||
 | 
					            it.toClassName()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        val className = ksClassDeclaration.toClassName()
 | 
				
			||||||
 | 
					        val setType = Set::class.asTypeName().parameterizedBy(
 | 
				
			||||||
 | 
					            ksClassDeclaration.toClassName()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        addProperty(
 | 
				
			||||||
 | 
					            PropertySpec.builder(
 | 
				
			||||||
 | 
					                "values",
 | 
				
			||||||
 | 
					                setType
 | 
				
			||||||
 | 
					            ).apply {
 | 
				
			||||||
 | 
					                modifiers.add(
 | 
				
			||||||
 | 
					                    KModifier.PRIVATE
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                initializer(
 | 
				
			||||||
 | 
					                    CodeBlock.of(
 | 
				
			||||||
 | 
					                        """setOf(${subClassesNames.joinToString(",\n") {it.simpleNames.joinToString(".")}})"""
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }.build()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        addFunction(
 | 
				
			||||||
 | 
					            FunSpec.builder("values").apply {
 | 
				
			||||||
 | 
					                receiver(ClassName(className.packageName, *className.simpleNames.toTypedArray(), "Companion"))
 | 
				
			||||||
 | 
					                returns(setType)
 | 
				
			||||||
 | 
					                addCode(
 | 
				
			||||||
 | 
					                    CodeBlock.of(
 | 
				
			||||||
 | 
					                        """return values"""
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }.build()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @OptIn(KspExperimental::class)
 | 
				
			||||||
 | 
					    override fun process(resolver: Resolver): List<KSAnnotated> {
 | 
				
			||||||
 | 
					        (resolver.getSymbolsWithAnnotation(GenerateSealedWorkaround::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach {
 | 
				
			||||||
 | 
					            val prefix = it.getAnnotationsByType(GenerateSealedWorkaround::class).first().prefix
 | 
				
			||||||
 | 
					            it.writeFile(prefix = prefix, suffix = "SealedWorkaround") {
 | 
				
			||||||
 | 
					                FileSpec.builder(
 | 
				
			||||||
 | 
					                    it.packageName.asString(),
 | 
				
			||||||
 | 
					                    "${it.simpleName.getShortName()}SealedWorkaround"
 | 
				
			||||||
 | 
					                ).apply {
 | 
				
			||||||
 | 
					                    addFileComment(
 | 
				
			||||||
 | 
					                        """
 | 
				
			||||||
 | 
					                        THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
 | 
				
			||||||
 | 
					                        TO REGENERATE IT JUST DELETE FILE
 | 
				
			||||||
 | 
					                        ORIGINAL FILE: ${it.containingFile ?.fileName}
 | 
				
			||||||
 | 
					                        """.trimIndent()
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    generateSealedWorkaround(it, resolver)
 | 
				
			||||||
 | 
					                }.build()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return emptyList()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								ksp/sealed/generator/src/main/kotlin/Provider.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ksp/sealed/generator/src/main/kotlin/Provider.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					package dev.inmo.micro_utils.ksp.sealed.generator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.devtools.ksp.processing.SymbolProcessor
 | 
				
			||||||
 | 
					import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
 | 
				
			||||||
 | 
					import com.google.devtools.ksp.processing.SymbolProcessorProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Provider : SymbolProcessorProvider {
 | 
				
			||||||
 | 
					    override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = Processor(
 | 
				
			||||||
 | 
					        environment.codeGenerator
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					dev.inmo.micro_utils.ksp.sealed.generator.Provider
 | 
				
			||||||
							
								
								
									
										27
									
								
								ksp/sealed/generator/test/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								ksp/sealed/generator/test/build.gradle
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					plugins {
 | 
				
			||||||
 | 
					    id "org.jetbrains.kotlin.multiplatform"
 | 
				
			||||||
 | 
					    id "org.jetbrains.kotlin.plugin.serialization"
 | 
				
			||||||
 | 
					    id "com.android.library"
 | 
				
			||||||
 | 
					    id "com.google.devtools.ksp"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					apply from: "$mppProjectWithSerializationPresetPath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					kotlin {
 | 
				
			||||||
 | 
					    sourceSets {
 | 
				
			||||||
 | 
					        commonMain {
 | 
				
			||||||
 | 
					            dependencies {
 | 
				
			||||||
 | 
					                api project(":micro_utils.ksp.sealed")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dependencies {
 | 
				
			||||||
 | 
					    add("kspCommonMainMetadata", project(":micro_utils.ksp.sealed.generator"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ksp {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								ksp/sealed/generator/test/src/commonMain/kotlin/Test.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								ksp/sealed/generator/test/src/commonMain/kotlin/Test.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					package dev.inmo.micro_utils.ksp.sealed.generator.test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dev.inmo.microutils.kps.sealed.GenerateSealedWorkaround
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@GenerateSealedWorkaround
 | 
				
			||||||
 | 
					sealed interface Test {
 | 
				
			||||||
 | 
					    @GenerateSealedWorkaround.Order(2)
 | 
				
			||||||
 | 
					    object A : Test
 | 
				
			||||||
 | 
					    @GenerateSealedWorkaround.Exclude
 | 
				
			||||||
 | 
					    object B : Test
 | 
				
			||||||
 | 
					    @GenerateSealedWorkaround.Order(0)
 | 
				
			||||||
 | 
					    object C : Test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Required for successful sealed workaround generation
 | 
				
			||||||
 | 
					    companion object
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					// THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
 | 
				
			||||||
 | 
					// TO REGENERATE IT JUST DELETE FILE
 | 
				
			||||||
 | 
					// ORIGINAL FILE: Test.kt
 | 
				
			||||||
 | 
					package dev.inmo.micro_utils.ksp.`sealed`.generator.test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlin.collections.Set
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private val values: Set<Test> = setOf(Test.C,
 | 
				
			||||||
 | 
					    Test.A)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public fun Test.Companion.values(): Set<Test> = values
 | 
				
			||||||
							
								
								
									
										14
									
								
								ksp/sealed/src/commonMain/kotlin/GenerateSealedWorkaround.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								ksp/sealed/src/commonMain/kotlin/GenerateSealedWorkaround.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					package dev.inmo.microutils.kps.sealed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Retention(AnnotationRetention.BINARY)
 | 
				
			||||||
 | 
					@Target(AnnotationTarget.CLASS)
 | 
				
			||||||
 | 
					annotation class GenerateSealedWorkaround(
 | 
				
			||||||
 | 
					    val prefix: String = ""
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    @Retention(AnnotationRetention.BINARY)
 | 
				
			||||||
 | 
					    @Target(AnnotationTarget.CLASS)
 | 
				
			||||||
 | 
					    annotation class Order(val order: Int)
 | 
				
			||||||
 | 
					    @Retention(AnnotationRetention.BINARY)
 | 
				
			||||||
 | 
					    @Target(AnnotationTarget.CLASS)
 | 
				
			||||||
 | 
					    annotation class Exclude
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -15,9 +15,6 @@ kotlin {
 | 
				
			|||||||
                api libs.ktor.client
 | 
					                api libs.ktor.client
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        androidMain {
 | 
					 | 
				
			||||||
            dependsOn jvmMain
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        linuxX64Main {
 | 
					        linuxX64Main {
 | 
				
			||||||
            dependencies {
 | 
					            dependencies {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,13 +12,9 @@ kotlin {
 | 
				
			|||||||
            dependencies {
 | 
					            dependencies {
 | 
				
			||||||
                api internalProject("micro_utils.common")
 | 
					                api internalProject("micro_utils.common")
 | 
				
			||||||
                api libs.kt.serialization.cbor
 | 
					                api libs.kt.serialization.cbor
 | 
				
			||||||
                api libs.klock
 | 
					 | 
				
			||||||
                api libs.uuid
 | 
					                api libs.uuid
 | 
				
			||||||
                api libs.ktor.io
 | 
					                api libs.ktor.io
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        androidMain {
 | 
					 | 
				
			||||||
            dependsOn jvmMain
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ class TemporalFilesRoutingConfigurator(
 | 
				
			|||||||
                    filesMutex: Mutex,
 | 
					                    filesMutex: Mutex,
 | 
				
			||||||
                    onNewFileFlow: Flow<TemporalFileId>
 | 
					                    onNewFileFlow: Flow<TemporalFileId>
 | 
				
			||||||
                ): Job = scope.launchSafelyWithoutExceptions {
 | 
					                ): Job = scope.launchSafelyWithoutExceptions {
 | 
				
			||||||
                    while (isActive) {
 | 
					                    while (currentCoroutineContext().isActive) {
 | 
				
			||||||
                        val filesWithCreationInfo = filesMap.mapNotNull { (fileId, file) ->
 | 
					                        val filesWithCreationInfo = filesMap.mapNotNull { (fileId, file) ->
 | 
				
			||||||
                            fileId to ((Files.getAttribute(file.toPath(), "creationTime") as? FileTime) ?.toMillis() ?: return@mapNotNull null)
 | 
					                            fileId to ((Files.getAttribute(file.toPath(), "creationTime") as? FileTime) ?.toMillis() ?: return@mapNotNull null)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,11 +5,3 @@ plugins {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
 | 
					apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
 | 
				
			||||||
 | 
					 | 
				
			||||||
kotlin {
 | 
					 | 
				
			||||||
    sourceSets {
 | 
					 | 
				
			||||||
        androidMain {
 | 
					 | 
				
			||||||
            dependsOn jvmMain
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					@file:Suppress("SERIALIZER_TYPE_INCOMPATIBLE") // for suppressing of @Serializable(IetfLangSerializer::class) on inheritors of IetfLang
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package dev.inmo.micro_utils.language_codes
 | 
					package dev.inmo.micro_utils.language_codes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import kotlinx.serialization.Serializable
 | 
					import kotlinx.serialization.Serializable
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,6 +60,12 @@ kotlin {
 | 
				
			|||||||
                implementation kotlin('test-junit')
 | 
					                implementation kotlin('test-junit')
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        nativeMain.dependsOn commonMain
 | 
				
			||||||
 | 
					        linuxX64Main.dependsOn nativeMain
 | 
				
			||||||
 | 
					        mingwX64Main.dependsOn nativeMain
 | 
				
			||||||
 | 
					        linuxArm64Main.dependsOn nativeMain
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        androidMain.dependsOn jvmMain
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,6 +45,13 @@ kotlin {
 | 
				
			|||||||
                implementation kotlin('test-junit')
 | 
					                implementation kotlin('test-junit')
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nativeMain.dependsOn commonMain
 | 
				
			||||||
 | 
					        linuxX64Main.dependsOn nativeMain
 | 
				
			||||||
 | 
					        mingwX64Main.dependsOn nativeMain
 | 
				
			||||||
 | 
					        linuxArm64Main.dependsOn nativeMain
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        androidMain.dependsOn jvmMain
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,6 +55,10 @@ kotlin {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nativeMain.dependsOn commonMain
 | 
				
			||||||
 | 
					        linuxX64Main.dependsOn nativeMain
 | 
				
			||||||
 | 
					        mingwX64Main.dependsOn nativeMain
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        androidMain.dependsOn jvmMain
 | 
					        androidMain.dependsOn jvmMain
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,8 +28,8 @@ kotlin {
 | 
				
			|||||||
        commonMain {
 | 
					        commonMain {
 | 
				
			||||||
            dependencies {
 | 
					            dependencies {
 | 
				
			||||||
                implementation kotlin('stdlib')
 | 
					                implementation kotlin('stdlib')
 | 
				
			||||||
                api libs.kt.serialization
 | 
					 | 
				
			||||||
                implementation compose.runtime
 | 
					                implementation compose.runtime
 | 
				
			||||||
 | 
					                api libs.kt.serialization
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        commonTest {
 | 
					        commonTest {
 | 
				
			||||||
@@ -47,6 +47,7 @@ kotlin {
 | 
				
			|||||||
        jvmTest {
 | 
					        jvmTest {
 | 
				
			||||||
            dependencies {
 | 
					            dependencies {
 | 
				
			||||||
                implementation kotlin('test-junit')
 | 
					                implementation kotlin('test-junit')
 | 
				
			||||||
 | 
					                implementation compose.uiTest
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        jsMain {
 | 
					        jsMain {
 | 
				
			||||||
@@ -61,14 +62,20 @@ kotlin {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        androidUnitTest {
 | 
					        androidUnitTest {
 | 
				
			||||||
 | 
					            dependencies {
 | 
				
			||||||
 | 
					                implementation kotlin('test-junit')
 | 
				
			||||||
 | 
					                implementation libs.android.test.junit
 | 
				
			||||||
 | 
					                implementation libs.android.espresso
 | 
				
			||||||
 | 
					                implementation compose.uiTest
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        androidInstrumentedTest {
 | 
				
			||||||
            dependencies {
 | 
					            dependencies {
 | 
				
			||||||
                implementation kotlin('test-junit')
 | 
					                implementation kotlin('test-junit')
 | 
				
			||||||
                implementation libs.android.test.junit
 | 
					                implementation libs.android.test.junit
 | 
				
			||||||
                implementation libs.android.espresso
 | 
					                implementation libs.android.espresso
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        androidMain.dependsOn jvmMain
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,13 @@ data class PaginationResult<T>(
 | 
				
			|||||||
     * Amount of pages for current pagination
 | 
					     * Amount of pages for current pagination
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @EncodeDefault
 | 
					    @EncodeDefault
 | 
				
			||||||
    val pagesNumber: Int = ceil(objectsNumber / size.toFloat()).toInt()
 | 
					    @SerialName("pagesNumber")
 | 
				
			||||||
 | 
					    val pagesNumberLong: Long = ceil(objectsNumber / size.toFloat()).toLong()
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Amount of pages for current pagination
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Transient
 | 
				
			||||||
 | 
					    val pagesNumber: Int = pagesNumberLong.toInt()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
        page: Int,
 | 
					        page: Int,
 | 
				
			||||||
@@ -35,31 +41,58 @@ data class PaginationResult<T>(
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun <T> emptyPaginationResult() = PaginationResult<T>(0, 0, emptyList(), 0L)
 | 
					val PaginationResult<*>.lastPageLong
 | 
				
			||||||
 | 
					    get() = pagesNumberLong - 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					val PaginationResult<*>.lastPage
 | 
				
			||||||
 | 
					    get() = lastPageLong.toInt()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					val PaginationResult<*>.isLastPage
 | 
				
			||||||
 | 
					    get() = page.toLong() == lastPageLong
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun <T> emptyPaginationResult(
 | 
					fun <T> emptyPaginationResult(
 | 
				
			||||||
    basePagination: Pagination
 | 
					    basePagination: Pagination,
 | 
				
			||||||
 | 
					    objectsNumber: Number
 | 
				
			||||||
) = PaginationResult<T>(
 | 
					) = PaginationResult<T>(
 | 
				
			||||||
    basePagination.page,
 | 
					    basePagination.page,
 | 
				
			||||||
    basePagination.size,
 | 
					    basePagination.size,
 | 
				
			||||||
    emptyList(),
 | 
					    emptyList(),
 | 
				
			||||||
    0L
 | 
					    objectsNumber.toLong()
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					fun <T> emptyPaginationResult(
 | 
				
			||||||
 | 
					    basePagination: Pagination,
 | 
				
			||||||
 | 
					) = emptyPaginationResult<T>(basePagination, 0)
 | 
				
			||||||
 | 
					fun <T> emptyPaginationResult() = emptyPaginationResult<T>(FirstPagePagination(0))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @return New [PaginationResult] with [data] without checking of data sizes equality
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					inline fun <I, O> PaginationResult<I>.changeResultsUnchecked(
 | 
				
			||||||
 | 
					    block: PaginationResult<I>.() -> List<O>
 | 
				
			||||||
 | 
					): PaginationResult<O> = PaginationResult(page, size, block(), objectsNumber)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @return New [PaginationResult] with [data] without checking of data sizes equality
 | 
					 * @return New [PaginationResult] with [data] without checking of data sizes equality
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
fun <I, O> PaginationResult<I>.changeResultsUnchecked(
 | 
					fun <I, O> PaginationResult<I>.changeResultsUnchecked(
 | 
				
			||||||
    data: List<O>
 | 
					    data: List<O>
 | 
				
			||||||
): PaginationResult<O> = PaginationResult(page, size, data, objectsNumber)
 | 
					): PaginationResult<O> = changeResultsUnchecked { data }
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @return New [PaginationResult] with [data] <b>with</b> checking of data sizes equality
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					inline fun <I, O> PaginationResult<I>.changeResults(
 | 
				
			||||||
 | 
					    block: PaginationResult<I>.() -> List<O>
 | 
				
			||||||
 | 
					): PaginationResult<O> {
 | 
				
			||||||
 | 
					    val data = block()
 | 
				
			||||||
 | 
					    require(data.size == results.size)
 | 
				
			||||||
 | 
					    return changeResultsUnchecked(data)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @return New [PaginationResult] with [data] <b>with</b> checking of data sizes equality
 | 
					 * @return New [PaginationResult] with [data] <b>with</b> checking of data sizes equality
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
fun <I, O> PaginationResult<I>.changeResults(
 | 
					fun <I, O> PaginationResult<I>.changeResults(
 | 
				
			||||||
    data: List<O>
 | 
					    data: List<O>
 | 
				
			||||||
): PaginationResult<O> {
 | 
					): PaginationResult<O> = changeResults { data }
 | 
				
			||||||
    require(data.size == results.size)
 | 
					 | 
				
			||||||
    return changeResultsUnchecked(data)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun <T> List<T>.createPaginationResult(
 | 
					fun <T> List<T>.createPaginationResult(
 | 
				
			||||||
    pagination: Pagination,
 | 
					    pagination: Pagination,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,8 +10,7 @@ inline fun doWithPagination(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Suppress("NOTHING_TO_INLINE")
 | 
					inline fun <T, PR: PaginationResult<T>> PR.nextPageIfTrue(condition: PR.() -> Boolean) = if (condition()) {
 | 
				
			||||||
inline fun PaginationResult<*>.nextPageIfNotEmpty() = if (results.isNotEmpty()) {
 | 
					 | 
				
			||||||
    SimplePagination(
 | 
					    SimplePagination(
 | 
				
			||||||
        page + 1,
 | 
					        page + 1,
 | 
				
			||||||
        size
 | 
					        size
 | 
				
			||||||
@@ -20,12 +19,28 @@ inline fun PaginationResult<*>.nextPageIfNotEmpty() = if (results.isNotEmpty())
 | 
				
			|||||||
    null
 | 
					    null
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Suppress("NOTHING_TO_INLINE")
 | 
					inline fun <T, PR: PaginationResult<T>> PR.thisPageIfTrue(condition: PR.() -> Boolean): PR? = if (condition()) {
 | 
				
			||||||
inline fun <T> PaginationResult<T>.thisPageIfNotEmpty(): PaginationResult<T>? = if (results.isNotEmpty()) {
 | 
					 | 
				
			||||||
    this
 | 
					    this
 | 
				
			||||||
} else {
 | 
					} else {
 | 
				
			||||||
    null
 | 
					    null
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Suppress("NOTHING_TO_INLINE")
 | 
					fun PaginationResult<*>.nextPageIfNotEmpty() = nextPageIfTrue { results.isNotEmpty() }
 | 
				
			||||||
inline fun <T> PaginationResult<T>.currentPageIfNotEmpty() = thisPageIfNotEmpty()
 | 
					
 | 
				
			||||||
 | 
					fun <T> PaginationResult<T>.thisPageIfNotEmpty(): PaginationResult<T>? = thisPageIfTrue { results.isNotEmpty() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun <T> PaginationResult<T>.currentPageIfNotEmpty() = thisPageIfNotEmpty()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun PaginationResult<*>.nextPageIfNotEmptyOrLastPage() = nextPageIfTrue { results.isNotEmpty() && !this.isLastPage }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun <T> PaginationResult<T>.thisPageIfNotEmptyOrLastPage(): PaginationResult<T>? = thisPageIfTrue  { results.isNotEmpty() && !this.isLastPage }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun <T> PaginationResult<T>.currentPageIfNotEmptyOrLastPage() = thisPageIfNotEmptyOrLastPage()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun PaginationResult<*>.nextPageIfNotLastPage() = nextPageIfTrue { !this.isLastPage }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun <T> PaginationResult<T>.thisPageIfNotLastPage(): PaginationResult<T>? = thisPageIfTrue  { !this.isLastPage }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun <T> PaginationResult<T>.currentPageIfNotLastPage() = thisPageIfNotLastPage()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ inline fun <T> doForAllWithNextPaging(
 | 
				
			|||||||
) {
 | 
					) {
 | 
				
			||||||
    doForAll(
 | 
					    doForAll(
 | 
				
			||||||
        initialPagination,
 | 
					        initialPagination,
 | 
				
			||||||
        { it.nextPageIfNotEmpty() },
 | 
					        { it.nextPageIfNotEmptyOrLastPage() },
 | 
				
			||||||
        block
 | 
					        block
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -29,7 +29,7 @@ inline fun <T> doAllWithCurrentPaging(
 | 
				
			|||||||
) {
 | 
					) {
 | 
				
			||||||
    doForAll(
 | 
					    doForAll(
 | 
				
			||||||
        initialPagination,
 | 
					        initialPagination,
 | 
				
			||||||
        { it.currentPageIfNotEmpty() },
 | 
					        { it.nextPageIfNotEmptyOrLastPage() },
 | 
				
			||||||
        block
 | 
					        block
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,7 +31,7 @@ inline fun <T> getAllWithNextPaging(
 | 
				
			|||||||
    block: (Pagination) -> PaginationResult<T>
 | 
					    block: (Pagination) -> PaginationResult<T>
 | 
				
			||||||
): List<T> = getAll(
 | 
					): List<T> = getAll(
 | 
				
			||||||
    initialPagination,
 | 
					    initialPagination,
 | 
				
			||||||
    { it.nextPageIfNotEmpty() },
 | 
					    { it.nextPageIfNotEmptyOrLastPage() },
 | 
				
			||||||
    block
 | 
					    block
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -48,7 +48,7 @@ inline fun <T> getAllWithCurrentPaging(
 | 
				
			|||||||
    block: (Pagination) -> PaginationResult<T>
 | 
					    block: (Pagination) -> PaginationResult<T>
 | 
				
			||||||
): List<T> = getAll(
 | 
					): List<T> = getAll(
 | 
				
			||||||
    initialPagination,
 | 
					    initialPagination,
 | 
				
			||||||
    { it.currentPageIfNotEmpty() },
 | 
					    { it.thisPageIfNotEmptyOrLastPage() },
 | 
				
			||||||
    block
 | 
					    block
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,33 +21,25 @@ fun <T> Iterable<T>.paginate(with: Pagination): PaginationResult<T> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun <T> List<T>.paginate(with: Pagination): PaginationResult<T> {
 | 
					fun <T> List<T>.paginate(with: Pagination): PaginationResult<T> {
 | 
				
			||||||
    val firstIndex = maxOf(with.firstIndex, 0)
 | 
					    if (with.firstIndex >= size || with.lastIndex < 0) {
 | 
				
			||||||
    val lastIndex = minOf(with.lastIndexExclusive, size)
 | 
					        return emptyPaginationResult(with, size.toLong())
 | 
				
			||||||
    if (firstIndex > lastIndex) {
 | 
					 | 
				
			||||||
        return emptyPaginationResult()
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return subList(firstIndex, lastIndex).createPaginationResult(
 | 
					    return asSequence().drop(with.firstIndex).take(with.size).toList().createPaginationResult(
 | 
				
			||||||
        with,
 | 
					        with,
 | 
				
			||||||
        size.toLong()
 | 
					        size.toLong()
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun <T> List<T>.paginate(with: Pagination, reversed: Boolean): PaginationResult<T> {
 | 
					fun <T> List<T>.paginate(with: Pagination, reversed: Boolean): PaginationResult<T> {
 | 
				
			||||||
 | 
					    return if (reversed) {
 | 
				
			||||||
        val actualPagination = with.optionallyReverse(
 | 
					        val actualPagination = with.optionallyReverse(
 | 
				
			||||||
            size,
 | 
					            size,
 | 
				
			||||||
            reversed
 | 
					            reversed
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        paginate(actualPagination).changeResultsUnchecked { results.reversed() }
 | 
				
			||||||
    val firstIndex = maxOf(actualPagination.firstIndex, 0)
 | 
					    } else {
 | 
				
			||||||
    val lastIndex = minOf(actualPagination.lastIndexExclusive, size)
 | 
					        paginate(with)
 | 
				
			||||||
    if (firstIndex > lastIndex) {
 | 
					 | 
				
			||||||
        return emptyPaginationResult()
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return subList(firstIndex, lastIndex).optionallyReverse(reversed).createPaginationResult(
 | 
					 | 
				
			||||||
        with,
 | 
					 | 
				
			||||||
        size.toLong()
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun <T> Set<T>.paginate(with: Pagination): PaginationResult<T> {
 | 
					fun <T> Set<T>.paginate(with: Pagination): PaginationResult<T> {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					package dev.inmo.micro_utils.pagination.utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.pagination.*
 | 
				
			||||||
 | 
					import kotlin.test.Test
 | 
				
			||||||
 | 
					import kotlin.test.assertEquals
 | 
				
			||||||
 | 
					import kotlin.test.assertFalse
 | 
				
			||||||
 | 
					import kotlin.test.assertTrue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PaginationPaging {
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testPaginateOnList() {
 | 
				
			||||||
 | 
					        val list = (0 until 7).toList()
 | 
				
			||||||
 | 
					        val startPagination = FirstPagePagination(2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var lastPageHappened = false
 | 
				
			||||||
 | 
					        doForAllWithNextPaging(startPagination) {
 | 
				
			||||||
 | 
					            val result = list.paginate(it)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (result.isLastPage) {
 | 
				
			||||||
 | 
					                lastPageHappened = true
 | 
				
			||||||
 | 
					                assertTrue(result.results.size == 1)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val testSublist = list.subList(it.firstIndex, minOf(it.lastIndexExclusive, list.size))
 | 
				
			||||||
 | 
					            assertEquals(result.results, testSublist)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(lastPageHappened)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testEmptyPaginateOnList() {
 | 
				
			||||||
 | 
					        val list = listOf<Int>()
 | 
				
			||||||
 | 
					        val startPagination = FirstPagePagination(2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var paginationHappend = false
 | 
				
			||||||
 | 
					        doForAllWithNextPaging(startPagination) {
 | 
				
			||||||
 | 
					            val resultPagination = list.paginate(it)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assertEquals(resultPagination, emptyPaginationResult(it, list.size))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assertFalse(paginationHappend)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            paginationHappend = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            resultPagination
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(paginationHappend)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testRightOutPaginateOnList() {
 | 
				
			||||||
 | 
					        val list = (0 until 7).toList()
 | 
				
			||||||
 | 
					        val startPagination = SimplePagination(page = 4, size = 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var paginationHappend = false
 | 
				
			||||||
 | 
					        doForAllWithNextPaging(startPagination) {
 | 
				
			||||||
 | 
					            val resultPagination = list.paginate(it)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assertEquals(resultPagination, emptyPaginationResult(it, list.size))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assertFalse(paginationHappend)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            paginationHappend = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            resultPagination
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(paginationHappend)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,6 +5,7 @@ import dev.inmo.micro_utils.coroutines.withReadAcquire
 | 
				
			|||||||
import dev.inmo.micro_utils.coroutines.withWriteLock
 | 
					import dev.inmo.micro_utils.coroutines.withWriteLock
 | 
				
			||||||
import dev.inmo.micro_utils.repos.*
 | 
					import dev.inmo.micro_utils.repos.*
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.cache.KVCache
 | 
					import dev.inmo.micro_utils.repos.cache.cache.KVCache
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.cache.util.ActualizeAllClearMode
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
					import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
@@ -29,7 +30,7 @@ open class ReadCRUDCacheRepo<ObjectType, IdType>(
 | 
				
			|||||||
            kvCache.getAll()
 | 
					            kvCache.getAll()
 | 
				
			||||||
        }.takeIf { it.size.toLong() == count() } ?: parentRepo.getAll().also {
 | 
					        }.takeIf { it.size.toLong() == count() } ?: parentRepo.getAll().also {
 | 
				
			||||||
            locker.withWriteLock {
 | 
					            locker.withWriteLock {
 | 
				
			||||||
                kvCache.actualizeAll(true) { it }
 | 
					                kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -148,7 +149,9 @@ open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
 | 
				
			|||||||
    locker,
 | 
					    locker,
 | 
				
			||||||
    idGetter
 | 
					    idGetter
 | 
				
			||||||
),
 | 
					),
 | 
				
			||||||
    CRUDRepo<ObjectType, IdType, InputValueType>
 | 
					    CRUDRepo<ObjectType, IdType, InputValueType> {
 | 
				
			||||||
 | 
					    override suspend fun invalidate() = kvCache.actualizeAll(parentRepo, locker = locker)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
 | 
					fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
 | 
				
			||||||
    kvCache: KVCache<IdType, ObjectType>,
 | 
					    kvCache: KVCache<IdType, ObjectType>,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,11 @@
 | 
				
			|||||||
package dev.inmo.micro_utils.repos.cache
 | 
					package dev.inmo.micro_utils.repos.cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface CacheRepo {
 | 
					interface InvalidatableRepo {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Invalidates its internal data. It __may__ lead to autoreload of data. In case when repo makes autoreload,
 | 
				
			||||||
 | 
					     * it must do loading of data __before__ clear
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    suspend fun invalidate()
 | 
					    suspend fun invalidate()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typealias CacheRepo = InvalidatableRepo
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ import dev.inmo.micro_utils.coroutines.withWriteLock
 | 
				
			|||||||
import dev.inmo.micro_utils.pagination.*
 | 
					import dev.inmo.micro_utils.pagination.*
 | 
				
			||||||
import dev.inmo.micro_utils.repos.*
 | 
					import dev.inmo.micro_utils.repos.*
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.cache.KVCache
 | 
					import dev.inmo.micro_utils.repos.cache.cache.KVCache
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.flow.*
 | 
					import kotlinx.coroutines.flow.*
 | 
				
			||||||
@@ -48,9 +49,7 @@ open class ReadKeyValueCacheRepo<Key,Value>(
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun invalidate() = locker.withWriteLock {
 | 
					    override suspend fun invalidate() = kvCache.actualizeAll(parentRepo, locker = locker)
 | 
				
			||||||
        kvCache.clear()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached(
 | 
					fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached(
 | 
				
			||||||
@@ -75,10 +74,6 @@ open class KeyValueCacheRepo<Key,Value>(
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }.launchIn(scope)
 | 
					    }.launchIn(scope)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun invalidate() = locker.withWriteLock {
 | 
					 | 
				
			||||||
        kvCache.clear()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override suspend fun clear() {
 | 
					    override suspend fun clear() {
 | 
				
			||||||
        parentRepo.clear()
 | 
					        parentRepo.clear()
 | 
				
			||||||
        locker.withWriteLock {
 | 
					        locker.withWriteLock {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ import dev.inmo.micro_utils.pagination.*
 | 
				
			|||||||
import dev.inmo.micro_utils.pagination.utils.*
 | 
					import dev.inmo.micro_utils.pagination.utils.*
 | 
				
			||||||
import dev.inmo.micro_utils.repos.*
 | 
					import dev.inmo.micro_utils.repos.*
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.cache.KVCache
 | 
					import dev.inmo.micro_utils.repos.cache.cache.KVCache
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.flow.*
 | 
					import kotlinx.coroutines.flow.*
 | 
				
			||||||
@@ -47,9 +48,7 @@ open class ReadKeyValuesCacheRepo<Key,Value>(
 | 
				
			|||||||
        kvCache.contains(k)
 | 
					        kvCache.contains(k)
 | 
				
			||||||
    } || parentRepo.contains(k)
 | 
					    } || parentRepo.contains(k)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun invalidate() = locker.withWriteLock {
 | 
					    override suspend fun invalidate() = kvCache.actualizeAll(parentRepo, locker = locker)
 | 
				
			||||||
        kvCache.clear()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
 | 
					fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
 | 
				
			||||||
@@ -84,10 +83,6 @@ open class KeyValuesCacheRepo<Key,Value>(
 | 
				
			|||||||
            kvCache.unset(it)
 | 
					            kvCache.unset(it)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }.launchIn(scope)
 | 
					    }.launchIn(scope)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    override suspend fun invalidate() = locker.withWriteLock {
 | 
					 | 
				
			||||||
        kvCache.clear()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun <Key, Value> KeyValuesRepo<Key, Value>.cached(
 | 
					fun <Key, Value> KeyValuesRepo<Key, Value>.cached(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ import dev.inmo.micro_utils.repos.ReadCRUDRepo
 | 
				
			|||||||
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
 | 
					import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
					import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
 | 
					import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.cache.util.ActualizeAllClearMode
 | 
				
			||||||
import dev.inmo.micro_utils.repos.set
 | 
					import dev.inmo.micro_utils.repos.set
 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
import kotlinx.coroutines.delay
 | 
					import kotlinx.coroutines.delay
 | 
				
			||||||
@@ -56,7 +57,7 @@ open class AutoRecacheReadCRUDRepo<RegisteredObject, Id>(
 | 
				
			|||||||
    override suspend fun getAll(): Map<Id, RegisteredObject> = actionWrapper.wrap {
 | 
					    override suspend fun getAll(): Map<Id, RegisteredObject> = actionWrapper.wrap {
 | 
				
			||||||
        originalRepo.getAll()
 | 
					        originalRepo.getAll()
 | 
				
			||||||
    }.onSuccess {
 | 
					    }.onSuccess {
 | 
				
			||||||
        kvCache.actualizeAll(clear = true) { it }
 | 
					        kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it }
 | 
				
			||||||
    }.getOrElse {
 | 
					    }.getOrElse {
 | 
				
			||||||
        kvCache.getAll()
 | 
					        kvCache.getAll()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ import dev.inmo.micro_utils.repos.ReadKeyValueRepo
 | 
				
			|||||||
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
 | 
					import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
					import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
 | 
					import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.cache.util.ActualizeAllClearMode
 | 
				
			||||||
import dev.inmo.micro_utils.repos.set
 | 
					import dev.inmo.micro_utils.repos.set
 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
import kotlinx.coroutines.delay
 | 
					import kotlinx.coroutines.delay
 | 
				
			||||||
@@ -56,7 +57,7 @@ open class AutoRecacheReadKeyValueRepo<Id, RegisteredObject>(
 | 
				
			|||||||
    override suspend fun getAll(): Map<Id, RegisteredObject> = actionWrapper.wrap {
 | 
					    override suspend fun getAll(): Map<Id, RegisteredObject> = actionWrapper.wrap {
 | 
				
			||||||
        originalRepo.getAll()
 | 
					        originalRepo.getAll()
 | 
				
			||||||
    }.onSuccess {
 | 
					    }.onSuccess {
 | 
				
			||||||
        kvCache.actualizeAll(clear = true) { it }
 | 
					        kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it }
 | 
				
			||||||
    }.getOrElse {
 | 
					    }.getOrElse {
 | 
				
			||||||
        kvCache.getAll()
 | 
					        kvCache.getAll()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ import dev.inmo.micro_utils.pagination.Pagination
 | 
				
			|||||||
import dev.inmo.micro_utils.pagination.PaginationResult
 | 
					import dev.inmo.micro_utils.pagination.PaginationResult
 | 
				
			||||||
import dev.inmo.micro_utils.repos.*
 | 
					import dev.inmo.micro_utils.repos.*
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.*
 | 
					import dev.inmo.micro_utils.repos.cache.*
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.cache.util.ActualizeAllClearMode
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
					import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
@@ -42,7 +43,7 @@ open class FullReadCRUDCacheRepo<ObjectType, IdType>(
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected open suspend fun actualizeAll() {
 | 
					    protected open suspend fun actualizeAll() {
 | 
				
			||||||
        locker.withWriteLock { kvCache.actualizeAll(parentRepo) }
 | 
					        kvCache.actualizeAll(parentRepo, locker = locker)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = doOrTakeAndActualize(
 | 
					    override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = doOrTakeAndActualize(
 | 
				
			||||||
@@ -72,7 +73,7 @@ open class FullReadCRUDCacheRepo<ObjectType, IdType>(
 | 
				
			|||||||
    override suspend fun getAll(): Map<IdType, ObjectType> = doOrTakeAndActualizeWithWriteLock(
 | 
					    override suspend fun getAll(): Map<IdType, ObjectType> = doOrTakeAndActualizeWithWriteLock(
 | 
				
			||||||
        { getAll().takeIf { it.isNotEmpty() }.optionalOrAbsentIfNull },
 | 
					        { getAll().takeIf { it.isNotEmpty() }.optionalOrAbsentIfNull },
 | 
				
			||||||
        { getAll() },
 | 
					        { getAll() },
 | 
				
			||||||
        { kvCache.actualizeAll(clear = true) { it } }
 | 
					        { kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it } }
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun getById(id: IdType): ObjectType? = doOrTakeAndActualizeWithWriteLock(
 | 
					    override suspend fun getById(id: IdType): ObjectType? = doOrTakeAndActualizeWithWriteLock(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,8 +8,8 @@ import dev.inmo.micro_utils.coroutines.withWriteLock
 | 
				
			|||||||
import dev.inmo.micro_utils.pagination.Pagination
 | 
					import dev.inmo.micro_utils.pagination.Pagination
 | 
				
			||||||
import dev.inmo.micro_utils.pagination.PaginationResult
 | 
					import dev.inmo.micro_utils.pagination.PaginationResult
 | 
				
			||||||
import dev.inmo.micro_utils.repos.*
 | 
					import dev.inmo.micro_utils.repos.*
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.cache.util.ActualizeAllClearMode
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
					import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
				
			||||||
import dev.inmo.micro_utils.repos.pagination.getAll
 | 
					 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.flow.*
 | 
					import kotlinx.coroutines.flow.*
 | 
				
			||||||
@@ -41,10 +41,7 @@ open class FullReadKeyValueCacheRepo<Key,Value>(
 | 
				
			|||||||
        actualize = { locker.withWriteLock { actualize(it) } }
 | 
					        actualize = { locker.withWriteLock { actualize(it) } }
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    protected open suspend fun actualizeAll() {
 | 
					    protected open suspend fun actualizeAll() {
 | 
				
			||||||
        locker.withWriteLock {
 | 
					        kvCache.actualizeAll(parentRepo, locker)
 | 
				
			||||||
            kvCache.clear()
 | 
					 | 
				
			||||||
            kvCache.set(parentRepo.getAll())
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun get(k: Key): Value? = doOrTakeAndActualizeWithWriteLock(
 | 
					    override suspend fun get(k: Key): Value? = doOrTakeAndActualizeWithWriteLock(
 | 
				
			||||||
@@ -74,7 +71,7 @@ open class FullReadKeyValueCacheRepo<Key,Value>(
 | 
				
			|||||||
    override suspend fun getAll(): Map<Key, Value> = doOrTakeAndActualizeWithWriteLock(
 | 
					    override suspend fun getAll(): Map<Key, Value> = doOrTakeAndActualizeWithWriteLock(
 | 
				
			||||||
        { getAll().takeIf { it.isNotEmpty() }.optionalOrAbsentIfNull },
 | 
					        { getAll().takeIf { it.isNotEmpty() }.optionalOrAbsentIfNull },
 | 
				
			||||||
        { getAll() },
 | 
					        { getAll() },
 | 
				
			||||||
        { kvCache.actualizeAll(clear = true) { it } }
 | 
					        { kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it } }
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = doOrTakeAndActualize(
 | 
					    override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = doOrTakeAndActualize(
 | 
				
			||||||
@@ -150,9 +147,7 @@ open class FullKeyValueCacheRepo<Key,Value>(
 | 
				
			|||||||
    override suspend fun unsetWithValues(toUnset: List<Value>) = parentRepo.unsetWithValues(toUnset)
 | 
					    override suspend fun unsetWithValues(toUnset: List<Value>) = parentRepo.unsetWithValues(toUnset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun invalidate() {
 | 
					    override suspend fun invalidate() {
 | 
				
			||||||
        locker.withWriteLock {
 | 
					        kvCache.actualizeAll(parentRepo, locker)
 | 
				
			||||||
            kvCache.actualizeAll(parentRepo)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun clear() {
 | 
					    override suspend fun clear() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ import dev.inmo.micro_utils.coroutines.withWriteLock
 | 
				
			|||||||
import dev.inmo.micro_utils.pagination.*
 | 
					import dev.inmo.micro_utils.pagination.*
 | 
				
			||||||
import dev.inmo.micro_utils.pagination.utils.*
 | 
					import dev.inmo.micro_utils.pagination.utils.*
 | 
				
			||||||
import dev.inmo.micro_utils.repos.*
 | 
					import dev.inmo.micro_utils.repos.*
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.cache.util.ActualizeAllClearMode
 | 
				
			||||||
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
					import dev.inmo.micro_utils.repos.cache.util.actualizeAll
 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
@@ -41,15 +42,13 @@ open class FullReadKeyValuesCacheRepo<Key,Value>(
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected open suspend fun actualizeKey(k: Key) {
 | 
					    protected open suspend fun actualizeKey(k: Key) {
 | 
				
			||||||
        locker.withWriteLock {
 | 
					        kvCache.actualizeAll(locker = locker, clearMode = ActualizeAllClearMode.Never) {
 | 
				
			||||||
            kvCache.set(k, parentRepo.getAll(k))
 | 
					            mapOf(k to parentRepo.getAll(k))
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected open suspend fun actualizeAll() {
 | 
					    protected open suspend fun actualizeAll() {
 | 
				
			||||||
        locker.withWriteLock {
 | 
					        kvCache.actualizeAll(parentRepo, locker = locker)
 | 
				
			||||||
            kvCache.actualizeAll(parentRepo)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
 | 
					    override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
 | 
				
			||||||
@@ -187,9 +186,7 @@ open class FullKeyValuesCacheRepo<Key,Value>(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun invalidate() {
 | 
					    override suspend fun invalidate() {
 | 
				
			||||||
        locker.withWriteLock {
 | 
					        kvCache.actualizeAll(parentRepo, locker = locker)
 | 
				
			||||||
            kvCache.actualizeAll(parentRepo)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun set(toSet: Map<Key, List<Value>>) {
 | 
					    override suspend fun set(toSet: Map<Key, List<Value>>) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,43 +1,169 @@
 | 
				
			|||||||
package dev.inmo.micro_utils.repos.cache.util
 | 
					package dev.inmo.micro_utils.repos.cache.util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.coroutines.SmartRWLocker
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.coroutines.withWriteLock
 | 
				
			||||||
import dev.inmo.micro_utils.repos.*
 | 
					import dev.inmo.micro_utils.repos.*
 | 
				
			||||||
 | 
					import kotlinx.serialization.Serializable
 | 
				
			||||||
 | 
					import kotlin.js.JsName
 | 
				
			||||||
 | 
					import kotlin.jvm.JvmName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAll(
 | 
					/**
 | 
				
			||||||
    clear: Boolean = true,
 | 
					 * `invalidate`/`actualizeAll` clearing mode. Declare when data in original repo will be cleared
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Serializable
 | 
				
			||||||
 | 
					sealed interface ActualizeAllClearMode {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Instruct user of this mode to clear internal data __before load__ of external data
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Serializable
 | 
				
			||||||
 | 
					    data object BeforeLoad : ActualizeAllClearMode
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Instruct user of this mode to clear internal data __after load__ of external data and __before set__ of internal data
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Serializable
 | 
				
			||||||
 | 
					    data object BeforeSet : ActualizeAllClearMode
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Instruct user of this mode to never clear internal data
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Serializable
 | 
				
			||||||
 | 
					    data object Never : ActualizeAllClearMode
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAllWithClearBeforeLoad(
 | 
				
			||||||
    getAll: () -> Map<K, V>
 | 
					    getAll: () -> Map<K, V>
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    set(
 | 
					    clear()
 | 
				
			||||||
        getAll().also {
 | 
					    val newData = getAll()
 | 
				
			||||||
            if (clear) {
 | 
					    set(newData)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAllWithClearBeforeSet(
 | 
				
			||||||
 | 
					    getAll: () -> Map<K, V>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    val newData = getAll()
 | 
				
			||||||
 | 
					    clear()
 | 
				
			||||||
 | 
					    set(newData)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAllWithoutClear(
 | 
				
			||||||
 | 
					    getAll: () -> Map<K, V>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    val newData = getAll()
 | 
				
			||||||
 | 
					    set(newData)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@JvmName("actualizeAllWithClearBeforeLoadWithLocker")
 | 
				
			||||||
 | 
					@JsName("actualizeAllWithClearBeforeLoadWithLocker")
 | 
				
			||||||
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAllWithClearBeforeLoad(
 | 
				
			||||||
 | 
					    locker: SmartRWLocker,
 | 
				
			||||||
 | 
					    getAll: () -> Map<K, V>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    locker.withWriteLock {
 | 
				
			||||||
        clear()
 | 
					        clear()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val newData = getAll()
 | 
				
			||||||
 | 
					    locker.withWriteLock {
 | 
				
			||||||
 | 
					        set(newData)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    )
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@JvmName("actualizeAllWithClearBeforeSetWithLocker")
 | 
				
			||||||
 | 
					@JsName("actualizeAllWithClearBeforeSetWithLocker")
 | 
				
			||||||
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAllWithClearBeforeSet(
 | 
				
			||||||
 | 
					    locker: SmartRWLocker,
 | 
				
			||||||
 | 
					    getAll: () -> Map<K, V>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    val newData = getAll()
 | 
				
			||||||
 | 
					    locker.withWriteLock {
 | 
				
			||||||
 | 
					        clear()
 | 
				
			||||||
 | 
					        set(newData)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@JvmName("actualizeAllWithoutClearWithLocker")
 | 
				
			||||||
 | 
					@JsName("actualizeAllWithoutClearWithLocker")
 | 
				
			||||||
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAllWithoutClear(
 | 
				
			||||||
 | 
					    locker: SmartRWLocker,
 | 
				
			||||||
 | 
					    getAll: () -> Map<K, V>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    val newData = getAll()
 | 
				
			||||||
 | 
					    locker.withWriteLock {
 | 
				
			||||||
 | 
					        set(newData)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAllWithClearBeforeLoad(
 | 
				
			||||||
 | 
					    locker: SmartRWLocker? = null,
 | 
				
			||||||
 | 
					    getAll: () -> Map<K, V>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    locker ?.let {
 | 
				
			||||||
 | 
					        actualizeAllWithClearBeforeLoad(locker = locker, getAll)
 | 
				
			||||||
 | 
					    } ?: actualizeAllWithClearBeforeLoad(getAll)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAllWithClearBeforeSet(
 | 
				
			||||||
 | 
					    locker: SmartRWLocker? = null,
 | 
				
			||||||
 | 
					    getAll: () -> Map<K, V>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    locker ?.let {
 | 
				
			||||||
 | 
					        actualizeAllWithClearBeforeSet(locker = locker, getAll)
 | 
				
			||||||
 | 
					    } ?: actualizeAllWithClearBeforeSet(getAll)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAllWithoutClear(
 | 
				
			||||||
 | 
					    locker: SmartRWLocker? = null,
 | 
				
			||||||
 | 
					    getAll: () -> Map<K, V>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    locker ?.let {
 | 
				
			||||||
 | 
					        actualizeAllWithoutClear(locker = locker, getAll)
 | 
				
			||||||
 | 
					    } ?: actualizeAllWithoutClear(getAll)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAll(
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAll(
 | 
				
			||||||
    repo: ReadKeyValueRepo<K, V>,
 | 
					    locker: SmartRWLocker? = null,
 | 
				
			||||||
    clear: Boolean = true,
 | 
					    clearMode: ActualizeAllClearMode = ActualizeAllClearMode.BeforeSet,
 | 
				
			||||||
 | 
					    getAll: () -> Map<K, V>
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    actualizeAll(clear) {
 | 
					    when (clearMode) {
 | 
				
			||||||
        repo.getAll()
 | 
					        ActualizeAllClearMode.BeforeLoad -> locker ?.let {
 | 
				
			||||||
 | 
					            actualizeAllWithClearBeforeLoad(locker = locker, getAll)
 | 
				
			||||||
 | 
					        } ?: actualizeAllWithClearBeforeLoad(getAll)
 | 
				
			||||||
 | 
					        ActualizeAllClearMode.BeforeSet -> locker ?.let {
 | 
				
			||||||
 | 
					            actualizeAllWithClearBeforeSet(locker = locker, getAll)
 | 
				
			||||||
 | 
					        } ?: actualizeAllWithClearBeforeSet(getAll)
 | 
				
			||||||
 | 
					        ActualizeAllClearMode.Never -> locker ?.let {
 | 
				
			||||||
 | 
					            actualizeAllWithoutClear(locker = locker, getAll)
 | 
				
			||||||
 | 
					        } ?: actualizeAllWithoutClear(getAll)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAll(
 | 
				
			||||||
 | 
					    parentRepo: ReadKeyValueRepo<K, V>,
 | 
				
			||||||
 | 
					    locker: SmartRWLocker? = null,
 | 
				
			||||||
 | 
					    clearMode: ActualizeAllClearMode = ActualizeAllClearMode.BeforeSet,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    actualizeAll(locker, clearMode) {
 | 
				
			||||||
 | 
					        parentRepo.getAll()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
suspend inline fun <K, V> KeyValueRepo<K, List<V>>.actualizeAll(
 | 
					suspend inline fun <K, V> KeyValueRepo<K, List<V>>.actualizeAll(
 | 
				
			||||||
    repo: ReadKeyValuesRepo<K, V>,
 | 
					    parentRepo: ReadKeyValuesRepo<K, V>,
 | 
				
			||||||
    clear: Boolean = true,
 | 
					    locker: SmartRWLocker? = null,
 | 
				
			||||||
 | 
					    clearMode: ActualizeAllClearMode = ActualizeAllClearMode.BeforeSet,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    actualizeAll(clear) {
 | 
					    actualizeAll(locker, clearMode) {
 | 
				
			||||||
        repo.getAll()
 | 
					        parentRepo.getAll()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAll(
 | 
					suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAll(
 | 
				
			||||||
    repo: ReadCRUDRepo<V, K>,
 | 
					    parentRepo: ReadCRUDRepo<V, K>,
 | 
				
			||||||
    clear: Boolean = true,
 | 
					    locker: SmartRWLocker? = null,
 | 
				
			||||||
 | 
					    clearMode: ActualizeAllClearMode = ActualizeAllClearMode.BeforeSet,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    actualizeAll(clear) {
 | 
					    actualizeAll(locker, clearMode) {
 | 
				
			||||||
        repo.getAll()
 | 
					        parentRepo.getAll()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,6 @@ kotlin {
 | 
				
			|||||||
                api internalProject("micro_utils.common")
 | 
					                api internalProject("micro_utils.common")
 | 
				
			||||||
                api internalProject("micro_utils.coroutines")
 | 
					                api internalProject("micro_utils.coroutines")
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            dependsOn jvmMain
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,6 +42,22 @@ interface WriteCRUDRepo<ObjectType, IdType, InputValueType> : Repo {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
typealias WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> = WriteCRUDRepo<ObjectType, IdType, InputValueType>
 | 
					typealias WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> = WriteCRUDRepo<ObjectType, IdType, InputValueType>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Just mirroring [WriteCRUDRepo.newObjectsFlow] to be same as in KV repos
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					val <ObjectType> WriteCRUDRepo<ObjectType, *, *>.onNewObjects: Flow<ObjectType>
 | 
				
			||||||
 | 
					    get() = newObjectsFlow
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Just mirroring [WriteCRUDRepo.updatedObjectsFlow] to be same as in KV repos
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					val <ObjectType> WriteCRUDRepo<ObjectType, *, *>.onUpdatedObjects: Flow<ObjectType>
 | 
				
			||||||
 | 
					    get() = updatedObjectsFlow
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Just mirroring [WriteCRUDRepo.deletedObjectsIdsFlow] to be same as in KV repos
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					val <IdType> WriteCRUDRepo<*, IdType, *>.onDeletedObjectsIds: Flow<IdType>
 | 
				
			||||||
 | 
					    get() = deletedObjectsIdsFlow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
suspend fun <ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>.create(
 | 
					suspend fun <ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>.create(
 | 
				
			||||||
    vararg values: InputValueType
 | 
					    vararg values: InputValueType
 | 
				
			||||||
): List<ObjectType> = create(values.toList())
 | 
					): List<ObjectType> = create(values.toList())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					package dev.inmo.micro_utils.repos.diff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.common.MapDiff
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.common.applyDiff
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.common.diff
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.CRUDRepo
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.ReadCRUDRepo
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.unset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> ReadCRUDRepo<Registered, Id>.diff(other: Map<Id, Registered>): MapDiff<Id, Registered> {
 | 
				
			||||||
 | 
					    return getAll().diff(other)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> Map<Id, Registered>.diff(other: ReadCRUDRepo<Registered, Id>): MapDiff<Id, Registered> {
 | 
				
			||||||
 | 
					    return diff(other.getAll())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> MutableMap<Id, Registered>.applyDiff(other: ReadCRUDRepo<Registered, Id>) {
 | 
				
			||||||
 | 
					    applyDiff(diff(other))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					package dev.inmo.micro_utils.repos.diff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.common.MapDiff
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.common.applyDiff
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.common.diff
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.KeyValueRepo
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.ReadKeyValueRepo
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.unset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> ReadKeyValueRepo<Id, Registered>.diff(other: Map<Id, Registered>): MapDiff<Id, Registered> {
 | 
				
			||||||
 | 
					    return getAll().diff(other)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> Map<Id, Registered>.diff(other: ReadKeyValueRepo<Id, Registered>): MapDiff<Id, Registered> {
 | 
				
			||||||
 | 
					    return diff(other.getAll())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> KeyValueRepo<Id, Registered>.applyDiff(diff: MapDiff<Id, Registered>) {
 | 
				
			||||||
 | 
					    unset(diff.removed.map { it.key })
 | 
				
			||||||
 | 
					    set(
 | 
				
			||||||
 | 
					        diff.changed.map { (k, oldNew) ->
 | 
				
			||||||
 | 
					            k to oldNew.second
 | 
				
			||||||
 | 
					        }.toMap() + diff.added
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> KeyValueRepo<Id, Registered>.applyDiff(other: Map<Id, Registered>) {
 | 
				
			||||||
 | 
					    applyDiff(diff(other))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> MutableMap<Id, Registered>.applyDiff(other: ReadKeyValueRepo<Id, Registered>) {
 | 
				
			||||||
 | 
					    applyDiff(diff(other))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					package dev.inmo.micro_utils.repos.diff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.common.MapDiff
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.common.applyDiff
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.common.diff
 | 
				
			||||||
 | 
					import dev.inmo.micro_utils.repos.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> ReadKeyValuesRepo<Id, Registered>.diff(other: Map<Id, List<Registered>>): MapDiff<Id, List<Registered>> {
 | 
				
			||||||
 | 
					    return getAll().diff(other)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> Map<Id, List<Registered>>.diff(other: ReadKeyValuesRepo<Id, Registered>): MapDiff<Id, List<Registered>> {
 | 
				
			||||||
 | 
					    return diff(other.getAll())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> KeyValuesRepo<Id, Registered>.applyDiff(diff: MapDiff<Id, List<Registered>>) {
 | 
				
			||||||
 | 
					    diff.removed.forEach {
 | 
				
			||||||
 | 
					        clear(it.key)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    set(
 | 
				
			||||||
 | 
					        diff.changed.map { (k, oldNew) ->
 | 
				
			||||||
 | 
					            k to oldNew.second
 | 
				
			||||||
 | 
					        }.toMap() + diff.added
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> KeyValuesRepo<Id, Registered>.applyDiff(other: Map<Id, List<Registered>>) {
 | 
				
			||||||
 | 
					    applyDiff(diff(other))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun <Id, Registered> MutableMap<Id, List<Registered>>.applyDiff(other: ReadKeyValuesRepo<Id, Registered>) {
 | 
				
			||||||
 | 
					    applyDiff(diff(other))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
package dev.inmo.micro_utils.repos.exposed.keyvalue
 | 
					package dev.inmo.micro_utils.repos.exposed.keyvalue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dev.inmo.micro_utils.repos.KeyValueRepo
 | 
					import dev.inmo.micro_utils.repos.KeyValueRepo
 | 
				
			||||||
 | 
					import kotlinx.coroutines.channels.BufferOverflow
 | 
				
			||||||
import kotlinx.coroutines.flow.*
 | 
					import kotlinx.coroutines.flow.*
 | 
				
			||||||
import org.jetbrains.exposed.sql.*
 | 
					import org.jetbrains.exposed.sql.*
 | 
				
			||||||
import org.jetbrains.exposed.sql.statements.*
 | 
					import org.jetbrains.exposed.sql.statements.*
 | 
				
			||||||
@@ -8,13 +9,15 @@ import org.jetbrains.exposed.sql.transactions.transaction
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
abstract class AbstractExposedKeyValueRepo<Key, Value>(
 | 
					abstract class AbstractExposedKeyValueRepo<Key, Value>(
 | 
				
			||||||
    override val database: Database,
 | 
					    override val database: Database,
 | 
				
			||||||
    tableName: String? = null
 | 
					    tableName: String? = null,
 | 
				
			||||||
 | 
					    flowsExtraBufferCapacity: Int = Int.MAX_VALUE,
 | 
				
			||||||
 | 
					    flowsBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
 | 
				
			||||||
) : KeyValueRepo<Key, Value>, AbstractExposedReadKeyValueRepo<Key, Value>(
 | 
					) : KeyValueRepo<Key, Value>, AbstractExposedReadKeyValueRepo<Key, Value>(
 | 
				
			||||||
    database,
 | 
					    database,
 | 
				
			||||||
    tableName
 | 
					    tableName
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    protected val _onNewValue = MutableSharedFlow<Pair<Key, Value>>()
 | 
					    protected val _onNewValue = MutableSharedFlow<Pair<Key, Value>>(extraBufferCapacity = flowsExtraBufferCapacity, onBufferOverflow = flowsBufferOverflow)
 | 
				
			||||||
    protected val _onValueRemoved = MutableSharedFlow<Key>()
 | 
					    protected val _onValueRemoved = MutableSharedFlow<Key>(extraBufferCapacity = flowsExtraBufferCapacity, onBufferOverflow = flowsBufferOverflow)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
 | 
					    override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
 | 
				
			||||||
    override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()
 | 
					    override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package dev.inmo.micro_utils.repos.exposed.keyvalue
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import dev.inmo.micro_utils.repos.KeyValueRepo
 | 
					import dev.inmo.micro_utils.repos.KeyValueRepo
 | 
				
			||||||
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
 | 
					import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
 | 
				
			||||||
 | 
					import kotlinx.coroutines.channels.BufferOverflow
 | 
				
			||||||
import kotlinx.coroutines.flow.*
 | 
					import kotlinx.coroutines.flow.*
 | 
				
			||||||
import org.jetbrains.exposed.sql.*
 | 
					import org.jetbrains.exposed.sql.*
 | 
				
			||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
 | 
					import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
 | 
				
			||||||
@@ -13,15 +14,17 @@ open class ExposedKeyValueRepo<Key, Value>(
 | 
				
			|||||||
    database: Database,
 | 
					    database: Database,
 | 
				
			||||||
    keyColumnAllocator: ColumnAllocator<Key>,
 | 
					    keyColumnAllocator: ColumnAllocator<Key>,
 | 
				
			||||||
    valueColumnAllocator: ColumnAllocator<Value>,
 | 
					    valueColumnAllocator: ColumnAllocator<Value>,
 | 
				
			||||||
    tableName: String? = null
 | 
					    tableName: String? = null,
 | 
				
			||||||
 | 
					    flowsExtraBufferCapacity: Int = Int.MAX_VALUE,
 | 
				
			||||||
 | 
					    flowsBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
 | 
				
			||||||
) : KeyValueRepo<Key, Value>, ExposedReadKeyValueRepo<Key, Value>(
 | 
					) : KeyValueRepo<Key, Value>, ExposedReadKeyValueRepo<Key, Value>(
 | 
				
			||||||
    database,
 | 
					    database,
 | 
				
			||||||
    keyColumnAllocator,
 | 
					    keyColumnAllocator,
 | 
				
			||||||
    valueColumnAllocator,
 | 
					    valueColumnAllocator,
 | 
				
			||||||
    tableName
 | 
					    tableName
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    protected val _onNewValue = MutableSharedFlow<Pair<Key, Value>>()
 | 
					    protected val _onNewValue = MutableSharedFlow<Pair<Key, Value>>(extraBufferCapacity = flowsExtraBufferCapacity, onBufferOverflow = flowsBufferOverflow)
 | 
				
			||||||
    protected val _onValueRemoved = MutableSharedFlow<Key>()
 | 
					    protected val _onValueRemoved = MutableSharedFlow<Key>(extraBufferCapacity = flowsExtraBufferCapacity, onBufferOverflow = flowsBufferOverflow)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
 | 
					    override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
 | 
				
			||||||
    override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()
 | 
					    override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
package dev.inmo.micro_utils.repos.exposed.onetomany
 | 
					package dev.inmo.micro_utils.repos.exposed.onetomany
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dev.inmo.micro_utils.repos.KeyValuesRepo
 | 
					import dev.inmo.micro_utils.repos.KeyValuesRepo
 | 
				
			||||||
 | 
					import kotlinx.coroutines.channels.BufferOverflow
 | 
				
			||||||
import kotlinx.coroutines.flow.*
 | 
					import kotlinx.coroutines.flow.*
 | 
				
			||||||
import org.jetbrains.exposed.sql.*
 | 
					import org.jetbrains.exposed.sql.*
 | 
				
			||||||
import org.jetbrains.exposed.sql.statements.InsertStatement
 | 
					import org.jetbrains.exposed.sql.statements.InsertStatement
 | 
				
			||||||
@@ -8,18 +9,20 @@ import org.jetbrains.exposed.sql.transactions.transaction
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
abstract class AbstractExposedKeyValuesRepo<Key, Value>(
 | 
					abstract class AbstractExposedKeyValuesRepo<Key, Value>(
 | 
				
			||||||
    override val database: Database,
 | 
					    override val database: Database,
 | 
				
			||||||
    tableName: String? = null
 | 
					    tableName: String? = null,
 | 
				
			||||||
 | 
					    flowsExtraBufferCapacity: Int = Int.MAX_VALUE,
 | 
				
			||||||
 | 
					    flowsBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
 | 
				
			||||||
) : KeyValuesRepo<Key, Value>, AbstractExposedReadKeyValuesRepo<Key, Value>(
 | 
					) : KeyValuesRepo<Key, Value>, AbstractExposedReadKeyValuesRepo<Key, Value>(
 | 
				
			||||||
    database,
 | 
					    database,
 | 
				
			||||||
    tableName
 | 
					    tableName
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    protected val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
 | 
					    protected val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow(extraBufferCapacity = flowsExtraBufferCapacity, onBufferOverflow = flowsBufferOverflow)
 | 
				
			||||||
    override val onNewValue: Flow<Pair<Key, Value>>
 | 
					    override val onNewValue: Flow<Pair<Key, Value>>
 | 
				
			||||||
        get() = _onNewValue.asSharedFlow()
 | 
					        get() = _onNewValue.asSharedFlow()
 | 
				
			||||||
    protected val _onValueRemoved: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
 | 
					    protected val _onValueRemoved: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow(extraBufferCapacity = flowsExtraBufferCapacity, onBufferOverflow = flowsBufferOverflow)
 | 
				
			||||||
    override val onValueRemoved: Flow<Pair<Key, Value>>
 | 
					    override val onValueRemoved: Flow<Pair<Key, Value>>
 | 
				
			||||||
        get() = _onValueRemoved.asSharedFlow()
 | 
					        get() = _onValueRemoved.asSharedFlow()
 | 
				
			||||||
    protected val _onDataCleared: MutableSharedFlow<Key> = MutableSharedFlow()
 | 
					    protected val _onDataCleared: MutableSharedFlow<Key> = MutableSharedFlow(extraBufferCapacity = flowsExtraBufferCapacity, onBufferOverflow = flowsBufferOverflow)
 | 
				
			||||||
    override val onDataCleared: Flow<Key>
 | 
					    override val onDataCleared: Flow<Key>
 | 
				
			||||||
        get() = _onDataCleared.asSharedFlow()
 | 
					        get() = _onDataCleared.asSharedFlow()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package dev.inmo.micro_utils.repos.exposed.onetomany
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import dev.inmo.micro_utils.repos.KeyValuesRepo
 | 
					import dev.inmo.micro_utils.repos.KeyValuesRepo
 | 
				
			||||||
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
 | 
					import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
 | 
				
			||||||
 | 
					import kotlinx.coroutines.channels.BufferOverflow
 | 
				
			||||||
import kotlinx.coroutines.flow.*
 | 
					import kotlinx.coroutines.flow.*
 | 
				
			||||||
import org.jetbrains.exposed.sql.*
 | 
					import org.jetbrains.exposed.sql.*
 | 
				
			||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
 | 
					import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
 | 
				
			||||||
@@ -13,20 +14,22 @@ open class ExposedKeyValuesRepo<Key, Value>(
 | 
				
			|||||||
    database: Database,
 | 
					    database: Database,
 | 
				
			||||||
    keyColumnAllocator: ColumnAllocator<Key>,
 | 
					    keyColumnAllocator: ColumnAllocator<Key>,
 | 
				
			||||||
    valueColumnAllocator: ColumnAllocator<Value>,
 | 
					    valueColumnAllocator: ColumnAllocator<Value>,
 | 
				
			||||||
    tableName: String? = null
 | 
					    tableName: String? = null,
 | 
				
			||||||
 | 
					    flowsExtraBufferCapacity: Int = Int.MAX_VALUE,
 | 
				
			||||||
 | 
					    flowsBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
 | 
				
			||||||
) : KeyValuesRepo<Key, Value>, ExposedReadKeyValuesRepo<Key, Value>(
 | 
					) : KeyValuesRepo<Key, Value>, ExposedReadKeyValuesRepo<Key, Value>(
 | 
				
			||||||
    database,
 | 
					    database,
 | 
				
			||||||
    keyColumnAllocator,
 | 
					    keyColumnAllocator,
 | 
				
			||||||
    valueColumnAllocator,
 | 
					    valueColumnAllocator,
 | 
				
			||||||
    tableName
 | 
					    tableName
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    protected val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
 | 
					    protected val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow(extraBufferCapacity = flowsExtraBufferCapacity, onBufferOverflow = flowsBufferOverflow)
 | 
				
			||||||
    override val onNewValue: Flow<Pair<Key, Value>>
 | 
					    override val onNewValue: Flow<Pair<Key, Value>>
 | 
				
			||||||
        get() = _onNewValue
 | 
					        get() = _onNewValue
 | 
				
			||||||
    protected val _onValueRemoved: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
 | 
					    protected val _onValueRemoved: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow(extraBufferCapacity = flowsExtraBufferCapacity, onBufferOverflow = flowsBufferOverflow)
 | 
				
			||||||
    override val onValueRemoved: Flow<Pair<Key, Value>>
 | 
					    override val onValueRemoved: Flow<Pair<Key, Value>>
 | 
				
			||||||
        get() = _onValueRemoved
 | 
					        get() = _onValueRemoved
 | 
				
			||||||
    protected val _onDataCleared: MutableSharedFlow<Key> = MutableSharedFlow()
 | 
					    protected val _onDataCleared: MutableSharedFlow<Key> = MutableSharedFlow(extraBufferCapacity = flowsExtraBufferCapacity, onBufferOverflow = flowsBufferOverflow)
 | 
				
			||||||
    override val onDataCleared: Flow<Key>
 | 
					    override val onDataCleared: Flow<Key>
 | 
				
			||||||
        get() = _onDataCleared
 | 
					        get() = _onDataCleared
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,6 @@
 | 
				
			|||||||
package dev.inmo.micro_utils.repos.generator
 | 
					package dev.inmo.micro_utils.repos.generator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.devtools.ksp.KspExperimental
 | 
					import com.google.devtools.ksp.*
 | 
				
			||||||
import com.google.devtools.ksp.getAnnotationsByType
 | 
					 | 
				
			||||||
import com.google.devtools.ksp.isAnnotationPresent
 | 
					 | 
				
			||||||
import com.google.devtools.ksp.processing.CodeGenerator
 | 
					import com.google.devtools.ksp.processing.CodeGenerator
 | 
				
			||||||
import com.google.devtools.ksp.processing.Resolver
 | 
					import com.google.devtools.ksp.processing.Resolver
 | 
				
			||||||
import com.google.devtools.ksp.processing.SymbolProcessor
 | 
					import com.google.devtools.ksp.processing.SymbolProcessor
 | 
				
			||||||
@@ -36,6 +34,7 @@ import kotlinx.serialization.Serializable
 | 
				
			|||||||
import java.io.File
 | 
					import java.io.File
 | 
				
			||||||
import kotlin.reflect.KProperty1
 | 
					import kotlin.reflect.KProperty1
 | 
				
			||||||
import kotlin.reflect.full.memberProperties
 | 
					import kotlin.reflect.full.memberProperties
 | 
				
			||||||
 | 
					import kotlin.reflect.jvm.jvmName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun KSClassifierReference.quilifiedName(): String = "${qualifier ?.let { "${it.quilifiedName()}." } ?: ""}${referencedName()}"
 | 
					private fun KSClassifierReference.quilifiedName(): String = "${qualifier ?.let { "${it.quilifiedName()}." } ?: ""}${referencedName()}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,16 +74,29 @@ class Processor(
 | 
				
			|||||||
                        ORIGINAL FILE: ${ksFile.fileName}
 | 
					                        ORIGINAL FILE: ${ksFile.fileName}
 | 
				
			||||||
                    """.trimIndent()
 | 
					                    """.trimIndent()
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    val newName = "New${ksClassDeclaration.simpleName.getShortName()}"
 | 
					                    val newName = "New${ksClassDeclaration.simpleName.getShortName()}"
 | 
				
			||||||
                    val registeredName = "Registered${ksClassDeclaration.simpleName.getShortName()}"
 | 
					                    val registeredName = "Registered${ksClassDeclaration.simpleName.getShortName()}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    val allKSClassProperties = ksClassDeclaration.getAllProperties()
 | 
					                    val allKSClassProperties = ksClassDeclaration.getAllProperties()
 | 
				
			||||||
                    val excludedKSClassProperties = allKSClassProperties.filter {
 | 
					                    val excludedKSClassProperties = allKSClassProperties.filter { property ->
 | 
				
			||||||
                        it.isAnnotationPresent(GenerateCRUDModelExcludeOverride::class)
 | 
					                        property.isAnnotationPresent(GenerateCRUDModelExcludeOverride::class) || (property.findOverridee() ?.isAnnotationPresent(GenerateCRUDModelExcludeOverride::class) == true)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    val excludedKSClassPropertiesNames = excludedKSClassProperties.map { it.simpleName.asString() }
 | 
					                    val excludedKSClassPropertiesNames = excludedKSClassProperties.map { it.simpleName.asString() }
 | 
				
			||||||
                    val ksClassProperties = allKSClassProperties.filter {
 | 
					                    val ksClassProperties = allKSClassProperties.filter {
 | 
				
			||||||
                        it !in excludedKSClassProperties
 | 
					                        it !in excludedKSClassProperties
 | 
				
			||||||
 | 
					                    }.groupBy { it.simpleName.asString() }.map {
 | 
				
			||||||
 | 
					                        var current = it.value.first()
 | 
				
			||||||
 | 
					                        var currentType = current.type.resolve()
 | 
				
			||||||
 | 
					                        it.value.forEach {
 | 
				
			||||||
 | 
					                            val type = it.type.resolve()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if (currentType.isAssignableFrom(type) && !type.isAssignableFrom(currentType)) {
 | 
				
			||||||
 | 
					                                current = it
 | 
				
			||||||
 | 
					                                currentType = type
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        current
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    val ksClassPropertiesNames = ksClassProperties.map { it.simpleName.asString() }
 | 
					                    val ksClassPropertiesNames = ksClassProperties.map { it.simpleName.asString() }
 | 
				
			||||||
                    val newNewType = TypeSpec.classBuilder(newName).apply {
 | 
					                    val newNewType = TypeSpec.classBuilder(newName).apply {
 | 
				
			||||||
@@ -99,17 +111,20 @@ class Processor(
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        primaryConstructor(
 | 
					                        primaryConstructor(
 | 
				
			||||||
                            FunSpec.constructorBuilder().apply {
 | 
					                            FunSpec.constructorBuilder().apply {
 | 
				
			||||||
 | 
					                                val withoutDefaults = mutableListOf<Pair<ParameterSpec.Builder, PropertySpec.Builder>>()
 | 
				
			||||||
                                ksClassProperties.forEach {
 | 
					                                ksClassProperties.forEach {
 | 
				
			||||||
                                    addParameter(
 | 
					                                    val property = PropertySpec.builder(it.simpleName.getShortName(), it.type.toTypeName(), KModifier.OVERRIDE).apply {
 | 
				
			||||||
                                        ParameterSpec.builder(it.simpleName.getShortName(), it.typeName).apply {
 | 
					 | 
				
			||||||
                                            annotations += it.annotations.map { it.toAnnotationSpec() }
 | 
					 | 
				
			||||||
                                        }.build()
 | 
					 | 
				
			||||||
                                    )
 | 
					 | 
				
			||||||
                                    typeBuilder.addProperty(
 | 
					 | 
				
			||||||
                                        PropertySpec.builder(it.simpleName.getShortName(), it.typeName, KModifier.OVERRIDE).apply {
 | 
					 | 
				
			||||||
                                        initializer(it.simpleName.getShortName())
 | 
					                                        initializer(it.simpleName.getShortName())
 | 
				
			||||||
                                        }.build()
 | 
					                                    }
 | 
				
			||||||
                                    )
 | 
					                                    ParameterSpec.builder(it.simpleName.getShortName(), it.type.toTypeName()).apply {
 | 
				
			||||||
 | 
					                                        withoutDefaults.add(this to property)
 | 
				
			||||||
 | 
					                                        annotations += it.annotations.map { it.toAnnotationSpec() }
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                withoutDefaults.forEach {
 | 
				
			||||||
 | 
					                                    addParameter(it.first.build())
 | 
				
			||||||
 | 
					                                    addProperty(it.second.build())
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }.build()
 | 
					                            }.build()
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
@@ -125,14 +140,25 @@ class Processor(
 | 
				
			|||||||
                        (it.arguments.first().value as List<KSType>).map { it.declaration as KSClassDeclaration }
 | 
					                        (it.arguments.first().value as List<KSType>).map { it.declaration as KSClassDeclaration }
 | 
				
			||||||
                    }.toList()
 | 
					                    }.toList()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    val registeredTypesProperties: List<KSPropertyDeclaration> = registeredSupertypes.flatMap { registeredType ->
 | 
					                    val registeredTypesProperties: List<KSPropertyDeclaration> = registeredSupertypes.flatMap { registeredType ->
 | 
				
			||||||
                        registeredType.getAllProperties()
 | 
					                        registeredType.getAllProperties()
 | 
				
			||||||
                    }.filter {
 | 
					                    }.filter {
 | 
				
			||||||
                        it.simpleName.asString() !in excludedKSClassPropertiesNames && it.getAnnotationsByType(GenerateCRUDModelExcludeOverride::class).none()
 | 
					                        it.simpleName.asString() !in excludedKSClassPropertiesNames && !it.isAnnotationPresent(GenerateCRUDModelExcludeOverride::class)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    val allProperties: List<KSPropertyDeclaration> = ksClassProperties.toList() + registeredTypesProperties
 | 
					                    val allProperties: List<KSPropertyDeclaration> = registeredTypesProperties + ksClassProperties.toList()
 | 
				
			||||||
                    val propertiesToOverrideInRegistered = allProperties.distinctBy { it.simpleName.asString() }.sortedBy { property ->
 | 
					                    val propertiesToOverrideInRegistered = allProperties.groupBy { it.simpleName.asString() }.map {
 | 
				
			||||||
 | 
					                        var current = it.value.first()
 | 
				
			||||||
 | 
					                        var currentType = current.type.resolve()
 | 
				
			||||||
 | 
					                        it.value.forEach {
 | 
				
			||||||
 | 
					                            val type = it.type.resolve()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if (currentType.isAssignableFrom(type) && !type.isAssignableFrom(currentType)) {
 | 
				
			||||||
 | 
					                                current = it
 | 
				
			||||||
 | 
					                                currentType = type
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        current
 | 
				
			||||||
 | 
					                    }.sortedBy { property ->
 | 
				
			||||||
                        val name = property.simpleName.asString()
 | 
					                        val name = property.simpleName.asString()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        ksClassPropertiesNames.indexOf(name).takeIf { it > -1 } ?.let {
 | 
					                        ksClassPropertiesNames.indexOf(name).takeIf { it > -1 } ?.let {
 | 
				
			||||||
@@ -156,17 +182,20 @@ class Processor(
 | 
				
			|||||||
                        addModifiers(KModifier.DATA)
 | 
					                        addModifiers(KModifier.DATA)
 | 
				
			||||||
                        primaryConstructor(
 | 
					                        primaryConstructor(
 | 
				
			||||||
                            FunSpec.constructorBuilder().apply {
 | 
					                            FunSpec.constructorBuilder().apply {
 | 
				
			||||||
 | 
					                                val withoutDefaults = mutableListOf<Pair<ParameterSpec.Builder, PropertySpec.Builder>>()
 | 
				
			||||||
                                propertiesToOverrideInRegistered.forEach {
 | 
					                                propertiesToOverrideInRegistered.forEach {
 | 
				
			||||||
                                    addParameter(
 | 
					                                    val property = PropertySpec.builder(it.simpleName.getShortName(), it.type.toTypeName(), KModifier.OVERRIDE).apply {
 | 
				
			||||||
                                        ParameterSpec.builder(it.simpleName.getShortName(), it.typeName).apply {
 | 
					 | 
				
			||||||
                                            annotations += it.annotations.map { it.toAnnotationSpec() }
 | 
					 | 
				
			||||||
                                        }.build()
 | 
					 | 
				
			||||||
                                    )
 | 
					 | 
				
			||||||
                                    typeBuilder.addProperty(
 | 
					 | 
				
			||||||
                                        PropertySpec.builder(it.simpleName.getShortName(), it.typeName, KModifier.OVERRIDE).apply {
 | 
					 | 
				
			||||||
                                        initializer(it.simpleName.getShortName())
 | 
					                                        initializer(it.simpleName.getShortName())
 | 
				
			||||||
                                        }.build()
 | 
					                                    }
 | 
				
			||||||
                                    )
 | 
					                                    ParameterSpec.builder(it.simpleName.getShortName(), it.type.toTypeName()).apply {
 | 
				
			||||||
 | 
					                                        withoutDefaults.add(this to property)
 | 
				
			||||||
 | 
					                                        annotations += it.annotations.map { it.toAnnotationSpec() }
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                withoutDefaults.forEach {
 | 
				
			||||||
 | 
					                                    addParameter(it.first.build())
 | 
				
			||||||
 | 
					                                    addProperty(it.second.build())
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }.build()
 | 
					                            }.build()
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
@@ -191,7 +220,7 @@ class Processor(
 | 
				
			|||||||
                        FunSpec.builder("asRegistered").apply {
 | 
					                        FunSpec.builder("asRegistered").apply {
 | 
				
			||||||
                            receiver(ksClassDeclaration.toClassName())
 | 
					                            receiver(ksClassDeclaration.toClassName())
 | 
				
			||||||
                            (registeredTypesProperties.filter { it.simpleName.asString() !in ksClassPropertiesNames }).forEach {
 | 
					                            (registeredTypesProperties.filter { it.simpleName.asString() !in ksClassPropertiesNames }).forEach {
 | 
				
			||||||
                                addParameter(it.simpleName.asString(), it.typeName)
 | 
					                                addParameter(it.simpleName.asString(), it.type.toTypeName())
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            addCode(
 | 
					                            addCode(
 | 
				
			||||||
                                CodeBlock.of(
 | 
					                                CodeBlock.of(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ public data class NewTest(
 | 
				
			|||||||
  override val property1: String,
 | 
					  override val property1: String,
 | 
				
			||||||
  override val property2: Int,
 | 
					  override val property2: Int,
 | 
				
			||||||
  @Serializable
 | 
					  @Serializable
 | 
				
			||||||
 | 
					  @SerialName(`value` = "custom_parent_name")
 | 
				
			||||||
  override val parent: ParentTypeId?,
 | 
					  override val parent: ParentTypeId?,
 | 
				
			||||||
) : Test
 | 
					) : Test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -24,6 +25,7 @@ public data class RegisteredTest(
 | 
				
			|||||||
  override val property1: String,
 | 
					  override val property1: String,
 | 
				
			||||||
  override val property2: Int,
 | 
					  override val property2: Int,
 | 
				
			||||||
  @Serializable
 | 
					  @Serializable
 | 
				
			||||||
 | 
					  @SerialName(`value` = "custom_parent_name")
 | 
				
			||||||
  override val parent: ParentTypeId?,
 | 
					  override val parent: ParentTypeId?,
 | 
				
			||||||
) : Test, IRegisteredTest
 | 
					) : Test, IRegisteredTest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package dev.inmo.micro_utils.repos.generator.test
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import dev.inmo.micro_utils.repos.annotations.GenerateCRUDModel
 | 
					import dev.inmo.micro_utils.repos.annotations.GenerateCRUDModel
 | 
				
			||||||
import dev.inmo.micro_utils.repos.annotations.GenerateCRUDModelExcludeOverride
 | 
					import dev.inmo.micro_utils.repos.annotations.GenerateCRUDModelExcludeOverride
 | 
				
			||||||
 | 
					import kotlinx.serialization.SerialName
 | 
				
			||||||
import kotlinx.serialization.Serializable
 | 
					import kotlinx.serialization.Serializable
 | 
				
			||||||
import kotlin.jvm.JvmInline
 | 
					import kotlin.jvm.JvmInline
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,6 +19,7 @@ sealed interface Test {
 | 
				
			|||||||
    val property1: String
 | 
					    val property1: String
 | 
				
			||||||
    val property2: Int
 | 
					    val property2: Int
 | 
				
			||||||
    @Serializable
 | 
					    @Serializable
 | 
				
			||||||
 | 
					    @SerialName("custom_parent_name")
 | 
				
			||||||
    val parent: ParentTypeId?
 | 
					    val parent: ParentTypeId?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @GenerateCRUDModelExcludeOverride
 | 
					    @GenerateCRUDModelExcludeOverride
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,9 +13,5 @@ kotlin {
 | 
				
			|||||||
                api project(":micro_utils.language_codes")
 | 
					                api project(":micro_utils.language_codes")
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        androidMain {
 | 
					 | 
				
			||||||
            dependsOn(jvmMain)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,6 +49,12 @@ String[] includes = [
 | 
				
			|||||||
    ":fsm:common",
 | 
					    ":fsm:common",
 | 
				
			||||||
    ":fsm:repos:common",
 | 
					    ":fsm:repos:common",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ":ksp:generator",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ":ksp:sealed",
 | 
				
			||||||
 | 
					    ":ksp:sealed:generator",
 | 
				
			||||||
 | 
					    ":ksp:sealed:generator:test",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ":dokka"
 | 
					    ":dokka"
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user