mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-10-29 19:20:26 +00:00 
			
		
		
		
	Compare commits
	
		
			31 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f104e9f352 | |||
| 393c9a7d06 | |||
| ea497ea488 | |||
|  | 5add89cad0 | ||
| 2db447d2ef | |||
| 31c83813e6 | |||
| 27483a282d | |||
| 48b816aa22 | |||
| 0065f94f52 | |||
| ccc0002eb2 | |||
| 15a2eee141 | |||
| b9faac71e5 | |||
| 0a4465de33 | |||
| f9dfd09628 | |||
| 8638d7afce | |||
| 8311793a43 | |||
| 0d552cfcd2 | |||
| 4b0f20dbd1 | |||
| cf531c949d | |||
| ba5c5f17d5 | |||
| 35825ad9b7 | |||
| b1eb26a89e | |||
| c9d04b6698 | |||
| 496133e014 | |||
| f2857ee2be | |||
| 22541f2d5e | |||
| e235c52b6c | |||
| e89b8c72dd | |||
| a374e53a3a | |||
| afb066c4ee | |||
| f05761d4a5 | 
							
								
								
									
										47
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,52 @@ | ||||
| # Changelog | ||||
|  | ||||
| ## 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 | ||||
|  | ||||
| * `Repos`: | ||||
|     * `Common`: | ||||
|         * Improve default `set` realization of `KeyValuesRepo` | ||||
|  | ||||
| ## 0.20.33 | ||||
|  | ||||
| * `Colors` | ||||
|     * `Common`: | ||||
|         * Add opportunity to use `HEXAColor` with `ahex` colors | ||||
|  | ||||
| ## 0.20.32 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Okio`: `3.7.0` -> `3.8.0` | ||||
| * `Resources`: | ||||
|     * Make `StringResource` serializable | ||||
|     * Add several variants of builder usages | ||||
|  | ||||
| ## 0.20.31 | ||||
|  | ||||
| * `Versions`: | ||||
|   | ||||
| @@ -12,16 +12,30 @@ import kotlin.math.floor | ||||
|  * * Red (0.5 capacity): `0xff000088u` | ||||
|  * | ||||
|  * Anyway it is recommended to use | ||||
|  * | ||||
|  * @param hexaUInt rgba [UInt] in format `0xFFEEBBAA` where FF - red, EE - green, BB - blue` and AA - alpha | ||||
|  */ | ||||
| @Serializable | ||||
| @JvmInline | ||||
| value class HEXAColor ( | ||||
|     val uint: UInt | ||||
|     val hexaUInt: UInt | ||||
| ) : Comparable<HEXAColor> { | ||||
|     /** | ||||
|      * @returns [hexaUInt] as a string with format `#FFEEBBAA` where FF - red, EE - green, BB - blue and AA - alpha | ||||
|      */ | ||||
|     val hexa: String | ||||
|         get() = "#${uint.toString(16).padStart(8, '0')}" | ||||
|         get() = "#${hexaUInt.toString(16).padStart(8, '0')}" | ||||
|  | ||||
|     /** | ||||
|      * @returns [hexaUInt] as a string with format `#FFEEBB` where FF - red, EE - green and BB - blue | ||||
|      */ | ||||
|     val hex: String | ||||
|         get() = hexa.take(7) | ||||
|     /** | ||||
|      * @returns [hexaUInt] as a string with format `#AAFFEEBB` where AA - alpha, FF - red, EE - green and BB - blue | ||||
|      */ | ||||
|     val ahex: String | ||||
|         get() = "#${a.toString(16).padStart(2, '2')}${hex.drop(1)}" | ||||
|     val rgba: String | ||||
|         get() = "rgba($r,$g,$b,${aOfOne.toString().take(5)})" | ||||
|     val rgb: String | ||||
| @@ -30,21 +44,25 @@ value class HEXAColor ( | ||||
|         get() = "#${r.shortPart()}${g.shortPart()}${b.shortPart()}" | ||||
|     val shortHexa: String | ||||
|         get() = "$shortHex${a.shortPart()}" | ||||
|     val rgbUInt: UInt | ||||
|         get() = (hexaUInt / 256u) | ||||
|     val rgbInt: Int | ||||
|         get() = (uint shr 2).toInt() | ||||
|         get() = rgbUInt.toInt() | ||||
|     val ahexUInt | ||||
|         get() = (a * 0x1000000).toUInt() + rgbUInt | ||||
|  | ||||
|     val r: Int | ||||
|         get() = ((uint and 0xff000000u) / 0x1000000u).toInt() | ||||
|         get() = ((hexaUInt and 0xff000000u) / 0x1000000u).toInt() | ||||
|     val g: Int | ||||
|         get() = ((uint and 0x00ff0000u) / 0x10000u).toInt() | ||||
|         get() = ((hexaUInt and 0x00ff0000u) / 0x10000u).toInt() | ||||
|     val b: Int | ||||
|         get() = ((uint and 0x0000ff00u) / 0x100u).toInt() | ||||
|         get() = ((hexaUInt and 0x0000ff00u) / 0x100u).toInt() | ||||
|     val a: Int | ||||
|         get() = ((uint and 0x000000ffu)).toInt() | ||||
|         get() = ((hexaUInt and 0x000000ffu)).toInt() | ||||
|     val aOfOne: Float | ||||
|         get() = a.toFloat() / (0xff) | ||||
|     init { | ||||
|         require(uint in 0u ..0xffffffffu) | ||||
|         require(hexaUInt in 0u ..0xffffffffu) | ||||
|     } | ||||
|  | ||||
|     constructor(r: Int, g: Int, b: Int, a: Int) : this( | ||||
| @@ -64,7 +82,7 @@ value class HEXAColor ( | ||||
|         return hexa | ||||
|     } | ||||
|  | ||||
|     override fun compareTo(other: HEXAColor): Int = (uint - other.uint).coerceIn(Int.MIN_VALUE.toUInt(), Int.MAX_VALUE.toLong().toUInt()).toInt() | ||||
|     override fun compareTo(other: HEXAColor): Int = (hexaUInt - other.hexaUInt).coerceIn(Int.MIN_VALUE.toUInt(), Int.MAX_VALUE.toLong().toUInt()).toInt() | ||||
|  | ||||
|     fun copy( | ||||
|         r: Int = this.r, | ||||
| @@ -121,6 +139,21 @@ value class HEXAColor ( | ||||
|             else -> color | ||||
|         }.lowercase().toUInt(16).let(::HEXAColor) | ||||
|  | ||||
|         /** | ||||
|          * Creates [HEXAColor] from [uint] presume it is in format `0xFFEEBBAA` where FF - red, EE - green, BB - blue` and AA - alpha | ||||
|          */ | ||||
|         fun fromHexa(uint: UInt) = HEXAColor(uint) | ||||
|  | ||||
|         /** | ||||
|          * Creates [HEXAColor] from [uint] presume it is in format `0xAAFFEEBB` where AA - alpha, FF - red, EE - green and BB - blue` | ||||
|          */ | ||||
|         fun fromAhex(uint: UInt) = HEXAColor( | ||||
|             a = ((uint and 0xff000000u) / 0x1000000u).toInt(), | ||||
|             r = ((uint and 0x00ff0000u) / 0x10000u).toInt(), | ||||
|             g = ((uint and 0x0000ff00u) / 0x100u).toInt(), | ||||
|             b = ((uint and 0x000000ffu)).toInt() | ||||
|         ) | ||||
|  | ||||
|         /** | ||||
|          * Parsing color from [color] | ||||
|          * | ||||
|   | ||||
| @@ -13,6 +13,9 @@ class HexColorTests { | ||||
|         val shortHexa: String, | ||||
|         val hex: String, | ||||
|         val hexa: String, | ||||
|         val ahex: String, | ||||
|         val ahexUInt: UInt, | ||||
|         val rgbUInt: UInt, | ||||
|         val rgb: String, | ||||
|         val rgba: String, | ||||
|         val r: Int, | ||||
| @@ -24,11 +27,14 @@ class HexColorTests { | ||||
|     val testColors: List<TestColor> | ||||
|         get() = listOf( | ||||
|             TestColor( | ||||
|                 color = HEXAColor(uint = 0xff0000ffu), | ||||
|                 color = HEXAColor(hexaUInt = 0xff0000ffu), | ||||
|                 shortHex = "#f00", | ||||
|                 shortHexa = "#f00f", | ||||
|                 hex = "#ff0000", | ||||
|                 hexa = "#ff0000ff", | ||||
|                 ahex = "#ffff0000", | ||||
|                 ahexUInt = 0xffff0000u, | ||||
|                 rgbUInt = 0xff0000u, | ||||
|                 rgb = "rgb(255,0,0)", | ||||
|                 rgba = "rgba(255,0,0,1.0)", | ||||
|                 r = 0xff, | ||||
| @@ -38,11 +44,14 @@ class HexColorTests { | ||||
|                 "rgba(255,0,0,1)", | ||||
|             ), | ||||
|             TestColor( | ||||
|                 color = HEXAColor(uint = 0x00ff00ffu), | ||||
|                 color = HEXAColor(hexaUInt = 0x00ff00ffu), | ||||
|                 shortHex = "#0f0", | ||||
|                 shortHexa = "#0f0f", | ||||
|                 hex = "#00ff00", | ||||
|                 hexa = "#00ff00ff", | ||||
|                 ahex = "#ff00ff00", | ||||
|                 ahexUInt = 0xff00ff00u, | ||||
|                 rgbUInt = 0x00ff00u, | ||||
|                 rgb = "rgb(0,255,0)", | ||||
|                 rgba = "rgba(0,255,0,1.0)", | ||||
|                 r = 0x00, | ||||
| @@ -57,6 +66,9 @@ class HexColorTests { | ||||
|                 shortHexa = "#00ff", | ||||
|                 hex = "#0000ff", | ||||
|                 hexa = "#0000ffff", | ||||
|                 ahex = "#ff0000ff", | ||||
|                 ahexUInt = 0xff0000ffu, | ||||
|                 rgbUInt = 0x0000ffu, | ||||
|                 rgb = "rgb(0,0,255)", | ||||
|                 rgba = "rgba(0,0,255,1.0)", | ||||
|                 r = 0x00, | ||||
| @@ -71,6 +83,9 @@ class HexColorTests { | ||||
|                 shortHexa = "#f008", | ||||
|                 hex = "#ff0000", | ||||
|                 hexa = "#ff000088", | ||||
|                 ahex = "#88ff0000", | ||||
|                 ahexUInt = 0x88ff0000u, | ||||
|                 rgbUInt = 0xff0000u, | ||||
|                 rgb = "rgb(255,0,0)", | ||||
|                 rgba = "rgba(255,0,0,0.533)", | ||||
|                 r = 0xff, | ||||
| @@ -84,6 +99,9 @@ class HexColorTests { | ||||
|                 shortHexa = "#0f08", | ||||
|                 hex = "#00ff00", | ||||
|                 hexa = "#00ff0088", | ||||
|                 ahex = "#8800ff00", | ||||
|                 ahexUInt = 0x8800ff00u, | ||||
|                 rgbUInt = 0x00ff00u, | ||||
|                 rgb = "rgb(0,255,0)", | ||||
|                 rgba = "rgba(0,255,0,0.533)", | ||||
|                 r = 0x00, | ||||
| @@ -97,6 +115,9 @@ class HexColorTests { | ||||
|                 shortHexa = "#00f8", | ||||
|                 hex = "#0000ff", | ||||
|                 hexa = "#0000ff88", | ||||
|                 ahex = "#880000ff", | ||||
|                 ahexUInt = 0x880000ffu, | ||||
|                 rgbUInt = 0x0000ffu, | ||||
|                 rgb = "rgb(0,0,255)", | ||||
|                 rgba = "rgba(0,0,255,0.533)", | ||||
|                 r = 0x00, | ||||
| @@ -110,6 +131,9 @@ class HexColorTests { | ||||
|                 shortHexa = "#f002", | ||||
|                 hex = "#ff0000", | ||||
|                 hexa = "#ff000022", | ||||
|                 ahex = "#22ff0000", | ||||
|                 ahexUInt = 0x22ff0000u, | ||||
|                 rgbUInt = 0xff0000u, | ||||
|                 rgb = "rgb(255,0,0)", | ||||
|                 rgba = "rgba(255,0,0,0.133)", | ||||
|                 r = 0xff, | ||||
| @@ -123,6 +147,9 @@ class HexColorTests { | ||||
|                 shortHexa = "#0f02", | ||||
|                 hex = "#00ff00", | ||||
|                 hexa = "#00ff0022", | ||||
|                 ahex = "#2200ff00", | ||||
|                 ahexUInt = 0x2200ff00u, | ||||
|                 rgbUInt = 0x00ff00u, | ||||
|                 rgb = "rgb(0,255,0)", | ||||
|                 rgba = "rgba(0,255,0,0.133)", | ||||
|                 r = 0x00, | ||||
| @@ -136,6 +163,9 @@ class HexColorTests { | ||||
|                 shortHexa = "#00f2", | ||||
|                 hex = "#0000ff", | ||||
|                 hexa = "#0000ff22", | ||||
|                 ahex = "#220000ff", | ||||
|                 ahexUInt = 0x220000ffu, | ||||
|                 rgbUInt = 0x0000ffu, | ||||
|                 rgb = "rgb(0,0,255)", | ||||
|                 rgba = "rgba(0,0,255,0.133)", | ||||
|                 r = 0x00, | ||||
| @@ -150,6 +180,9 @@ class HexColorTests { | ||||
|         testColors.forEach { | ||||
|             assertEquals(it.hex, it.color.hex) | ||||
|             assertEquals(it.hexa, it.color.hexa) | ||||
|             assertEquals(it.ahex, it.color.ahex) | ||||
|             assertEquals(it.rgbUInt, it.color.rgbUInt) | ||||
|             assertEquals(it.ahexUInt, it.color.ahexUInt) | ||||
|             assertEquals(it.shortHex, it.color.shortHex) | ||||
|             assertEquals(it.shortHexa, it.color.shortHexa) | ||||
|             assertEquals(it.rgb, it.color.rgb) | ||||
| @@ -158,6 +191,7 @@ class HexColorTests { | ||||
|             assertEquals(it.g, it.color.g) | ||||
|             assertEquals(it.b, it.color.b) | ||||
|             assertEquals(it.a, it.color.a) | ||||
|             assertEquals(it.color, HEXAColor.fromAhex(it.ahexUInt)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -167,7 +201,7 @@ class HexColorTests { | ||||
|             assertEquals(it.color.copy(aOfOne = 1f), HEXAColor.parseStringColor(it.hex)) | ||||
|             assertEquals(it.color, HEXAColor.parseStringColor(it.hexa)) | ||||
|             assertEquals(it.color.copy(aOfOne = 1f), HEXAColor.parseStringColor(it.rgb)) | ||||
|             assertTrue(it.color.uint.toInt() - HEXAColor.parseStringColor(it.rgba).uint.toInt() in -0x1 .. 0x1, ) | ||||
|             assertTrue(it.color.hexaUInt.toInt() - HEXAColor.parseStringColor(it.rgba).hexaUInt.toInt() in -0x1 .. 0x1, ) | ||||
|             assertEquals(it.color.copy(aOfOne = 1f), HEXAColor.parseStringColor(it.shortHex)) | ||||
|             assertEquals(it.color.copy(a = floor(it.color.a.toFloat() / 16).toInt() * 0x10), HEXAColor.parseStringColor(it.shortHexa)) | ||||
|         } | ||||
|   | ||||
| @@ -15,5 +15,5 @@ crypto_js_version=4.1.1 | ||||
| # Project data | ||||
|  | ||||
| group=dev.inmo | ||||
| version=0.20.31 | ||||
| android_code_version=237 | ||||
| version=0.20.38 | ||||
| android_code_version=244 | ||||
|   | ||||
| @@ -1,32 +1,32 @@ | ||||
| [versions] | ||||
|  | ||||
| kt = "1.9.22" | ||||
| kt-serialization = "1.6.2" | ||||
| kt-coroutines = "1.7.3" | ||||
| kt-serialization = "1.6.3" | ||||
| kt-coroutines = "1.8.0" | ||||
|  | ||||
| kslog = "1.3.2" | ||||
|  | ||||
| jb-compose = "1.5.12" | ||||
| jb-exposed = "0.47.0" | ||||
| jb-dokka = "1.9.10" | ||||
| jb-compose = "1.6.0" | ||||
| jb-exposed = "0.48.0" | ||||
| jb-dokka = "1.9.20" | ||||
|  | ||||
| korlibs = "5.3.1" | ||||
| korlibs = "5.3.2" | ||||
| uuid = "0.8.2" | ||||
|  | ||||
| ktor = "2.3.8" | ||||
| ktor = "2.3.9" | ||||
|  | ||||
| gh-release = "2.5.2" | ||||
|  | ||||
| koin = "3.5.3" | ||||
|  | ||||
| okio = "3.7.0" | ||||
| okio = "3.8.0" | ||||
|  | ||||
| ksp = "1.9.22-1.0.17" | ||||
| ksp = "1.9.22-1.0.18" | ||||
| kotlin-poet = "1.16.0" | ||||
|  | ||||
| versions = "0.51.0" | ||||
|  | ||||
| android-gradle = "8.2.2" | ||||
| android-gradle = "8.3.0" | ||||
| dexcount = "4.0.0" | ||||
|  | ||||
| android-coreKtx = "1.12.0" | ||||
| @@ -35,7 +35,7 @@ android-appCompat = "1.6.1" | ||||
| android-fragment = "1.6.2" | ||||
| android-espresso = "3.5.1" | ||||
| android-test = "1.1.5" | ||||
| android-compose-material3 = "1.1.2" | ||||
| android-compose-material3 = "1.2.0" | ||||
|  | ||||
| android-props-minSdk = "21" | ||||
| 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 | ||||
| distributionPath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import dev.inmo.micro_utils.coroutines.withReadAcquire | ||||
| import dev.inmo.micro_utils.coroutines.withWriteLock | ||||
| import dev.inmo.micro_utils.repos.* | ||||
| 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 kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| @@ -29,7 +30,7 @@ open class ReadCRUDCacheRepo<ObjectType, IdType>( | ||||
|             kvCache.getAll() | ||||
|         }.takeIf { it.size.toLong() == count() } ?: parentRepo.getAll().also { | ||||
|             locker.withWriteLock { | ||||
|                 kvCache.actualizeAll(true) { it } | ||||
|                 kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -148,7 +149,9 @@ open class CRUDCacheRepo<ObjectType, IdType, InputValueType>( | ||||
|     locker, | ||||
|     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( | ||||
|     kvCache: KVCache<IdType, ObjectType>, | ||||
|   | ||||
| @@ -1,5 +1,11 @@ | ||||
| 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() | ||||
| } | ||||
|  | ||||
| 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.repos.* | ||||
| 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.Dispatchers | ||||
| import kotlinx.coroutines.flow.* | ||||
| @@ -48,9 +49,7 @@ open class ReadKeyValueCacheRepo<Key,Value>( | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override suspend fun invalidate() = locker.withWriteLock { | ||||
|         kvCache.clear() | ||||
|     } | ||||
|     override suspend fun invalidate() = kvCache.actualizeAll(parentRepo, locker = locker) | ||||
| } | ||||
|  | ||||
| fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached( | ||||
| @@ -75,10 +74,6 @@ open class KeyValueCacheRepo<Key,Value>( | ||||
|         } | ||||
|     }.launchIn(scope) | ||||
|  | ||||
|     override suspend fun invalidate() = locker.withWriteLock { | ||||
|         kvCache.clear() | ||||
|     } | ||||
|  | ||||
|     override suspend fun clear() { | ||||
|         parentRepo.clear() | ||||
|         locker.withWriteLock { | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import dev.inmo.micro_utils.pagination.* | ||||
| import dev.inmo.micro_utils.pagination.utils.* | ||||
| import dev.inmo.micro_utils.repos.* | ||||
| import dev.inmo.micro_utils.repos.cache.cache.KVCache | ||||
| import dev.inmo.micro_utils.repos.cache.util.actualizeAll | ||||
| import kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.flow.* | ||||
| @@ -47,9 +48,7 @@ open class ReadKeyValuesCacheRepo<Key,Value>( | ||||
|         kvCache.contains(k) | ||||
|     } || parentRepo.contains(k) | ||||
|  | ||||
|     override suspend fun invalidate() = locker.withWriteLock { | ||||
|         kvCache.clear() | ||||
|     } | ||||
|     override suspend fun invalidate() = kvCache.actualizeAll(parentRepo, locker = locker) | ||||
| } | ||||
|  | ||||
| fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached( | ||||
| @@ -84,10 +83,6 @@ open class KeyValuesCacheRepo<Key,Value>( | ||||
|             kvCache.unset(it) | ||||
|         } | ||||
|     }.launchIn(scope) | ||||
|  | ||||
|     override suspend fun invalidate() = locker.withWriteLock { | ||||
|         kvCache.clear() | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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.util.actualizeAll | ||||
| 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 kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.delay | ||||
| @@ -56,7 +57,7 @@ open class AutoRecacheReadCRUDRepo<RegisteredObject, Id>( | ||||
|     override suspend fun getAll(): Map<Id, RegisteredObject> = actionWrapper.wrap { | ||||
|         originalRepo.getAll() | ||||
|     }.onSuccess { | ||||
|         kvCache.actualizeAll(clear = true) { it } | ||||
|         kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it } | ||||
|     }.getOrElse { | ||||
|         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.util.actualizeAll | ||||
| 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 kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.delay | ||||
| @@ -56,7 +57,7 @@ open class AutoRecacheReadKeyValueRepo<Id, RegisteredObject>( | ||||
|     override suspend fun getAll(): Map<Id, RegisteredObject> = actionWrapper.wrap { | ||||
|         originalRepo.getAll() | ||||
|     }.onSuccess { | ||||
|         kvCache.actualizeAll(clear = true) { it } | ||||
|         kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it } | ||||
|     }.getOrElse { | ||||
|         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.repos.* | ||||
| 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 kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| @@ -42,7 +43,7 @@ open class FullReadCRUDCacheRepo<ObjectType, IdType>( | ||||
|     ) | ||||
|  | ||||
|     protected open suspend fun actualizeAll() { | ||||
|         locker.withWriteLock { kvCache.actualizeAll(parentRepo) } | ||||
|         kvCache.actualizeAll(parentRepo, locker = locker) | ||||
|     } | ||||
|  | ||||
|     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( | ||||
|         { getAll().takeIf { it.isNotEmpty() }.optionalOrAbsentIfNull }, | ||||
|         { getAll() }, | ||||
|         { kvCache.actualizeAll(clear = true) { it } } | ||||
|         { kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it } } | ||||
|     ) | ||||
|  | ||||
|     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.PaginationResult | ||||
| 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.pagination.getAll | ||||
| import kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.flow.* | ||||
| @@ -41,10 +41,7 @@ open class FullReadKeyValueCacheRepo<Key,Value>( | ||||
|         actualize = { locker.withWriteLock { actualize(it) } } | ||||
|     ) | ||||
|     protected open suspend fun actualizeAll() { | ||||
|         locker.withWriteLock { | ||||
|             kvCache.clear() | ||||
|             kvCache.set(parentRepo.getAll()) | ||||
|         } | ||||
|         kvCache.actualizeAll(parentRepo, locker) | ||||
|     } | ||||
|  | ||||
|     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( | ||||
|         { getAll().takeIf { it.isNotEmpty() }.optionalOrAbsentIfNull }, | ||||
|         { getAll() }, | ||||
|         { kvCache.actualizeAll(clear = true) { it } } | ||||
|         { kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it } } | ||||
|     ) | ||||
|  | ||||
|     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 invalidate() { | ||||
|         locker.withWriteLock { | ||||
|             kvCache.actualizeAll(parentRepo) | ||||
|         } | ||||
|         kvCache.actualizeAll(parentRepo, locker) | ||||
|     } | ||||
|  | ||||
|     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.utils.* | ||||
| 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 kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| @@ -41,15 +42,13 @@ open class FullReadKeyValuesCacheRepo<Key,Value>( | ||||
|     ) | ||||
|  | ||||
|     protected open suspend fun actualizeKey(k: Key) { | ||||
|         locker.withWriteLock { | ||||
|             kvCache.set(k, parentRepo.getAll(k)) | ||||
|         kvCache.actualizeAll(locker = locker, clearMode = ActualizeAllClearMode.Never) { | ||||
|             mapOf(k to parentRepo.getAll(k)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected open suspend fun actualizeAll() { | ||||
|         locker.withWriteLock { | ||||
|             kvCache.actualizeAll(parentRepo) | ||||
|         } | ||||
|         kvCache.actualizeAll(parentRepo, locker = locker) | ||||
|     } | ||||
|  | ||||
|     override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> { | ||||
| @@ -187,9 +186,11 @@ open class FullKeyValuesCacheRepo<Key,Value>( | ||||
|     } | ||||
|  | ||||
|     override suspend fun invalidate() { | ||||
|         locker.withWriteLock { | ||||
|             kvCache.actualizeAll(parentRepo) | ||||
|         kvCache.actualizeAll(parentRepo, locker = locker) | ||||
|     } | ||||
|  | ||||
|     override suspend fun set(toSet: Map<Key, List<Value>>) { | ||||
|         super<KeyValuesRepo>.set(toSet) | ||||
|     } | ||||
|  | ||||
|     override suspend fun removeWithValue(v: Value) { | ||||
|   | ||||
| @@ -1,43 +1,169 @@ | ||||
| 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 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> | ||||
| ) { | ||||
|     set( | ||||
|         getAll().also { | ||||
|             if (clear) { | ||||
|     clear() | ||||
|     val newData = getAll() | ||||
|     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() | ||||
|     } | ||||
|  | ||||
|     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( | ||||
|     repo: ReadKeyValueRepo<K, V>, | ||||
|     clear: Boolean = true, | ||||
|     locker: SmartRWLocker? = null, | ||||
|     clearMode: ActualizeAllClearMode = ActualizeAllClearMode.BeforeSet, | ||||
|     getAll: () -> Map<K, V> | ||||
| ) { | ||||
|     actualizeAll(clear) { | ||||
|         repo.getAll() | ||||
|     when (clearMode) { | ||||
|         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( | ||||
|     repo: ReadKeyValuesRepo<K, V>, | ||||
|     clear: Boolean = true, | ||||
|     parentRepo: ReadKeyValuesRepo<K, V>, | ||||
|     locker: SmartRWLocker? = null, | ||||
|     clearMode: ActualizeAllClearMode = ActualizeAllClearMode.BeforeSet, | ||||
| ) { | ||||
|     actualizeAll(clear) { | ||||
|         repo.getAll() | ||||
|     actualizeAll(locker, clearMode) { | ||||
|         parentRepo.getAll() | ||||
|     } | ||||
| } | ||||
|  | ||||
| suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAll( | ||||
|     repo: ReadCRUDRepo<V, K>, | ||||
|     clear: Boolean = true, | ||||
|     parentRepo: ReadCRUDRepo<V, K>, | ||||
|     locker: SmartRWLocker? = null, | ||||
|     clearMode: ActualizeAllClearMode = ActualizeAllClearMode.BeforeSet, | ||||
| ) { | ||||
|     actualizeAll(clear) { | ||||
|         repo.getAll() | ||||
|     actualizeAll(locker, clearMode) { | ||||
|         parentRepo.getAll() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| package dev.inmo.micro_utils.repos | ||||
|  | ||||
| import dev.inmo.micro_utils.common.diff | ||||
| import dev.inmo.micro_utils.pagination.* | ||||
| import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging | ||||
| import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging | ||||
| @@ -130,6 +131,14 @@ interface KeyValuesRepo<Key, Value> : ReadKeyValuesRepo<Key, Value>, WriteKeyVal | ||||
|  | ||||
|         remove(toRemove) | ||||
|     } | ||||
|  | ||||
|     override suspend fun set(toSet: Map<Key, List<Value>>) { | ||||
|         toSet.forEach { (k, v) -> | ||||
|             val diff = getAll(k).diff(v) | ||||
|             remove(k, diff.removed.map { it.value }) | ||||
|             add(k, diff.added.map { it.value }) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| typealias OneToManyKeyValueRepo<Key,Value> = KeyValuesRepo<Key, Value> | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,11 @@ | ||||
| package dev.inmo.micro_utils.strings | ||||
|  | ||||
| import dev.inmo.micro_utils.language_codes.IetfLang | ||||
| import kotlinx.serialization.KSerializer | ||||
| import kotlinx.serialization.Serializable | ||||
| import kotlinx.serialization.descriptors.SerialDescriptor | ||||
| import kotlinx.serialization.encoding.Decoder | ||||
| import kotlinx.serialization.encoding.Encoder | ||||
|  | ||||
| /** | ||||
|  * Use this class as a type of your strings object fields. For example: | ||||
| @@ -15,9 +20,10 @@ import dev.inmo.micro_utils.language_codes.IetfLang | ||||
|  * | ||||
|  * @see buildStringResource | ||||
|  */ | ||||
| class StringResource( | ||||
| @Serializable(StringResource.Companion::class) | ||||
| data class StringResource( | ||||
|     val default: String, | ||||
|     val map: Map<IetfLang, Lazy<String>> | ||||
|     val translations: Map<IetfLang, Lazy<String>> | ||||
| ) { | ||||
|     class Builder( | ||||
|         var default: String | ||||
| @@ -31,11 +37,18 @@ class StringResource( | ||||
|         infix fun IetfLang.variant(value: () -> String) = this variant lazy(value) | ||||
|         infix fun IetfLang.variant(value: String) = this variant lazyOf(value) | ||||
|  | ||||
|         operator fun IetfLang.invoke(value: () -> String) = this variant value | ||||
|         operator fun IetfLang.invoke(value: String) = this variant value | ||||
|  | ||||
|  | ||||
|         infix fun String.variant(value: Lazy<String>) = IetfLang(this) variant value | ||||
|         infix fun String.variant(value: () -> String) = IetfLang(this) variant lazy(value) | ||||
|         infix fun String.variant(value: String) = this variant lazyOf(value) | ||||
|  | ||||
|  | ||||
|         operator fun String.invoke(value: () -> String) = this variant value | ||||
|         operator fun String.invoke(value: String) = this variant value | ||||
|  | ||||
|         fun build() = StringResource(default, map.toMap()) | ||||
|     } | ||||
|  | ||||
| @@ -43,17 +56,48 @@ class StringResource( | ||||
|         if (languageCode == null) { | ||||
|             return default | ||||
|         } | ||||
|         map[languageCode] ?.let { return it.value } | ||||
|         translations[languageCode] ?.let { return it.value } | ||||
|  | ||||
|         return languageCode.parentLang ?.let { | ||||
|             map[it] ?.value | ||||
|             translations[it] ?.value | ||||
|         } ?: default | ||||
|     } | ||||
|  | ||||
|     companion object : KSerializer<StringResource> { | ||||
|         @Serializable | ||||
|         private class Surrogate( | ||||
|             val default: String, | ||||
|             val translations: Map<String, String> | ||||
|         ) | ||||
|  | ||||
|         override val descriptor: SerialDescriptor | ||||
|             get() = Surrogate.serializer().descriptor | ||||
|  | ||||
|         override fun deserialize(decoder: Decoder): StringResource { | ||||
|             val surrogate = Surrogate.serializer().deserialize(decoder) | ||||
|             return StringResource( | ||||
|                 surrogate.default, | ||||
|                 surrogate.translations.map { IetfLang(it.key) to lazyOf(it.value) }.toMap() | ||||
|             ) | ||||
|         } | ||||
|  | ||||
|         override fun serialize(encoder: Encoder, value: StringResource) { | ||||
|             Surrogate.serializer().serialize( | ||||
|                 encoder, | ||||
|                 Surrogate( | ||||
|                     value.default, | ||||
|                     value.translations.map { | ||||
|                         it.key.code to it.value.value | ||||
|                     }.toMap() | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline fun buildStringResource( | ||||
|     default: String, | ||||
|     builder: StringResource.Builder.() -> Unit | ||||
|     builder: StringResource.Builder.() -> Unit = {} | ||||
| ): StringResource { | ||||
|     return StringResource.Builder(default).apply(builder).build() | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user