mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-10-30 03:30:19 +00:00 
			
		
		
		
	Compare commits
	
		
			50 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 35825ad9b7 | |||
| b1eb26a89e | |||
| c9d04b6698 | |||
| 496133e014 | |||
| f2857ee2be | |||
| 22541f2d5e | |||
| e235c52b6c | |||
| e89b8c72dd | |||
| a374e53a3a | |||
| afb066c4ee | |||
| f05761d4a5 | |||
| 76adc9ea33 | |||
| 99dd291413 | |||
| f904eb27e1 | |||
| eeb8214812 | |||
| f7215b039e | |||
| c07fe5a0f9 | |||
| 0d28cb6e20 | |||
| a1a17bfd1f | |||
| f386f09592 | |||
| a47e17fe6e | |||
| 01dc3b63ff | |||
| 2d97e0699e | |||
| 75f514d99b | |||
| 9a687cfc1c | |||
| 14edf8b6b7 | |||
| 23aa2d8917 | |||
| 7651388b5c | |||
| 805ab32b24 | |||
| cc623b1097 | |||
| 1420416b3e | |||
| 9a0b67f938 | |||
| 303e1e6281 | |||
| ff59b0cc9c | |||
| be5d2ee715 | |||
| 8dd2e3f6f9 | |||
| 2eedd196d2 | |||
| 759a3f2784 | |||
| 386fa830c3 | |||
| c382423d77 | |||
| bb466ce66c | |||
| 72cd3dd8a1 | |||
|  | 595cedaaf1 | ||
| eeaceb6cf5 | |||
| 1bd671685b | |||
| 48d3fe41f2 | |||
| 7ab21871cd | |||
| 65d01b1fb3 | |||
| 6230accb68 | |||
| c6ed821934 | 
							
								
								
									
										80
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,85 @@ | ||||
| # Changelog | ||||
|  | ||||
| ## 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`: | ||||
|     * `Ktor`: `2.3.7` -> `2.3.8` | ||||
|  | ||||
| ## 0.20.30 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Exposed`: `0.46.0` -> `0.47.0` | ||||
|  | ||||
| ## 0.20.29 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Kotlin`: `1.9.21` -> `1.9.22` | ||||
|     * `Compose`: `1.5.11` -> `1.5.12` | ||||
|     * `Korlibs`: `5.3.0` -> `5.3.1` | ||||
|  | ||||
| ## 0.20.28 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Kotlin`: `1.9.22` -> `1.9.21` (downgrade) | ||||
|     * `Compose`: `1.6.0-dev13691` -> `1.5.11` (downgrade) | ||||
|  | ||||
| ## 0.20.27 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Kotlin`: `1.9.21` -> `1.9.22` | ||||
|     * `Compose`: `1.5.11` -> `1.6.0-dev13691` | ||||
|  | ||||
| ## 0.20.26 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Exposed`: `0.45.0` -> `0.46.0`. **This update brinds new api deprecations in exposed** | ||||
| * `Resources`: | ||||
|     * Add opportunity to get default translation by passing `null` as `IetfLang` argument | ||||
|     * Add several useful extensions to get translations in `JS` target | ||||
|  | ||||
| ## 0.20.25 | ||||
|  | ||||
| * `Colors`: | ||||
|     * `Common`: | ||||
|         * Module inited | ||||
|  | ||||
| ## 0.20.24 | ||||
|  | ||||
| **Since this version depdendencies of klock and krypto replaced with `com.soywiz.korge:korlibs-time` and `com.soywiz.korge:korlibs-crypto`** | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Klock` (since now `KorlibsTime`): `4.0.10` -> `5.3.0` | ||||
|     * `Krypto` (since now `KorlibsCrypto`): `4.0.10` -> `5.3.0` | ||||
| * `Serialization`: | ||||
|     * `Mapper`: | ||||
|         * `Mapper` pass decoder into callback of deserialization strategy | ||||
|         * `Mapper` pass encoder into callback of serialization strategy | ||||
|  | ||||
| ## 0.20.23 | ||||
|  | ||||
| * `Versions`: | ||||
|     * `Koin`: `3.5.0` -> `3.5.3` | ||||
|     * `Okio`: `3.6.0` -> `3.7.0` | ||||
| * `LanguageCodes`: | ||||
|     * Fixes in intermediate language codes (like `Chinese.Hans`) | ||||
|     * Rename `IetfLanguageCode` to `IetfLang` | ||||
|         * Rename all subsequent functions (including serializer) | ||||
|     * New lazy properties `knownLanguageCodesMap`, `knownLanguageCodesMapByLowerCasedKeys` and several others | ||||
|  | ||||
| ## 0.20.22 | ||||
|  | ||||
| * `Common`: | ||||
|   | ||||
							
								
								
									
										7
									
								
								colors/common/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								colors/common/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" | ||||
							
								
								
									
										174
									
								
								colors/common/src/commonMain/kotlin/HEXAColor.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								colors/common/src/commonMain/kotlin/HEXAColor.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| package dev.inmo.micro_utils.colors.common | ||||
|  | ||||
| import kotlinx.serialization.Serializable | ||||
| import kotlin.jvm.JvmInline | ||||
| import kotlin.math.floor | ||||
|  | ||||
| /** | ||||
|  * Wrapper for RGBA colors. Receiving [UInt] in main constructor. Each part in main constructor | ||||
|  * configured with `0x00 - 0xff` range. Examples: | ||||
|  * | ||||
|  * * Red: `0xff0000ffu` | ||||
|  * * 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 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() = "#${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 | ||||
|         get() = "rgb($r,$g,$b)" | ||||
|     val shortHex: String | ||||
|         get() = "#${r.shortPart()}${g.shortPart()}${b.shortPart()}" | ||||
|     val shortHexa: String | ||||
|         get() = "$shortHex${a.shortPart()}" | ||||
|     val rgbUInt: UInt | ||||
|         get() = (hexaUInt / 256u) | ||||
|     val rgbInt: Int | ||||
|         get() = rgbUInt.toInt() | ||||
|     val ahexUInt | ||||
|         get() = (a * 0x1000000).toUInt() + rgbUInt | ||||
|  | ||||
|     val r: Int | ||||
|         get() = ((hexaUInt and 0xff000000u) / 0x1000000u).toInt() | ||||
|     val g: Int | ||||
|         get() = ((hexaUInt and 0x00ff0000u) / 0x10000u).toInt() | ||||
|     val b: Int | ||||
|         get() = ((hexaUInt and 0x0000ff00u) / 0x100u).toInt() | ||||
|     val a: Int | ||||
|         get() = ((hexaUInt and 0x000000ffu)).toInt() | ||||
|     val aOfOne: Float | ||||
|         get() = a.toFloat() / (0xff) | ||||
|     init { | ||||
|         require(hexaUInt in 0u ..0xffffffffu) | ||||
|     } | ||||
|  | ||||
|     constructor(r: Int, g: Int, b: Int, a: Int) : this( | ||||
|         ((r * 0x1000000).toLong() + g * 0x10000 + b * 0x100 + a).toUInt() | ||||
|     ) { | ||||
|         require(r in 0 ..0xff) | ||||
|         require(g in 0 ..0xff) | ||||
|         require(b in 0 ..0xff) | ||||
|         require(a in 0 ..0xff) | ||||
|     } | ||||
|  | ||||
|     constructor(r: Int, g: Int, b: Int, aOfOne: Float = 1f) : this( | ||||
|         r = r, g = g, b = b, a = (aOfOne * 0xff).toInt() | ||||
|     ) | ||||
|  | ||||
|     override fun toString(): String { | ||||
|         return hexa | ||||
|     } | ||||
|  | ||||
|     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, | ||||
|         g: Int = this.g, | ||||
|         b: Int = this.b, | ||||
|         aOfOne: Float = this.aOfOne | ||||
|     ) = HEXAColor(r = r, g = g, b = b, aOfOne = aOfOne) | ||||
|     fun copy( | ||||
|         r: Int = this.r, | ||||
|         g: Int = this.g, | ||||
|         b: Int = this.b, | ||||
|         a: Int | ||||
|     ) = HEXAColor(r = r, g = g, b = b, a = a) | ||||
|  | ||||
|     companion object { | ||||
|         /** | ||||
|          * Parsing color from [color] | ||||
|          * | ||||
|          * Supported formats samples (on Red color based): | ||||
|          * | ||||
|          * * `#f00` | ||||
|          * * `#f00f` | ||||
|          * * `#ff0000` | ||||
|          * * `#ff0000ff` | ||||
|          * * `rgb(255, 0, 0)` | ||||
|          * * `rgba(255, 0, 0, 1)` | ||||
|          */ | ||||
|         fun parseStringColor(color: String): HEXAColor = when { | ||||
|             color.startsWith("#") -> color.removePrefix("#").let { color -> | ||||
|                 when (color.length) { | ||||
|                     3 -> color.map { "$it$it" }.joinToString(separator = "", postfix = "ff") | ||||
|                     4 -> color.take(3).map { "$it$it" }.joinToString(separator = "", postfix = color.takeLast(1).let { "${it}0" }) | ||||
|                     6 -> "${color}ff" | ||||
|                     8 -> color | ||||
|                     else -> error("Malfurmed color string: $color. It is expected that color started with # will contains 3, 6 or 8 valuable parts") | ||||
|                 } | ||||
|             } | ||||
|             color.startsWith("rgb(") -> color | ||||
|                 .removePrefix("rgb(") | ||||
|                 .removeSuffix(")") | ||||
|                 .replace(Regex("\\s"), "") | ||||
|                 .split(",") | ||||
|                 .joinToString("", postfix = "ff") { | ||||
|                     it.toInt().toString(16).padStart(2, '0') | ||||
|                 } | ||||
|             color.startsWith("rgba(") -> color | ||||
|                 .removePrefix("rgba(") | ||||
|                 .removeSuffix(")") | ||||
|                 .replace(Regex("\\s"), "") | ||||
|                 .split(",").let { | ||||
|                     it.take(3).map { it.toInt().toString(16).padStart(2, '0') } + (it.last().toFloat() * 0xff).toInt().toString(16).padStart(2, '0') | ||||
|                 } | ||||
|                 .joinToString("") | ||||
|             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] | ||||
|          * | ||||
|          * Supported formats samples (on Red color based): | ||||
|          * | ||||
|          * * `#f00` | ||||
|          * * `#ff0000` | ||||
|          * * `#ff0000ff` | ||||
|          * * `rgb(255, 0, 0)` | ||||
|          * * `rgba(255, 0, 0, 1)` | ||||
|          */ | ||||
|         operator fun invoke(color: String) = parseStringColor(color) | ||||
|  | ||||
|         private fun Int.shortPart(): String { | ||||
|             return (floor(toFloat() / 16)).toInt().toString(16) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										209
									
								
								colors/common/src/commonTest/kotlin/HexColorTests.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								colors/common/src/commonTest/kotlin/HexColorTests.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,209 @@ | ||||
| package dev.inmo.micro_utils.colors.common | ||||
|  | ||||
| import kotlin.math.floor | ||||
| import kotlin.test.Test | ||||
| import kotlin.test.assertEquals | ||||
| import kotlin.test.assertTrue | ||||
|  | ||||
| class HexColorTests { | ||||
|     val alphaRgbaPrecision = 5 | ||||
|     class TestColor( | ||||
|         val color: HEXAColor, | ||||
|         val shortHex: String, | ||||
|         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, | ||||
|         val g: Int, | ||||
|         val b: Int, | ||||
|         val a: Int, | ||||
|         vararg val additionalRGBAVariants: String | ||||
|     ) | ||||
|     val testColors: List<TestColor> | ||||
|         get() = listOf( | ||||
|             TestColor( | ||||
|                 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, | ||||
|                 g = 0x00, | ||||
|                 b = 0x00, | ||||
|                 a = 0xff, | ||||
|                 "rgba(255,0,0,1)", | ||||
|             ), | ||||
|             TestColor( | ||||
|                 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, | ||||
|                 g = 0xff, | ||||
|                 b = 0x00, | ||||
|                 a = 0xff, | ||||
|                 "rgba(0,255,0,1)" | ||||
|             ), | ||||
|             TestColor( | ||||
|                 color = HEXAColor(0x0000ffffu), | ||||
|                 shortHex = "#00f", | ||||
|                 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, | ||||
|                 g = 0x00, | ||||
|                 b = 0xff, | ||||
|                 a = 0xff, | ||||
|                 "rgba(0,0,255,1)" | ||||
|             ), | ||||
|             TestColor( | ||||
|                 color = HEXAColor(0xff000088u), | ||||
|                 shortHex = "#f00", | ||||
|                 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, | ||||
|                 g = 0x00, | ||||
|                 b = 0x00, | ||||
|                 a = 0x88, | ||||
|             ), | ||||
|             TestColor( | ||||
|                 color = HEXAColor(0x00ff0088u), | ||||
|                 shortHex = "#0f0", | ||||
|                 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, | ||||
|                 g = 0xff, | ||||
|                 b = 0x00, | ||||
|                 a = 0x88, | ||||
|             ), | ||||
|             TestColor( | ||||
|                 color = HEXAColor(0x0000ff88u), | ||||
|                 shortHex = "#00f", | ||||
|                 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, | ||||
|                 g = 0x00, | ||||
|                 b = 0xff, | ||||
|                 a = 0x88, | ||||
|             ), | ||||
|             TestColor( | ||||
|                 color = HEXAColor(0xff000022u), | ||||
|                 shortHex = "#f00", | ||||
|                 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, | ||||
|                 g = 0x00, | ||||
|                 b = 0x00, | ||||
|                 a = 0x22, | ||||
|             ), | ||||
|             TestColor( | ||||
|                 color = HEXAColor(0x00ff0022u), | ||||
|                 shortHex = "#0f0", | ||||
|                 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, | ||||
|                 g = 0xff, | ||||
|                 b = 0x00, | ||||
|                 a = 0x22, | ||||
|             ), | ||||
|             TestColor( | ||||
|                 color = HEXAColor(0x0000ff22u), | ||||
|                 shortHex = "#00f", | ||||
|                 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, | ||||
|                 g = 0x00, | ||||
|                 b = 0xff, | ||||
|                 a = 0x22, | ||||
|             ), | ||||
|         ) | ||||
|  | ||||
|     @Test | ||||
|     fun baseTest() { | ||||
|         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) | ||||
|             assertTrue(it.rgba == it.color.rgba || it.color.rgba in it.additionalRGBAVariants) | ||||
|             assertEquals(it.r, it.color.r) | ||||
|             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)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     fun testHexParseColor() { | ||||
|         testColors.forEach { | ||||
|             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.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)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -18,13 +18,13 @@ if (new File(projectDir, "secret.gradle").exists()) { | ||||
|     githubRelease { | ||||
|         token "${project.property('GITHUB_RELEASE_TOKEN')}" | ||||
|  | ||||
|         owner "InsanusMokrassar" | ||||
|         repo "MicroUtils" | ||||
|         owner = "InsanusMokrassar" | ||||
|         repo = "MicroUtils" | ||||
|  | ||||
|         tagName "v${project.version}" | ||||
|         releaseName "${project.version}" | ||||
|         targetCommitish "${project.version}" | ||||
|         tagName = "v${project.version}" | ||||
|         releaseName = "${project.version}" | ||||
|         targetCommitish = "${project.version}" | ||||
|  | ||||
|         body getCurrentVersionChangelog() | ||||
|         body = getCurrentVersionChangelog() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -15,5 +15,5 @@ crypto_js_version=4.1.1 | ||||
| # Project data | ||||
|  | ||||
| group=dev.inmo | ||||
| version=0.20.22 | ||||
| android_code_version=228 | ||||
| version=0.20.33 | ||||
| android_code_version=239 | ||||
|   | ||||
| @@ -1,32 +1,32 @@ | ||||
| [versions] | ||||
|  | ||||
| kt = "1.9.21" | ||||
| kt = "1.9.22" | ||||
| kt-serialization = "1.6.2" | ||||
| kt-coroutines = "1.7.3" | ||||
|  | ||||
| kslog = "1.3.1" | ||||
| kslog = "1.3.2" | ||||
|  | ||||
| jb-compose = "1.5.11" | ||||
| jb-exposed = "0.45.0" | ||||
| jb-compose = "1.5.12" | ||||
| jb-exposed = "0.47.0" | ||||
| jb-dokka = "1.9.10" | ||||
|  | ||||
| korlibs = "4.0.10" | ||||
| korlibs = "5.3.1" | ||||
| uuid = "0.8.2" | ||||
|  | ||||
| ktor = "2.3.7" | ||||
| ktor = "2.3.8" | ||||
|  | ||||
| gh-release = "2.4.1" | ||||
| gh-release = "2.5.2" | ||||
|  | ||||
| koin = "3.5.0" | ||||
| koin = "3.5.3" | ||||
|  | ||||
| okio = "3.6.0" | ||||
| okio = "3.8.0" | ||||
|  | ||||
| ksp = "1.9.21-1.0.16" | ||||
| kotlin-poet = "1.15.3" | ||||
| ksp = "1.9.22-1.0.17" | ||||
| kotlin-poet = "1.16.0" | ||||
|  | ||||
| versions = "0.50.0" | ||||
| versions = "0.51.0" | ||||
|  | ||||
| android-gradle = "8.2.0" | ||||
| android-gradle = "8.2.2" | ||||
| dexcount = "4.0.0" | ||||
|  | ||||
| android-coreKtx = "1.12.0" | ||||
| @@ -72,8 +72,8 @@ ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negoti | ||||
|  | ||||
| kslog = { module = "dev.inmo:kslog", version.ref = "kslog" } | ||||
|  | ||||
| klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "korlibs" } | ||||
| krypto = { module = "com.soywiz.korlibs.krypto:krypto", version.ref = "korlibs" } | ||||
| klock = { module = "com.soywiz.korge:korlibs-time", version.ref = "korlibs" } | ||||
| krypto = { module = "com.soywiz.korge:korlibs-crypto", version.ref = "korlibs" } | ||||
| uuid = { module = "com.benasher44:uuid", version.ref = "uuid" } | ||||
| koin = { module = "io.insert-koin:koin-core", version.ref = "koin" } | ||||
|  | ||||
|   | ||||
							
								
								
									
										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 | ||||
|   | ||||
| @@ -12,9 +12,12 @@ private val json = Json { | ||||
|     ignoreUnknownKeys = true | ||||
| } | ||||
| 
 | ||||
| private const val baseClassName = "IetfLanguageCode" | ||||
| private const val baseClassName = "IetfLang" | ||||
| private const val oldBaseClassName = "IetfLanguageCode" | ||||
| private const val unknownBaseClassName = "Unknown$baseClassName" | ||||
| private const val baseClassSerializerName = "IetfLanguageCodeSerializer" | ||||
| private const val oldUnknownBaseClassName = "Unknown$oldBaseClassName" | ||||
| private const val baseClassSerializerName = "${baseClassName}Serializer" | ||||
| private const val oldBaseClassSerializerName = "IetfLanguageCodeSerializer" | ||||
| private const val baseClassSerializerAnnotationName = "@Serializable(${baseClassSerializerName}::class)" | ||||
| 
 | ||||
| @Serializable | ||||
| @@ -78,14 +81,12 @@ private fun printLanguageCodeAndTags( | ||||
|     indents: String = "    " | ||||
| ): String = if (tag.subtags.isEmpty()) { | ||||
| """${indents}${baseClassSerializerAnnotationName} | ||||
| ${indents}object ${tag.title} : ${parent ?.title ?: baseClassName}() { override val code: String = "${tag.tag}"; override val withoutDialect: String get() = ${parent ?.title ?.let { "$it.code" } ?: "code"} }""" | ||||
| ${indents}object ${tag.title} : ${parent ?.title ?: baseClassName}() { override val code: String = "${tag.tag}"${parent ?.let { parent -> "; override val parentLang: ${parent.title} get() = ${parent.title};" } ?: ""} }""" | ||||
| } else { | ||||
| """ | ||||
| ${indents}${baseClassSerializerAnnotationName} | ||||
| ${indents}sealed class ${tag.title} : ${parent ?.title ?: baseClassName}() { | ||||
| ${indents}    override val code: String = "${tag.tag}" | ||||
| ${indents}    override val withoutDialect: String | ||||
| ${indents}        get() = code | ||||
| ${indents}    override val code: String = "${tag.tag}"${parent ?.let { parent -> "\n${indents}    override val parentLang: ${parent.title} get() = ${parent.title};" } ?: ""} | ||||
| 
 | ||||
| ${tag.subtags.joinToString("\n") { printLanguageCodeAndTags(it, tag, "${indents}    ") }} | ||||
| 
 | ||||
| @@ -95,7 +96,7 @@ ${indents}} | ||||
| """ | ||||
| } | ||||
| 
 | ||||
| fun buildKtFileContent(tags: List<Tag>): String = """ | ||||
| fun buildKtFileContent(tags: List<Tag>, prePackage: String): String = """ | ||||
| import kotlinx.serialization.Serializable | ||||
| 
 | ||||
| /** | ||||
| @@ -106,20 +107,27 @@ import kotlinx.serialization.Serializable | ||||
| ${baseClassSerializerAnnotationName} | ||||
| sealed class $baseClassName { | ||||
|     abstract val code: String | ||||
|     abstract val withoutDialect: String | ||||
|     open val parentLang: $baseClassName? | ||||
|         get() = null | ||||
|     open val withoutDialect: String | ||||
|         get() = parentLang ?.code ?: code | ||||
| 
 | ||||
| ${tags.joinToString("\n") { printLanguageCodeAndTags(it, indents = "    ") } } | ||||
| 
 | ||||
|     $baseClassSerializerAnnotationName | ||||
|     data class $unknownBaseClassName (override val code: String) : $baseClassName() { | ||||
|         override val withoutDialect: String = code.takeWhile { it != '-' } | ||||
|         override val parentLang = code.dropLastWhile { it != '-' }.removeSuffix("-").takeIf { it.length > 0 } ?.let(::$unknownBaseClassName) | ||||
|     } | ||||
|     @Deprecated("Renamed", ReplaceWith("$baseClassName.$unknownBaseClassName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName.$unknownBaseClassName")) | ||||
|     val $oldUnknownBaseClassName = $unknownBaseClassName | ||||
| 
 | ||||
|     override fun toString() = code | ||||
| } | ||||
| @Deprecated("Renamed", ReplaceWith("$baseClassName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName")) | ||||
| typealias $oldBaseClassName = $baseClassName | ||||
| """.trimIndent() | ||||
| 
 | ||||
| fun createStringConverterCode(tags: List<Tag>): String { | ||||
| fun createStringConverterCode(tags: List<Tag>, prePackage: String): String { | ||||
|     fun createDeserializeVariantForTag( | ||||
|         tag: Tag, | ||||
|         pretitle: String = baseClassName, | ||||
| @@ -128,19 +136,70 @@ fun createStringConverterCode(tags: List<Tag>): String { | ||||
|         val currentTitle = "$pretitle.${tag.title}" | ||||
|         return """${indents}$currentTitle.code -> $currentTitle${if (tag.subtags.isNotEmpty()) tag.subtags.joinToString("\n", "\n") { createDeserializeVariantForTag(it, currentTitle, indents) } else ""}""" | ||||
|     } | ||||
| 
 | ||||
|     return """fun String.as$baseClassName(): $baseClassName { | ||||
|     return when (this) { | ||||
| ${tags.joinToString("\n") { createDeserializeVariantForTag(it) }} | ||||
|         else -> $baseClassName.${unknownBaseClassName}(this) | ||||
|     fun createInheritorVariantForTag( | ||||
|         tag: Tag, | ||||
|         pretitle: String = baseClassName, | ||||
|         indents: String = "        " | ||||
|     ): String { | ||||
|         val currentTitle = "$pretitle.${tag.title}" | ||||
|         val subtags = if (tag.subtags.isNotEmpty()) { | ||||
|             tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForTag(it, currentTitle, "$indents    ") } | ||||
|         } else { | ||||
|             "" | ||||
|         } | ||||
|         return "${indents}$currentTitle$subtags" | ||||
|     } | ||||
|     fun createInheritorVariantForMapForTag( | ||||
|         tag: Tag, | ||||
|         pretitle: String = baseClassName, | ||||
|         indents: String = "        ", | ||||
|         codeSuffix: String = "" | ||||
|     ): String { | ||||
|         val currentTitle = "$pretitle.${tag.title}" | ||||
|         val subtags = if (tag.subtags.isNotEmpty()) { | ||||
|             tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForMapForTag(it, currentTitle, "$indents    ", codeSuffix) } | ||||
|         } else { | ||||
|             "" | ||||
|         } | ||||
|         return "${indents}$currentTitle.code${codeSuffix} to $currentTitle$subtags" | ||||
|     } | ||||
| 
 | ||||
|     return """val knownLanguageCodesMap: Map<String, $baseClassName> by lazy { | ||||
|     mapOf( | ||||
| ${tags.joinToString(",\n") { createInheritorVariantForMapForTag(it, indents = "        ") }} | ||||
|     ) | ||||
| } | ||||
| val knownLanguageCodesMapByLowerCasedKeys: Map<String, $baseClassName> by lazy { | ||||
|     mapOf( | ||||
| ${tags.joinToString(",\n") { createInheritorVariantForMapForTag(it, indents = "        ", codeSuffix = ".lowercase()") }} | ||||
|     ) | ||||
| } | ||||
| val knownLanguageCodes: List<$baseClassName> by lazy { | ||||
|     knownLanguageCodesMap.values.toList() | ||||
| } | ||||
| 
 | ||||
| fun String.as$baseClassName(): $baseClassName { | ||||
|     return knownLanguageCodesMap[this] ?: $baseClassName.${unknownBaseClassName}(this) | ||||
| } | ||||
| fun String.as${baseClassName}CaseInsensitive(): $baseClassName { | ||||
|     return knownLanguageCodesMapByLowerCasedKeys[this.lowercase()] ?: $baseClassName.${unknownBaseClassName}(this) | ||||
| } | ||||
| @Deprecated("Renamed", ReplaceWith("this.as$baseClassName()", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}as$baseClassName")) | ||||
| fun String.as$oldBaseClassName(): $baseClassName = as$baseClassName() | ||||
| 
 | ||||
| fun convertTo$baseClassName(code: String) = code.as$baseClassName() | ||||
| fun convertTo${baseClassName}CaseInsensitive(code: String) = code.as${baseClassName}CaseInsensitive() | ||||
| @Deprecated("Renamed", ReplaceWith("convertTo$baseClassName(code)", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}convertTo$baseClassName")) | ||||
| fun convertTo$oldBaseClassName(code: String) = convertTo$baseClassName(code) | ||||
| 
 | ||||
| fun $baseClassName(code: String) = code.as$baseClassName() | ||||
| fun ${baseClassName}CaseInsensitive(code: String) = code.as${baseClassName}CaseInsensitive() | ||||
| @Deprecated("Renamed", ReplaceWith("$baseClassName(code)", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName")) | ||||
| fun $oldBaseClassName(code: String) = $baseClassName(code) | ||||
| """ | ||||
| } | ||||
| 
 | ||||
| fun createSerializerCode(tags: List<Tag>): String { | ||||
| fun createSerializerCode(tags: List<Tag>, prePackage: String): String { | ||||
|     return """import kotlinx.serialization.KSerializer | ||||
| import kotlinx.serialization.builtins.serializer | ||||
| import kotlinx.serialization.encoding.Decoder | ||||
| @@ -153,16 +212,20 @@ object $baseClassSerializerName : KSerializer<$baseClassName> { | ||||
|         return $baseClassName(decoder.decodeString()) | ||||
|     } | ||||
| 
 | ||||
|     override fun serialize(encoder: Encoder, value: IetfLanguageCode) { | ||||
|     override fun serialize(encoder: Encoder, value: $baseClassName) { | ||||
|         encoder.encodeString(value.code) | ||||
|     } | ||||
| } | ||||
| @Deprecated("Renamed", ReplaceWith("$baseClassSerializerName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassSerializerName")) | ||||
| typealias $oldBaseClassSerializerName = $baseClassSerializerName | ||||
| """ | ||||
| } | ||||
| 
 | ||||
| suspend fun main(vararg args: String) { | ||||
|     val outputFolder = args.firstOrNull() ?.let { File(it) } | ||||
|     outputFolder ?.mkdirs() | ||||
|     val targetPackage = args.getOrNull(1) | ||||
|     val targetPackagePrefix = targetPackage ?.let { "package $it\n\n" } ?: "" | ||||
|     val ietfLanguageCodesLink = "https://datahub.io/core/language-codes/r/language-codes.json" | ||||
|     val ietfLanguageCodesAdditionalTagsLink = "https://datahub.io/core/language-codes/r/ietf-language-tags.json" | ||||
| 
 | ||||
| @@ -203,18 +266,18 @@ suspend fun main(vararg args: String) { | ||||
|     File(outputFolder, "LanguageCodes.kt").apply { | ||||
|         delete() | ||||
|         createNewFile() | ||||
|         writeText(buildKtFileContent(tags)) | ||||
|         writeText(targetPackagePrefix + buildKtFileContent(tags, targetPackage ?: "")) | ||||
|     } | ||||
| 
 | ||||
|     File(outputFolder, "StringToLanguageCodes.kt").apply { | ||||
|         delete() | ||||
|         createNewFile() | ||||
|         writeText(createStringConverterCode(tags)) | ||||
|         writeText(targetPackagePrefix + createStringConverterCode(tags, targetPackage ?: "")) | ||||
|     } | ||||
| 
 | ||||
|     File(outputFolder, "$baseClassSerializerName.kt").apply { | ||||
|         delete() | ||||
|         createNewFile() | ||||
|         writeText(createSerializerCode(tags)) | ||||
|         writeText(targetPackagePrefix + createSerializerCode(tags, targetPackage ?: "")) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,20 @@ | ||||
| package dev.inmo.micro_utils.language_codes | ||||
|  | ||||
| import kotlinx.serialization.KSerializer | ||||
| import kotlinx.serialization.builtins.serializer | ||||
| import kotlinx.serialization.encoding.Decoder | ||||
| import kotlinx.serialization.encoding.Encoder | ||||
|  | ||||
| object IetfLangSerializer : KSerializer<IetfLang> { | ||||
|     override val descriptor = String.serializer().descriptor | ||||
|  | ||||
|     override fun deserialize(decoder: Decoder): IetfLang { | ||||
|         return IetfLang(decoder.decodeString()) | ||||
|     } | ||||
|  | ||||
|     override fun serialize(encoder: Encoder, value: IetfLang) { | ||||
|         encoder.encodeString(value.code) | ||||
|     } | ||||
| } | ||||
| @Deprecated("Renamed", ReplaceWith("IetfLangSerializer", "dev.inmo.micro_utils.language_codes.IetfLangSerializer")) | ||||
| typealias IetfLanguageCodeSerializer = IetfLangSerializer | ||||
| @@ -1,18 +0,0 @@ | ||||
| package dev.inmo.micro_utils.language_codes | ||||
|  | ||||
| import kotlinx.serialization.KSerializer | ||||
| import kotlinx.serialization.builtins.serializer | ||||
| import kotlinx.serialization.encoding.Decoder | ||||
| import kotlinx.serialization.encoding.Encoder | ||||
|  | ||||
| object IetfLanguageCodeSerializer : KSerializer<IetfLanguageCode> { | ||||
|     override val descriptor = String.serializer().descriptor | ||||
|  | ||||
|     override fun deserialize(decoder: Decoder): IetfLanguageCode { | ||||
|         return IetfLanguageCode(decoder.decodeString()) | ||||
|     } | ||||
|  | ||||
|     override fun serialize(encoder: Encoder, value: IetfLanguageCode) { | ||||
|         encoder.encodeString(value.code) | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,16 @@ | ||||
| package dev.inmo.micro_utils.language_codes | ||||
|  | ||||
| import kotlin.test.Test | ||||
| import kotlin.test.assertEquals | ||||
|  | ||||
| class UnknownIetfLanguageTests { | ||||
|     @Test | ||||
|     fun commonTestOfParentCode() { | ||||
|         knownLanguageCodes.forEach { | ||||
|             val language = IetfLang.UnknownIetfLang(it.code) | ||||
|             assertEquals(it.code, language.code) | ||||
|             assertEquals(it.withoutDialect, language.withoutDialect) | ||||
|             assertEquals(it.parentLang ?.code, language.parentLang ?.code) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -2,7 +2,9 @@ package dev.inmo.micro_utils.language_codes | ||||
|  | ||||
| import java.util.Locale | ||||
|  | ||||
| fun IetfLanguageCode.toJavaLocale(): Locale = Locale.forLanguageTag(code) | ||||
| fun IetfLanguageCode?.toJavaLocaleOrDefault(): Locale = this ?.toJavaLocale() ?: Locale.getDefault() | ||||
| fun IetfLang.toJavaLocale(): Locale = Locale.forLanguageTag(code) | ||||
| fun IetfLang?.toJavaLocaleOrDefault(): Locale = this?.toJavaLocale() ?: Locale.getDefault() | ||||
|  | ||||
| fun Locale.toIetfLanguageCode(): IetfLanguageCode = IetfLanguageCode(toLanguageTag()) | ||||
| fun Locale.toIetfLang(): IetfLang = IetfLang(toLanguageTag()) | ||||
| @Deprecated("Renamed", ReplaceWith("this.toIetfLang()", "dev.inmo.micro_utils.language_codes.toIetfLang")) | ||||
| fun Locale.toIetfLanguageCode(): IetfLang = toIetfLang() | ||||
|   | ||||
| @@ -35,14 +35,14 @@ abstract class AbstractExposedReadCRUDRepo<ObjectType, IdType>( | ||||
|     } | ||||
|     override suspend fun getById(id: IdType): ObjectType? { | ||||
|         return transaction(db = database) { | ||||
|             select { | ||||
|             selectAll().where { | ||||
|                 selectById(id) | ||||
|             }.limit(1).firstOrNull() ?.asObject | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override suspend fun contains(id: IdType): Boolean = transaction(db = database) { | ||||
|         select { selectById(id) }.limit(1).any() | ||||
|         selectAll().where { selectById(id) }.limit(1).any() | ||||
|     } | ||||
|  | ||||
|     override suspend fun getAll(): Map<IdType, ObjectType> = transaction(database) { | ||||
|   | ||||
| @@ -101,7 +101,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>( | ||||
|         }.let { | ||||
|             if (it > 0) { | ||||
|                 transaction(db = database) { | ||||
|                     select { | ||||
|                     selectAll().where { | ||||
|                         selectById(this, id) | ||||
|                     }.limit(1).firstOrNull() ?.asObject | ||||
|                 } | ||||
| @@ -142,7 +142,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>( | ||||
|                 ids | ||||
|             } else { | ||||
|                 ids.filter { | ||||
|                     select { selectById(it) }.limit(1).none() | ||||
|                     selectAll().where { selectById(it) }.limit(1).none() | ||||
|                 } | ||||
|             } | ||||
|         }.forEach { | ||||
|   | ||||
| @@ -65,7 +65,7 @@ abstract class AbstractExposedKeyValueRepo<Key, Value>( | ||||
|     override suspend fun unsetWithValues(toUnset: List<Value>) { | ||||
|         transaction(database) { | ||||
|             toUnset.flatMap { | ||||
|                 val keys = select { selectByValue(it) }.mapNotNull { it.asKey } | ||||
|                 val keys = selectAll().where { selectByValue(it) }.mapNotNull { it.asKey } | ||||
|                 deleteWhere { selectByIds(it, keys) } | ||||
|                 keys | ||||
|             } | ||||
|   | ||||
| @@ -24,11 +24,11 @@ abstract class AbstractExposedReadKeyValueRepo<Key, Value>( | ||||
|     abstract val selectByValue: ISqlExpressionBuilder.(Value) -> Op<Boolean> | ||||
|  | ||||
|     override suspend fun get(k: Key): Value? = transaction(database) { | ||||
|         select { selectById(k) }.limit(1).firstOrNull() ?.asObject | ||||
|         selectAll().where { selectById(k) }.limit(1).firstOrNull() ?.asObject | ||||
|     } | ||||
|  | ||||
|     override suspend fun contains(key: Key): Boolean = transaction(database) { | ||||
|         select { selectById(key) }.limit(1).any() | ||||
|         selectAll().where { selectById(key) }.limit(1).any() | ||||
|     } | ||||
|  | ||||
|     override suspend fun getAll(): Map<Key, Value> = transaction(database) { selectAll().associate { it.asKey to it.asObject } } | ||||
| @@ -46,7 +46,7 @@ abstract class AbstractExposedReadKeyValueRepo<Key, Value>( | ||||
|     } | ||||
|  | ||||
|     override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) { | ||||
|         select { selectByValue(v) }.selectPaginated( | ||||
|         selectAll().where { selectByValue(v) }.selectPaginated( | ||||
|             pagination, | ||||
|             keyColumn, | ||||
|             reversed | ||||
|   | ||||
| @@ -65,7 +65,7 @@ open class ExposedKeyValueRepo<Key, Value>( | ||||
|     override suspend fun unsetWithValues(toUnset: List<Value>) { | ||||
|         transaction(database) { | ||||
|             toUnset.flatMap { | ||||
|                 val keys = select { valueColumn.eq(it) }.mapNotNull { it[keyColumn] } | ||||
|                 val keys = selectAll().where { valueColumn.eq(it) }.mapNotNull { it[keyColumn] } | ||||
|                 deleteWhere { keyColumn.inList(keys) } | ||||
|                 keys | ||||
|             } | ||||
|   | ||||
| @@ -29,7 +29,7 @@ abstract class AbstractExposedKeyValuesRepo<Key, Value>( | ||||
|         transaction(database) { | ||||
|             toAdd.keys.flatMap { k -> | ||||
|                 toAdd[k] ?.mapNotNull { v -> | ||||
|                     if (select { selectById(k).and(selectByValue(v)) }.limit(1).any()) { | ||||
|                     if (selectAll().where { selectById(k).and(selectByValue(v)) }.limit(1).any()) { | ||||
|                         return@mapNotNull null | ||||
|                     } | ||||
|                     val insertResult = insert { | ||||
|   | ||||
| @@ -19,7 +19,7 @@ abstract class AbstractExposedReadKeyValuesRepo<Key, Value>( | ||||
|         get() = asKey | ||||
|     abstract val selectByValue: ISqlExpressionBuilder.(Value) -> Op<Boolean> | ||||
|  | ||||
|     override suspend fun count(k: Key): Long = transaction(database) { select { selectById(k) }.count() } | ||||
|     override suspend fun count(k: Key): Long = transaction(database) { selectAll().where { selectById(k) }.count() } | ||||
|  | ||||
|     override suspend fun count(): Long = transaction(database) { selectAll().count() } | ||||
|  | ||||
| @@ -28,7 +28,7 @@ abstract class AbstractExposedReadKeyValuesRepo<Key, Value>( | ||||
|         pagination: Pagination, | ||||
|         reversed: Boolean | ||||
|     ): PaginationResult<Value> = transaction(database) { | ||||
|         select { selectById(k) }.selectPaginated( | ||||
|         selectAll().where { selectById(k) }.selectPaginated( | ||||
|             pagination, | ||||
|             keyColumn, | ||||
|             reversed | ||||
| @@ -55,7 +55,7 @@ abstract class AbstractExposedReadKeyValuesRepo<Key, Value>( | ||||
|         pagination: Pagination, | ||||
|         reversed: Boolean | ||||
|     ): PaginationResult<Key> = transaction(database) { | ||||
|         select { selectByValue(v) }.selectPaginated( | ||||
|         selectAll().where { selectByValue(v) }.selectPaginated( | ||||
|             pagination, | ||||
|             keyColumn, | ||||
|             reversed | ||||
| @@ -65,11 +65,11 @@ abstract class AbstractExposedReadKeyValuesRepo<Key, Value>( | ||||
|     } | ||||
|  | ||||
|     override suspend fun contains(k: Key): Boolean = transaction(database) { | ||||
|         select { selectById(k) }.limit(1).any() | ||||
|         selectAll().where { selectById(k) }.limit(1).any() | ||||
|     } | ||||
|  | ||||
|     override suspend fun contains(k: Key, v: Value): Boolean = transaction(database) { | ||||
|         select { selectById(k).and(selectByValue(v)) }.limit(1).any() | ||||
|         selectAll().where { selectById(k).and(selectByValue(v)) }.limit(1).any() | ||||
|     } | ||||
|  | ||||
|     override suspend fun getAll(reverseLists: Boolean): Map<Key, List<Value>> = transaction(database) { | ||||
| @@ -85,9 +85,9 @@ abstract class AbstractExposedReadKeyValuesRepo<Key, Value>( | ||||
|  | ||||
|     override suspend fun getAll(k: Key, reverseLists: Boolean): List<Value> = transaction(database) { | ||||
|         val query = if (reverseLists) { | ||||
|             select { selectById(k) }.orderBy(keyColumn, SortOrder.DESC) | ||||
|             selectAll().where { selectById(k) }.orderBy(keyColumn, SortOrder.DESC) | ||||
|         } else { | ||||
|             select { selectById(k) } | ||||
|             selectAll().where { selectById(k) } | ||||
|         } | ||||
|         query.map { | ||||
|             it.asObject | ||||
|   | ||||
| @@ -34,7 +34,7 @@ open class ExposedKeyValuesRepo<Key, Value>( | ||||
|         transaction(database) { | ||||
|             toAdd.keys.flatMap { k -> | ||||
|                 toAdd[k] ?.mapNotNull { v -> | ||||
|                     if (select { keyColumn.eq(k).and(valueColumn.eq(v)) }.limit(1).count() > 0) { | ||||
|                     if (selectAll().where { keyColumn.eq(k).and(valueColumn.eq(v)) }.limit(1).count() > 0) { | ||||
|                         return@mapNotNull null | ||||
|                     } | ||||
|                     val insertResult = insert { | ||||
| @@ -69,7 +69,7 @@ open class ExposedKeyValuesRepo<Key, Value>( | ||||
|  | ||||
|     override suspend fun removeWithValue(v: Value) { | ||||
|         transaction(database) { | ||||
|             val keys = select { selectByValue(v) }.map { it.asKey } | ||||
|             val keys = selectAll().where { selectByValue(v) }.map { it.asKey } | ||||
|             deleteWhere { SqlExpressionBuilder.selectByValue(v) } | ||||
|             keys | ||||
|         }.forEach { | ||||
| @@ -85,7 +85,7 @@ open class ExposedKeyValuesRepo<Key, Value>( | ||||
|  | ||||
|     override suspend fun clearWithValue(v: Value) { | ||||
|         transaction(database) { | ||||
|             val toClear = select { selectByValue(v) } | ||||
|             val toClear = selectAll().where { selectByValue(v) } | ||||
|                 .asSequence() | ||||
|                 .map { it.asKey to it.asObject } | ||||
|                 .groupBy { it.first } | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class ExposedStandardVersionsRepoProxy( | ||||
|     } | ||||
|  | ||||
|     override suspend fun getTableVersion(tableName: String): Int? = transaction(database) { | ||||
|         select { tableNameColumn.eq(tableName) }.limit(1).firstOrNull() ?.getOrNull(tableVersionColumn) | ||||
|         selectAll().where { tableNameColumn.eq(tableName) }.limit(1).firstOrNull() ?.getOrNull(tableVersionColumn) | ||||
|     } | ||||
|  | ||||
|     override suspend fun updateTableVersion(tableName: String, version: Int) { | ||||
|   | ||||
| @@ -4,7 +4,6 @@ import android.content.Context | ||||
| import android.content.res.Configuration | ||||
| import android.content.res.Resources | ||||
| import android.os.Build | ||||
| import dev.inmo.micro_utils.language_codes.toIetfLanguageCode | ||||
|  | ||||
| fun StringResource.translation(configuration: Configuration): String = translation( | ||||
|     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { | ||||
|   | ||||
| @@ -1,32 +1,103 @@ | ||||
| package dev.inmo.micro_utils.strings | ||||
|  | ||||
| import dev.inmo.micro_utils.language_codes.IetfLanguageCode | ||||
| 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 | ||||
|  | ||||
| class StringResource( | ||||
| /** | ||||
|  * Use this class as a type of your strings object fields. For example: | ||||
|  * | ||||
|  * ```kotlin | ||||
|  * object Strings { | ||||
|  *     val someResource: StringResource | ||||
|  * } | ||||
|  * ``` | ||||
|  * | ||||
|  * Use [buildStringResource] for useful creation of string resource | ||||
|  * | ||||
|  * @see buildStringResource | ||||
|  */ | ||||
| @Serializable(StringResource.Companion::class) | ||||
| data class StringResource( | ||||
|     val default: String, | ||||
|     val map: Map<IetfLanguageCode, Lazy<String>> | ||||
|     val translations: Map<IetfLang, Lazy<String>> | ||||
| ) { | ||||
|     class Builder( | ||||
|         var default: String | ||||
|     ) { | ||||
|         private val map = mutableMapOf<IetfLanguageCode, Lazy<String>>() | ||||
|         private val map = mutableMapOf<IetfLang, Lazy<String>>() | ||||
|  | ||||
|         infix fun IetfLanguageCode.variant(value: Lazy<String>) { | ||||
|         infix fun IetfLang.variant(value: Lazy<String>) { | ||||
|             map[this] = value | ||||
|         } | ||||
|         infix fun IetfLanguageCode.variant(value: String) = this variant lazyOf(value) | ||||
|         infix fun String.variant(value: Lazy<String>) = IetfLanguageCode(this) variant value | ||||
|  | ||||
|         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()) | ||||
|     } | ||||
|  | ||||
|     fun translation(languageCode: IetfLanguageCode): String = (map[languageCode] ?: map[IetfLanguageCode(languageCode.withoutDialect)]) ?.value ?: default | ||||
|     fun translation(languageCode: IetfLang?): String { | ||||
|         if (languageCode == null) { | ||||
|             return default | ||||
|         } | ||||
|         translations[languageCode] ?.let { return it.value } | ||||
|  | ||||
|         return languageCode.parentLang ?.let { | ||||
|             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() | ||||
| } | ||||
|   | ||||
							
								
								
									
										11
									
								
								resources/src/jsMain/kotlin/StringResourceExtensions.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								resources/src/jsMain/kotlin/StringResourceExtensions.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| package dev.inmo.micro_utils.strings | ||||
|  | ||||
| import dev.inmo.micro_utils.language_codes.IetfLang | ||||
| import kotlinx.browser.window | ||||
| import org.w3c.dom.NavigatorLanguage | ||||
|  | ||||
| fun StringResource.translation(language: NavigatorLanguage) = translation( | ||||
|     language.language.unsafeCast<String?>() ?.let { IetfLang(it) } | ||||
| ) | ||||
|  | ||||
| fun StringResource.translation() = translation(window.navigator) | ||||
| @@ -1,10 +1,10 @@ | ||||
| package dev.inmo.micro_utils.strings | ||||
|  | ||||
| import dev.inmo.micro_utils.language_codes.toIetfLanguageCode | ||||
| import dev.inmo.micro_utils.language_codes.toIetfLang | ||||
| import java.util.Locale | ||||
|  | ||||
| fun StringResource.translation(locale: Locale = Locale.getDefault()): String { | ||||
|     return translation(locale.toIetfLanguageCode()) | ||||
|     return translation(locale.toIetfLang()) | ||||
| } | ||||
|  | ||||
| fun Locale.translation(resource: StringResource): String = resource.translation(this) | ||||
| @@ -16,11 +16,16 @@ import kotlinx.serialization.encoding.Encoder | ||||
|  */ | ||||
| open class MapperDeserializationStrategy<I, O>( | ||||
|     private val base: DeserializationStrategy<I>, | ||||
|     private val deserialize: (I) -> O | ||||
|     private val deserialize: (Decoder, I) -> O | ||||
| ) : DeserializationStrategy<O> { | ||||
|     override val descriptor: SerialDescriptor = base.descriptor | ||||
|  | ||||
|     constructor( | ||||
|         base: DeserializationStrategy<I>, | ||||
|         deserialize: (I) -> O | ||||
|     ) : this(base, { _, i -> deserialize(i) }) | ||||
|  | ||||
|     override fun deserialize(decoder: Decoder): O { | ||||
|         return deserialize(base.deserialize(decoder)) | ||||
|         return deserialize(decoder, base.deserialize(decoder)) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,7 @@ | ||||
| package dev.inmo.micro_utils.serialization.mapper | ||||
|  | ||||
| import kotlinx.serialization.KSerializer | ||||
| import kotlinx.serialization.SerializationStrategy | ||||
| import kotlinx.serialization.descriptors.SerialDescriptor | ||||
| import kotlinx.serialization.encoding.Decoder | ||||
| import kotlinx.serialization.encoding.Encoder | ||||
|  | ||||
| /** | ||||
| @@ -11,15 +9,20 @@ import kotlinx.serialization.encoding.Encoder | ||||
|  * serialization | ||||
|  * | ||||
|  * @param base Serializer for [I] | ||||
|  * @param serialize Will be used in [serialize] method to convert incoming [O] to [I] and serialize with [base] | ||||
|  * @param internalSerialize Will be used in [internalSerialize] method to convert incoming [O] to [I] and serialize with [base] | ||||
|  */ | ||||
| open class MapperSerializationStrategy<I, O>( | ||||
|     private val base: SerializationStrategy<I>, | ||||
|     private val serialize: (O) -> I | ||||
|     private val internalSerialize: (Encoder, O) -> I | ||||
| ) : SerializationStrategy<O> { | ||||
|     override val descriptor: SerialDescriptor = base.descriptor | ||||
|  | ||||
|     constructor( | ||||
|         base: SerializationStrategy<I>, | ||||
|         serialize: (O) -> I | ||||
|     ) : this(base, { _, o -> serialize(o) }) | ||||
|  | ||||
|     override fun serialize(encoder: Encoder, value: O) { | ||||
|         base.serialize(encoder, serialize(value)) | ||||
|         base.serialize(encoder, internalSerialize(encoder, value)) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| package dev.inmo.micro_utils.serialization.mapper | ||||
|  | ||||
| import kotlinx.serialization.DeserializationStrategy | ||||
| import kotlinx.serialization.KSerializer | ||||
| import kotlinx.serialization.SerializationStrategy | ||||
| import kotlinx.serialization.descriptors.SerialDescriptor | ||||
| import kotlinx.serialization.encoding.Decoder | ||||
| import kotlinx.serialization.encoding.Encoder | ||||
| @@ -15,16 +17,28 @@ import kotlinx.serialization.encoding.Encoder | ||||
|  */ | ||||
| open class MapperSerializer<I, O>( | ||||
|     private val base: KSerializer<I>, | ||||
|     private val serialize: (O) -> I, | ||||
|     private val deserialize: (I) -> O | ||||
| ) : KSerializer<O> { | ||||
|     private val serialize: (Encoder, O) -> I, | ||||
|     private val deserialize: (Decoder, I) -> O | ||||
| ) : KSerializer<O>, | ||||
|     DeserializationStrategy<O> by MapperDeserializationStrategy<I, O>(base, deserialize), | ||||
|     SerializationStrategy<O> by MapperSerializationStrategy<I, O>(base, serialize) { | ||||
|     override val descriptor: SerialDescriptor = base.descriptor | ||||
|  | ||||
|     override fun deserialize(decoder: Decoder): O { | ||||
|         return deserialize(base.deserialize(decoder)) | ||||
|     } | ||||
|     constructor( | ||||
|         base: KSerializer<I>, | ||||
|         serialize: (O) -> I, | ||||
|         deserialize: (I) -> O | ||||
|     ) : this(base, { _, o -> serialize(o) }, { _, i -> deserialize(i) }) | ||||
|  | ||||
|     override fun serialize(encoder: Encoder, value: O) { | ||||
|         base.serialize(encoder, serialize(value)) | ||||
|     } | ||||
|     constructor( | ||||
|         base: KSerializer<I>, | ||||
|         serialize: (Encoder, O) -> I, | ||||
|         deserialize: (I) -> O | ||||
|     ) : this(base, serialize, { _, i -> deserialize(i) }) | ||||
|  | ||||
|     constructor( | ||||
|         base: KSerializer<I>, | ||||
|         serialize: (O) -> I, | ||||
|         deserialize: (Decoder, I) -> O | ||||
|     ) : this(base, { _, o -> serialize(o) }, deserialize) | ||||
| } | ||||
|   | ||||
| @@ -42,6 +42,7 @@ String[] includes = [ | ||||
|     ":serialization:mapper", | ||||
|     ":startup:plugin", | ||||
|     ":startup:launcher", | ||||
|     ":colors:common", | ||||
|  | ||||
|     ":resources", | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user