mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-17 14:29:24 +00:00
Compare commits
82 Commits
Author | SHA1 | Date | |
---|---|---|---|
837cac644d | |||
e83e0a8535 | |||
2e309c31a6 | |||
625db02651 | |||
e3144c08c1 | |||
40e25970ad | |||
4ccb8a8d11 | |||
26d1c3d9ef | |||
cc8fe7b001 | |||
4601eab864 | |||
1ee9c88ffd | |||
7feac213f6 | |||
c75a9c0f61 | |||
afd0c9784f | |||
f1ddbea06e | |||
42bf3cd4e3 | |||
27f5549f56 | |||
b0569f8421 | |||
8d86f29325 | |||
aeca32498a | |||
e8232664f3 | |||
efa869f91a | |||
dbff3d7cf1 | |||
9e70f73684 | |||
9f2f0de0c4 | |||
bf293a8f8f | |||
5fbc1a132f | |||
36093b9741 | |||
8713fb04c5 | |||
ea82a59f31 | |||
18f67cd4f2 | |||
120e7228c7 | |||
892fa90c37 | |||
491dac5bf0 | |||
2ab06fbafd | |||
e68735d061 | |||
9ba4d98c30 | |||
19bbfd4916 | |||
d94cd7ea94 | |||
0dd8f41eb0 | |||
f1ab5ab51f | |||
a8056f3120 | |||
09c0cdebb5 | |||
629d7c7a82 | |||
a0dd1aec3d | |||
17d6377902 | |||
ad401105a1 | |||
4913e99c2e | |||
16b97029cf | |||
481130c9bb | |||
698ed6718d | |||
d164813bb4 | |||
52157ee0e7 | |||
877b62fe5d | |||
d823a02971 | |||
e950056e3b | |||
70014ba233 | |||
4425f24a20 | |||
410964a44b | |||
30389e8536 | |||
5314833041 | |||
40f7cf7678 | |||
83a0b07062 | |||
1b4900d691 | |||
f9795d53a0 | |||
2b9bb4f141 | |||
9196e4c367 | |||
374a5a1a37 | |||
827cf32c1b | |||
98ad6dbeb2 | |||
63c8f642ec | |||
3bfe64f797 | |||
ec98029467 | |||
ab58478686 | |||
90247667d1 | |||
e661185534 | |||
d73e4e8e1f | |||
a6905e73cb | |||
93b054d55e | |||
5db4c5c717 | |||
5e04521929 | |||
30440b4ed1 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
.idea
|
||||
.kotlin
|
||||
out/*
|
||||
*.iml
|
||||
target
|
||||
@@ -17,3 +18,6 @@ publishing.sh
|
||||
|
||||
local.*
|
||||
local/
|
||||
**/*.local.*
|
||||
|
||||
.kotlin/
|
||||
|
107
CHANGELOG.md
107
CHANGELOG.md
@@ -1,5 +1,112 @@
|
||||
# Changelog
|
||||
|
||||
## 0.22.3
|
||||
|
||||
* `Versions`:
|
||||
* `Serialization`: `1.7.2` -> `1.7.3`
|
||||
* `Coroutines`: `1.8.1` -> `1.9.0`
|
||||
* `Compose`: `1.7.0-alpha03` -> `1.7.0-beta02`
|
||||
* `Koin`: `3.5.6` -> `4.0.0`
|
||||
* `Okio`: `3.9.0` -> `3.9.1`
|
||||
* `AndroidFragment`: `1.8.2` -> `1.8.3`
|
||||
* `androidx.compose.material3:material3` has been replaced with `org.jetbrains.compose.material3:material3`
|
||||
* `Common`:
|
||||
* `JS`:
|
||||
* Add several useful extensions
|
||||
* `Compose`:
|
||||
* `JS`:
|
||||
* Add several useful extensions
|
||||
|
||||
## 0.22.2
|
||||
|
||||
* `Versions`:
|
||||
* `Exposed`: `0.53.0` -> `0.54.0`
|
||||
* `SQLite`: `3.46.0.1` -> `3.46.1.0`
|
||||
|
||||
## 0.22.1
|
||||
|
||||
* `Versions`:
|
||||
* `Kotlin`: `2.0.10` -> `2.0.20`
|
||||
* `Serialization`: `1.7.1` -> `1.7.2`
|
||||
* `KSLog`: `1.3.5` -> `1.3.6`
|
||||
* `Compose`: `1.7.0-alpha02` -> `1.7.0-alpha03`
|
||||
* `Ktor`: `2.3.11` -> `2.3.12`
|
||||
|
||||
## 0.22.0
|
||||
|
||||
**THIS UPDATE CONTAINS BREAKING CHANGES ACCORDING TO UPDATE UP TO KOTLIN 2.0**
|
||||
|
||||
* `Versions`:
|
||||
* `Kotlin`: `1.9.23` -> `2.0.10`
|
||||
* `Serialization`: `1.6.3` -> `1.7.1`
|
||||
* `KSLog`: `1.3.4` -> `1.3.5`
|
||||
* `Compose`: `1.6.2` -> `1.7.0-alpha02`
|
||||
* `Exposed`: `0.50.1` -> `0.53.0`
|
||||
* `AndroidAppCompat`: `1.6.1` -> `1.7.0`
|
||||
* `AndroidFragment`: `1.7.1` -> `1.8.2`
|
||||
|
||||
## 0.21.6
|
||||
|
||||
* `KSP`:
|
||||
* `Sealed`:
|
||||
* Fixes in generation
|
||||
|
||||
## 0.21.5
|
||||
|
||||
* `KSP`:
|
||||
* Add utility functions `KSClassDeclaration.findSubClasses`
|
||||
* `Sealed`:
|
||||
* Improve generation
|
||||
|
||||
## 0.21.4
|
||||
|
||||
* `Common`:
|
||||
* `Compose`:
|
||||
* Add support of mingw, linux, arm64 targets
|
||||
* `Coroutines`:
|
||||
* `Compose`:
|
||||
* Add support of mingw, linux, arm64 targets
|
||||
* `Koin`:
|
||||
* Add support of mingw, linux, arm64 targets
|
||||
* `KSP`:
|
||||
* `ClassCasts`:
|
||||
* Add support of mingw, linux, arm64 targets
|
||||
* `Sealed`:
|
||||
* Add support of mingw, linux, arm64 targets
|
||||
|
||||
## 0.21.3
|
||||
|
||||
* `Colors`:
|
||||
* Added as a module. It should be used by default in case you wish all the API currently realized for `HEXAColor`
|
||||
* `Coroutines`:
|
||||
* Fix of [#374](https://github.com/InsanusMokrassar/MicroUtils/issues/374):
|
||||
* Add vararg variants of `awaitFirst`
|
||||
* Add `joinFirst`
|
||||
|
||||
## 0.21.2
|
||||
|
||||
* `KSP`:
|
||||
* `ClassCasts`:
|
||||
* Module has been initialized
|
||||
|
||||
## 0.21.1
|
||||
|
||||
* `KSP`:
|
||||
* Module has been initialized
|
||||
* `Generator`:
|
||||
* Module has been initialized
|
||||
* `Sealed`:
|
||||
* Module has been initialized
|
||||
|
||||
## 0.21.0
|
||||
|
||||
**THIS UPDATE CONTAINS BREAKING CHANGES IN `safely*`-ORIENTED FUNCTIONS**
|
||||
|
||||
* `Coroutines`:
|
||||
* **All `safely` functions lost their `supervisorScope` in favor to wrapping `runCatching`**
|
||||
* `runCatchingSafely` is the main handling function of all `safely` functions
|
||||
* `launchSafely*` and `asyncSafely*` blocks lost `CoroutineScope` as their receiver
|
||||
|
||||
## 0.20.52
|
||||
|
||||
* `Coroutines`:
|
||||
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppAndroidProjectPresetPath"
|
||||
apply from: "$mppAndroidProject"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppAndroidProjectPresetPath"
|
||||
apply from: "$mppAndroidProject"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -3,9 +3,10 @@ plugins {
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
alias(libs.plugins.jb.compose)
|
||||
alias(libs.plugins.kt.jb.compose)
|
||||
}
|
||||
|
||||
apply from: "$mppProjectWithSerializationAndComposePresetPath"
|
||||
apply from: "$mppProjectWithSerializationAndCompose"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppAndroidProjectPresetPath"
|
||||
apply from: "$mppAndroidProject"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -3,15 +3,16 @@ plugins {
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
alias(libs.plugins.jb.compose)
|
||||
alias(libs.plugins.kt.jb.compose)
|
||||
}
|
||||
|
||||
apply from: "$mppProjectWithSerializationAndComposePresetPath"
|
||||
apply from: "$mppProjectWithSerializationAndCompose"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
androidMain {
|
||||
dependencies {
|
||||
api libs.android.compose.material3
|
||||
api libs.jb.compose.material3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -29,15 +29,6 @@ allprojects {
|
||||
maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" }
|
||||
maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
|
||||
}
|
||||
|
||||
// temporal crutch until legacy tests will be stabled or legacy target will be removed
|
||||
if (it != rootProject.findProject("docs")) {
|
||||
tasks.whenTaskAdded { task ->
|
||||
if(task.name == "jsLegacyBrowserTest" || task.name == "jsLegacyNodeTest") {
|
||||
task.enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "./extensions.gradle"
|
||||
|
17
colors/build.gradle
Normal file
17
colors/build.gradle
Normal file
@@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api project(":micro_utils.colors.common")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,4 +4,4 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
298
colors/src/commonMain/kotlin/StandardColors.kt
Normal file
298
colors/src/commonMain/kotlin/StandardColors.kt
Normal file
@@ -0,0 +1,298 @@
|
||||
package dev.inmo.micro_utils.colors
|
||||
|
||||
import dev.inmo.micro_utils.colors.common.HEXAColor
|
||||
|
||||
val HEXAColor.Companion.aliceblue
|
||||
get() = HEXAColor(0xF0F8FFFFu)
|
||||
val HEXAColor.Companion.antiquewhite
|
||||
get() = HEXAColor(0xFAEBD7FFu)
|
||||
val HEXAColor.Companion.aqua
|
||||
get() = HEXAColor(0x00FFFFFFu)
|
||||
val HEXAColor.Companion.aquamarine
|
||||
get() = HEXAColor(0x7FFFD4FFu)
|
||||
val HEXAColor.Companion.azure
|
||||
get() = HEXAColor(0xF0FFFFFFu)
|
||||
val HEXAColor.Companion.beige
|
||||
get() = HEXAColor(0xF5F5DCFFu)
|
||||
val HEXAColor.Companion.bisque
|
||||
get() = HEXAColor(0xFFE4C4FFu)
|
||||
val HEXAColor.Companion.black
|
||||
get() = HEXAColor(0x000000FFu)
|
||||
val HEXAColor.Companion.blanchedalmond
|
||||
get() = HEXAColor(0xFFEBCDFFu)
|
||||
val HEXAColor.Companion.blue
|
||||
get() = HEXAColor(0x0000FFFFu)
|
||||
val HEXAColor.Companion.blueviolet
|
||||
get() = HEXAColor(0x8A2BE2FFu)
|
||||
val HEXAColor.Companion.brown
|
||||
get() = HEXAColor(0xA52A2AFFu)
|
||||
val HEXAColor.Companion.burlywood
|
||||
get() = HEXAColor(0xDEB887FFu)
|
||||
val HEXAColor.Companion.cadetblue
|
||||
get() = HEXAColor(0x5F9EA0FFu)
|
||||
val HEXAColor.Companion.chartreuse
|
||||
get() = HEXAColor(0x7FFF00FFu)
|
||||
val HEXAColor.Companion.chocolate
|
||||
get() = HEXAColor(0xD2691EFFu)
|
||||
val HEXAColor.Companion.coral
|
||||
get() = HEXAColor(0xFF7F50FFu)
|
||||
val HEXAColor.Companion.cornflowerblue
|
||||
get() = HEXAColor(0x6495EDFFu)
|
||||
val HEXAColor.Companion.cornsilk
|
||||
get() = HEXAColor(0xFFF8DCFFu)
|
||||
val HEXAColor.Companion.crimson
|
||||
get() = HEXAColor(0xDC143CFFu)
|
||||
val HEXAColor.Companion.cyan
|
||||
get() = HEXAColor(0x00FFFFFFu)
|
||||
val HEXAColor.Companion.darkblue
|
||||
get() = HEXAColor(0x00008BFFu)
|
||||
val HEXAColor.Companion.darkcyan
|
||||
get() = HEXAColor(0x008B8BFFu)
|
||||
val HEXAColor.Companion.darkgoldenrod
|
||||
get() = HEXAColor(0xB8860BFFu)
|
||||
val HEXAColor.Companion.darkgray
|
||||
get() = HEXAColor(0xA9A9A9FFu)
|
||||
val HEXAColor.Companion.darkgreen
|
||||
get() = HEXAColor(0x006400FFu)
|
||||
val HEXAColor.Companion.darkgrey
|
||||
get() = HEXAColor(0xA9A9A9FFu)
|
||||
val HEXAColor.Companion.darkkhaki
|
||||
get() = HEXAColor(0xBDB76BFFu)
|
||||
val HEXAColor.Companion.darkmagenta
|
||||
get() = HEXAColor(0x8B008BFFu)
|
||||
val HEXAColor.Companion.darkolivegreen
|
||||
get() = HEXAColor(0x556B2FFFu)
|
||||
val HEXAColor.Companion.darkorange
|
||||
get() = HEXAColor(0xFF8C00FFu)
|
||||
val HEXAColor.Companion.darkorchid
|
||||
get() = HEXAColor(0x9932CCFFu)
|
||||
val HEXAColor.Companion.darkred
|
||||
get() = HEXAColor(0x8B0000FFu)
|
||||
val HEXAColor.Companion.darksalmon
|
||||
get() = HEXAColor(0xE9967AFFu)
|
||||
val HEXAColor.Companion.darkseagreen
|
||||
get() = HEXAColor(0x8FBC8FFFu)
|
||||
val HEXAColor.Companion.darkslateblue
|
||||
get() = HEXAColor(0x483D8BFFu)
|
||||
val HEXAColor.Companion.darkslategray
|
||||
get() = HEXAColor(0x2F4F4FFFu)
|
||||
val HEXAColor.Companion.darkslategrey
|
||||
get() = HEXAColor(0x2F4F4FFFu)
|
||||
val HEXAColor.Companion.darkturquoise
|
||||
get() = HEXAColor(0x00CED1FFu)
|
||||
val HEXAColor.Companion.darkviolet
|
||||
get() = HEXAColor(0x9400D3FFu)
|
||||
val HEXAColor.Companion.deeppink
|
||||
get() = HEXAColor(0xFF1493FFu)
|
||||
val HEXAColor.Companion.deepskyblue
|
||||
get() = HEXAColor(0x00BFFFFFu)
|
||||
val HEXAColor.Companion.dimgray
|
||||
get() = HEXAColor(0x696969FFu)
|
||||
val HEXAColor.Companion.dimgrey
|
||||
get() = HEXAColor(0x696969FFu)
|
||||
val HEXAColor.Companion.dodgerblue
|
||||
get() = HEXAColor(0x1E90FFFFu)
|
||||
val HEXAColor.Companion.firebrick
|
||||
get() = HEXAColor(0xB22222FFu)
|
||||
val HEXAColor.Companion.floralwhite
|
||||
get() = HEXAColor(0xFFFAF0FFu)
|
||||
val HEXAColor.Companion.forestgreen
|
||||
get() = HEXAColor(0x228B22FFu)
|
||||
val HEXAColor.Companion.fuchsia
|
||||
get() = HEXAColor(0xFF00FFFFu)
|
||||
val HEXAColor.Companion.gainsboro
|
||||
get() = HEXAColor(0xDCDCDCFFu)
|
||||
val HEXAColor.Companion.ghostwhite
|
||||
get() = HEXAColor(0xF8F8FFFFu)
|
||||
val HEXAColor.Companion.gold
|
||||
get() = HEXAColor(0xFFD700FFu)
|
||||
val HEXAColor.Companion.goldenrod
|
||||
get() = HEXAColor(0xDAA520FFu)
|
||||
val HEXAColor.Companion.gray
|
||||
get() = HEXAColor(0x808080FFu)
|
||||
val HEXAColor.Companion.green
|
||||
get() = HEXAColor(0x008000FFu)
|
||||
val HEXAColor.Companion.greenyellow
|
||||
get() = HEXAColor(0xADFF2FFFu)
|
||||
val HEXAColor.Companion.grey
|
||||
get() = HEXAColor(0x808080FFu)
|
||||
val HEXAColor.Companion.honeydew
|
||||
get() = HEXAColor(0xF0FFF0FFu)
|
||||
val HEXAColor.Companion.hotpink
|
||||
get() = HEXAColor(0xFF69B4FFu)
|
||||
val HEXAColor.Companion.indianred
|
||||
get() = HEXAColor(0xCD5C5CFFu)
|
||||
val HEXAColor.Companion.indigo
|
||||
get() = HEXAColor(0x4B0082FFu)
|
||||
val HEXAColor.Companion.ivory
|
||||
get() = HEXAColor(0xFFFFF0FFu)
|
||||
val HEXAColor.Companion.khaki
|
||||
get() = HEXAColor(0xF0E68CFFu)
|
||||
val HEXAColor.Companion.lavender
|
||||
get() = HEXAColor(0xE6E6FAFFu)
|
||||
val HEXAColor.Companion.lavenderblush
|
||||
get() = HEXAColor(0xFFF0F5FFu)
|
||||
val HEXAColor.Companion.lawngreen
|
||||
get() = HEXAColor(0x7CFC00FFu)
|
||||
val HEXAColor.Companion.lemonchiffon
|
||||
get() = HEXAColor(0xFFFACDFFu)
|
||||
val HEXAColor.Companion.lightblue
|
||||
get() = HEXAColor(0xADD8E6FFu)
|
||||
val HEXAColor.Companion.lightcoral
|
||||
get() = HEXAColor(0xF08080FFu)
|
||||
val HEXAColor.Companion.lightcyan
|
||||
get() = HEXAColor(0xE0FFFFFFu)
|
||||
val HEXAColor.Companion.lightgoldenrodyellow
|
||||
get() = HEXAColor(0xFAFAD2FFu)
|
||||
val HEXAColor.Companion.lightgray
|
||||
get() = HEXAColor(0xD3D3D3FFu)
|
||||
val HEXAColor.Companion.lightgreen
|
||||
get() = HEXAColor(0x90EE90FFu)
|
||||
val HEXAColor.Companion.lightgrey
|
||||
get() = HEXAColor(0xD3D3D3FFu)
|
||||
val HEXAColor.Companion.lightpink
|
||||
get() = HEXAColor(0xFFB6C1FFu)
|
||||
val HEXAColor.Companion.lightsalmon
|
||||
get() = HEXAColor(0xFFA07AFFu)
|
||||
val HEXAColor.Companion.lightseagreen
|
||||
get() = HEXAColor(0x20B2AAFFu)
|
||||
val HEXAColor.Companion.lightskyblue
|
||||
get() = HEXAColor(0x87CEFAFFu)
|
||||
val HEXAColor.Companion.lightslategray
|
||||
get() = HEXAColor(0x778899FFu)
|
||||
val HEXAColor.Companion.lightslategrey
|
||||
get() = HEXAColor(0x778899FFu)
|
||||
val HEXAColor.Companion.lightsteelblue
|
||||
get() = HEXAColor(0xB0C4DEFFu)
|
||||
val HEXAColor.Companion.lightyellow
|
||||
get() = HEXAColor(0xFFFFE0FFu)
|
||||
val HEXAColor.Companion.lime
|
||||
get() = HEXAColor(0x00FF00FFu)
|
||||
val HEXAColor.Companion.limegreen
|
||||
get() = HEXAColor(0x32CD32FFu)
|
||||
val HEXAColor.Companion.linen
|
||||
get() = HEXAColor(0xFAF0E6FFu)
|
||||
val HEXAColor.Companion.magenta
|
||||
get() = HEXAColor(0xFF00FFFFu)
|
||||
val HEXAColor.Companion.maroon
|
||||
get() = HEXAColor(0x800000FFu)
|
||||
val HEXAColor.Companion.mediumaquamarine
|
||||
get() = HEXAColor(0x66CDAAFFu)
|
||||
val HEXAColor.Companion.mediumblue
|
||||
get() = HEXAColor(0x0000CDFFu)
|
||||
val HEXAColor.Companion.mediumorchid
|
||||
get() = HEXAColor(0xBA55D3FFu)
|
||||
val HEXAColor.Companion.mediumpurple
|
||||
get() = HEXAColor(0x9370DBFFu)
|
||||
val HEXAColor.Companion.mediumseagreen
|
||||
get() = HEXAColor(0x3CB371FFu)
|
||||
val HEXAColor.Companion.mediumslateblue
|
||||
get() = HEXAColor(0x7B68EEFFu)
|
||||
val HEXAColor.Companion.mediumspringgreen
|
||||
get() = HEXAColor(0x00FA9AFFu)
|
||||
val HEXAColor.Companion.mediumturquoise
|
||||
get() = HEXAColor(0x48D1CCFFu)
|
||||
val HEXAColor.Companion.mediumvioletred
|
||||
get() = HEXAColor(0xC71585FFu)
|
||||
val HEXAColor.Companion.midnightblue
|
||||
get() = HEXAColor(0x191970FFu)
|
||||
val HEXAColor.Companion.mintcream
|
||||
get() = HEXAColor(0xF5FFFAFFu)
|
||||
val HEXAColor.Companion.mistyrose
|
||||
get() = HEXAColor(0xFFE4E1FFu)
|
||||
val HEXAColor.Companion.moccasin
|
||||
get() = HEXAColor(0xFFE4B5FFu)
|
||||
val HEXAColor.Companion.navajowhite
|
||||
get() = HEXAColor(0xFFDEADFFu)
|
||||
val HEXAColor.Companion.navy
|
||||
get() = HEXAColor(0x000080FFu)
|
||||
val HEXAColor.Companion.oldlace
|
||||
get() = HEXAColor(0xFDF5E6FFu)
|
||||
val HEXAColor.Companion.olive
|
||||
get() = HEXAColor(0x808000FFu)
|
||||
val HEXAColor.Companion.olivedrab
|
||||
get() = HEXAColor(0x6B8E23FFu)
|
||||
val HEXAColor.Companion.orange
|
||||
get() = HEXAColor(0xFFA500FFu)
|
||||
val HEXAColor.Companion.orangered
|
||||
get() = HEXAColor(0xFF4500FFu)
|
||||
val HEXAColor.Companion.orchid
|
||||
get() = HEXAColor(0xDA70D6FFu)
|
||||
val HEXAColor.Companion.palegoldenrod
|
||||
get() = HEXAColor(0xEEE8AAFFu)
|
||||
val HEXAColor.Companion.palegreen
|
||||
get() = HEXAColor(0x98FB98FFu)
|
||||
val HEXAColor.Companion.paleturquoise
|
||||
get() = HEXAColor(0xAFEEEEFFu)
|
||||
val HEXAColor.Companion.palevioletred
|
||||
get() = HEXAColor(0xDB7093FFu)
|
||||
val HEXAColor.Companion.papayawhip
|
||||
get() = HEXAColor(0xFFEFD5FFu)
|
||||
val HEXAColor.Companion.peachpuff
|
||||
get() = HEXAColor(0xFFDAB9FFu)
|
||||
val HEXAColor.Companion.peru
|
||||
get() = HEXAColor(0xCD853FFFu)
|
||||
val HEXAColor.Companion.pink
|
||||
get() = HEXAColor(0xFFC0CBFFu)
|
||||
val HEXAColor.Companion.plum
|
||||
get() = HEXAColor(0xDDA0DDFFu)
|
||||
val HEXAColor.Companion.powderblue
|
||||
get() = HEXAColor(0xB0E0E6FFu)
|
||||
val HEXAColor.Companion.purple
|
||||
get() = HEXAColor(0x800080FFu)
|
||||
val HEXAColor.Companion.red
|
||||
get() = HEXAColor(0xFF0000FFu)
|
||||
val HEXAColor.Companion.rosybrown
|
||||
get() = HEXAColor(0xBC8F8FFFu)
|
||||
val HEXAColor.Companion.royalblue
|
||||
get() = HEXAColor(0x4169E1FFu)
|
||||
val HEXAColor.Companion.saddlebrown
|
||||
get() = HEXAColor(0x8B4513FFu)
|
||||
val HEXAColor.Companion.salmon
|
||||
get() = HEXAColor(0xFA8072FFu)
|
||||
val HEXAColor.Companion.sandybrown
|
||||
get() = HEXAColor(0xF4A460FFu)
|
||||
val HEXAColor.Companion.seagreen
|
||||
get() = HEXAColor(0x2E8B57FFu)
|
||||
val HEXAColor.Companion.seashell
|
||||
get() = HEXAColor(0xFFF5EEFFu)
|
||||
val HEXAColor.Companion.sienna
|
||||
get() = HEXAColor(0xA0522DFFu)
|
||||
val HEXAColor.Companion.silver
|
||||
get() = HEXAColor(0xC0C0C0FFu)
|
||||
val HEXAColor.Companion.skyblue
|
||||
get() = HEXAColor(0x87CEEBFFu)
|
||||
val HEXAColor.Companion.slateblue
|
||||
get() = HEXAColor(0x6A5ACDFFu)
|
||||
val HEXAColor.Companion.slategray
|
||||
get() = HEXAColor(0x708090FFu)
|
||||
val HEXAColor.Companion.slategrey
|
||||
get() = HEXAColor(0x708090FFu)
|
||||
val HEXAColor.Companion.snow
|
||||
get() = HEXAColor(0xFFFAFAFFu)
|
||||
val HEXAColor.Companion.springgreen
|
||||
get() = HEXAColor(0x00FF7FFFu)
|
||||
val HEXAColor.Companion.steelblue
|
||||
get() = HEXAColor(0x4682B4FFu)
|
||||
val HEXAColor.Companion.tan
|
||||
get() = HEXAColor(0xD2B48CFFu)
|
||||
val HEXAColor.Companion.teal
|
||||
get() = HEXAColor(0x008080FFu)
|
||||
val HEXAColor.Companion.thistle
|
||||
get() = HEXAColor(0xD8BFD8FFu)
|
||||
val HEXAColor.Companion.tomato
|
||||
get() = HEXAColor(0xFF6347FFu)
|
||||
val HEXAColor.Companion.turquoise
|
||||
get() = HEXAColor(0x40E0D0FFu)
|
||||
val HEXAColor.Companion.violet
|
||||
get() = HEXAColor(0xEE82EEFFu)
|
||||
val HEXAColor.Companion.wheat
|
||||
get() = HEXAColor(0xF5DEB3FFu)
|
||||
val HEXAColor.Companion.white
|
||||
get() = HEXAColor(0xFFFFFFFFu)
|
||||
val HEXAColor.Companion.whitesmoke
|
||||
get() = HEXAColor(0xF5F5F5FFu)
|
||||
val HEXAColor.Companion.yellow
|
||||
get() = HEXAColor(0xFFFF00FFu)
|
||||
val HEXAColor.Companion.yellowgreen
|
||||
get() = HEXAColor(0x9ACD32FFu)
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -3,9 +3,10 @@ plugins {
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
alias(libs.plugins.jb.compose)
|
||||
alias(libs.plugins.kt.jb.compose)
|
||||
}
|
||||
|
||||
apply from: "$mppProjectWithSerializationAndComposePresetPath"
|
||||
apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -0,0 +1,19 @@
|
||||
package dev.inmo.micro_utils.common.compose
|
||||
|
||||
import org.jetbrains.compose.web.dom.AttrBuilderContext
|
||||
import org.w3c.dom.Element
|
||||
|
||||
operator fun <T : Element> AttrBuilderContext<T>?.plus(
|
||||
other: AttrBuilderContext<T>?
|
||||
) = when (this) {
|
||||
null -> other ?: {}
|
||||
else -> when (other) {
|
||||
null -> this ?: {}
|
||||
else -> {
|
||||
{
|
||||
invoke(this)
|
||||
other(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package dev.inmo.micro_utils.common.compose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.DisposableEffectResult
|
||||
import androidx.compose.runtime.DisposableEffectScope
|
||||
import org.jetbrains.compose.web.attributes.AttrsScope
|
||||
import org.jetbrains.compose.web.dom.ElementScope
|
||||
import org.w3c.dom.Element
|
||||
|
||||
/**
|
||||
* This function must be called in the context of your tag content. It works like default [AttrsScope.ref],
|
||||
* but able to be used several times. Uses [DisposableEffect] under the hood
|
||||
*/
|
||||
@Composable
|
||||
fun <T : Element> ElementScope<T>.ref(
|
||||
block: DisposableEffectScope.(T) -> DisposableEffectResult
|
||||
) {
|
||||
DisposableEffect(0) {
|
||||
block(scopeElement)
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package dev.inmo.micro_utils.common.compose
|
||||
|
||||
import org.jetbrains.compose.web.dom.AttrBuilderContext
|
||||
|
||||
fun tagClasses(vararg classnames: String): AttrBuilderContext<*> = {
|
||||
classes(*classnames)
|
||||
}
|
||||
|
||||
fun tagId(id: String): AttrBuilderContext<*> = {
|
||||
id(id)
|
||||
}
|
@@ -8,7 +8,7 @@ private inline fun <T> getObject(
|
||||
additional: MutableList<T>,
|
||||
iterator: Iterator<T>
|
||||
): T? = when {
|
||||
additional.isNotEmpty() -> additional.removeFirst()
|
||||
additional.isNotEmpty() -> additional.removeAt(0)
|
||||
iterator.hasNext() -> iterator.next()
|
||||
else -> null
|
||||
}
|
||||
|
@@ -0,0 +1,13 @@
|
||||
package dev.inmo.micro_utils.common
|
||||
|
||||
import kotlinx.browser.window
|
||||
|
||||
fun copyToClipboard(text: String): Boolean {
|
||||
return runCatching {
|
||||
window.navigator.clipboard.writeText(
|
||||
text
|
||||
)
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}.isSuccess
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package dev.inmo.micro_utils.common
|
||||
|
||||
import kotlinx.browser.window
|
||||
import org.w3c.files.Blob
|
||||
import org.w3c.files.BlobPropertyBag
|
||||
import kotlin.js.json
|
||||
|
||||
external class ClipboardItem(data: dynamic)
|
||||
|
||||
inline fun Blob.convertToClipboardItem(): ClipboardItem {
|
||||
val itemData: dynamic = json(this.type to this)
|
||||
return ClipboardItem(itemData)
|
||||
}
|
||||
|
||||
suspend fun copyImageURLToClipboard(imageUrl: String): Boolean {
|
||||
return runCatching {
|
||||
val response = window.fetch(imageUrl).await()
|
||||
val blob = response.blob().await()
|
||||
val data = arrayOf(
|
||||
Blob(
|
||||
arrayOf(blob),
|
||||
BlobPropertyBag("image/png")
|
||||
).convertToClipboardItem()
|
||||
).asDynamic()
|
||||
window.navigator.clipboard.write(data)
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}.isSuccess
|
||||
}
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -3,9 +3,10 @@ plugins {
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
alias(libs.plugins.jb.compose)
|
||||
alias(libs.plugins.kt.jb.compose)
|
||||
}
|
||||
|
||||
apply from: "$mppProjectWithSerializationAndComposePresetPath"
|
||||
apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -3,6 +3,12 @@ package dev.inmo.micro_utils.coroutines
|
||||
import kotlinx.coroutines.*
|
||||
import kotlin.coroutines.*
|
||||
|
||||
/**
|
||||
* Trying to [Deferred.await] on all [this] [Deferred]s. The first [Deferred] completed its work will interrupt all
|
||||
* others awaits and, if [cancelOnResult] passed as true (**by default**), will also cancel all the others [Deferred]s
|
||||
*
|
||||
* @param scope Will be used to create [CoroutineScope.LinkedSupervisorScope] and launch joining of all [Job]s in it
|
||||
*/
|
||||
suspend fun <T> Iterable<Deferred<T>>.awaitFirstWithDeferred(
|
||||
scope: CoroutineScope,
|
||||
cancelOnResult: Boolean = true
|
||||
@@ -24,10 +30,45 @@ suspend fun <T> Iterable<Deferred<T>>.awaitFirstWithDeferred(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trying to [Deferred.await] on all [this] [Deferred]s. The first [Deferred] completed its work will interrupt all
|
||||
* others awaits and, if [cancelOnResult] passed as true (**by default**), will also cancel all the others [Deferred]s
|
||||
*
|
||||
* @param scope Will be used to create [CoroutineScope.LinkedSupervisorScope] and launch joining of all [Job]s in it
|
||||
*/
|
||||
suspend fun <T> Iterable<Deferred<T>>.awaitFirst(
|
||||
scope: CoroutineScope,
|
||||
cancelOnResult: Boolean = true
|
||||
): T = awaitFirstWithDeferred(scope, cancelOnResult).second
|
||||
|
||||
/**
|
||||
* Trying to [Deferred.await] on all [this] [Deferred]s. The first [Deferred] completed its work will interrupt all
|
||||
* others awaits and, if [cancelOthers] passed as true (**by default**), will also cancel all the others [Deferred]s
|
||||
*
|
||||
* Creates [CoroutineScope] using [coroutineContext] for internal purposes
|
||||
*/
|
||||
suspend fun <T> Iterable<Deferred<T>>.awaitFirst(
|
||||
cancelOthers: Boolean = true
|
||||
): T = awaitFirst(CoroutineScope(coroutineContext), cancelOthers)
|
||||
|
||||
/**
|
||||
* Trying to [Deferred.await] on all [deferreds]. The first [Deferred] completed its work will interrupt all
|
||||
* others awaits and, if [cancelOnResult] passed as true (**by default**), will also cancel all the others [deferreds]
|
||||
*
|
||||
* @param scope Will be used to create [CoroutineScope.LinkedSupervisorScope] and launch joining of all [Job]s in it
|
||||
*/
|
||||
suspend fun <T> awaitFirst(
|
||||
vararg deferreds: Deferred<T>,
|
||||
scope: CoroutineScope,
|
||||
cancelOnResult: Boolean = true
|
||||
): T = deferreds.toList().awaitFirstWithDeferred(scope, cancelOnResult).second
|
||||
/**
|
||||
* Trying to [Deferred.await] on all [deferreds]. The first [Deferred] completed its work will interrupt all
|
||||
* others awaits and, if [cancelOthers] passed as true (**by default**), will also cancel all the others [deferreds]
|
||||
*
|
||||
* Creates [CoroutineScope] using [coroutineContext] for internal purposes
|
||||
*/
|
||||
suspend fun <T> awaitFirst(
|
||||
vararg deferreds: Deferred<T>,
|
||||
cancelOthers: Boolean = true
|
||||
): T = awaitFirst(*deferreds, scope = CoroutineScope(coroutineContext), cancelOnResult = cancelOthers)
|
||||
|
@@ -0,0 +1,44 @@
|
||||
package dev.inmo.micro_utils.coroutines
|
||||
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
typealias ExceptionHandler<T> = suspend (Throwable) -> T
|
||||
|
||||
/**
|
||||
* This instance will be used in all calls of [safely] where exception handler has not been passed
|
||||
*/
|
||||
var defaultSafelyExceptionHandler: ExceptionHandler<Nothing> = { throw it }
|
||||
|
||||
/**
|
||||
* This instance will be used in all calls of [safelyWithoutExceptions] as an exception handler for [safely] call
|
||||
*/
|
||||
var defaultSafelyWithoutExceptionHandler: ExceptionHandler<Unit> = {
|
||||
try {
|
||||
defaultSafelyExceptionHandler(it)
|
||||
} catch (e: Throwable) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This key can (and will) be used to get [ContextSafelyExceptionHandler] from [coroutineContext] of suspend functions
|
||||
* and in [ContextSafelyExceptionHandler] for defining of its [CoroutineContext.Element.key]
|
||||
*
|
||||
* @see safelyWithContextExceptionHandler
|
||||
* @see ContextSafelyExceptionHandler
|
||||
*/
|
||||
object ContextSafelyExceptionHandlerKey : CoroutineContext.Key<ContextSafelyExceptionHandler>
|
||||
|
||||
/**
|
||||
* [ExceptionHandler] wrapper which was created to make possible to use [handler] across all coroutines calls
|
||||
*
|
||||
* @see safelyWithContextExceptionHandler
|
||||
* @see ContextSafelyExceptionHandlerKey
|
||||
*/
|
||||
class ContextSafelyExceptionHandler(
|
||||
val handler: ExceptionHandler<Unit>
|
||||
) : CoroutineContext.Element {
|
||||
override val key: CoroutineContext.Key<*>
|
||||
get() = ContextSafelyExceptionHandlerKey
|
||||
}
|
@@ -4,46 +4,58 @@ import kotlinx.coroutines.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
typealias ExceptionHandler<T> = suspend (Throwable) -> T
|
||||
|
||||
/**
|
||||
* This instance will be used in all calls of [safely] where exception handler has not been passed
|
||||
* Launching [block] in [runCatching]. In case of failure, it will:
|
||||
*
|
||||
* * Try to get [ContextSafelyExceptionHandler] from current [coroutineContext] and call its
|
||||
* [ContextSafelyExceptionHandler.handler] invoke. **Thrown exception from its handler
|
||||
* will pass out of [runCatchingSafely]**
|
||||
* * Execute [onException] inside of new [runCatching] and return its result. Throws exception
|
||||
* will be caught by [runCatching] and wrapped in [Result]
|
||||
*
|
||||
* @return [Result] with result of [block] if no exceptions or [Result] from [onException] execution
|
||||
*/
|
||||
var defaultSafelyExceptionHandler: ExceptionHandler<Nothing> = { throw it }
|
||||
|
||||
/**
|
||||
* This instance will be used in all calls of [safelyWithoutExceptions] as an exception handler for [safely] call
|
||||
*/
|
||||
var defaultSafelyWithoutExceptionHandler: ExceptionHandler<Unit> = {
|
||||
try {
|
||||
defaultSafelyExceptionHandler(it)
|
||||
} catch (e: Throwable) {
|
||||
// do nothing
|
||||
suspend inline fun <T> runCatchingSafely(
|
||||
onException: ExceptionHandler<T>,
|
||||
block: suspend () -> T
|
||||
): Result<T> {
|
||||
return runCatching {
|
||||
block()
|
||||
}.onFailure {
|
||||
coroutineContext[ContextSafelyExceptionHandlerKey] ?.handler ?.invoke(it)
|
||||
return runCatching {
|
||||
onException(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This key can (and will) be used to get [ContextSafelyExceptionHandler] from [coroutineContext] of suspend functions
|
||||
* and in [ContextSafelyExceptionHandler] for defining of its [CoroutineContext.Element.key]
|
||||
*
|
||||
* @see safelyWithContextExceptionHandler
|
||||
* @see ContextSafelyExceptionHandler
|
||||
*/
|
||||
object ContextSafelyExceptionHandlerKey : CoroutineContext.Key<ContextSafelyExceptionHandler>
|
||||
suspend inline fun <T, R> R.runCatchingSafely(
|
||||
onException: ExceptionHandler<T>,
|
||||
block: suspend R.() -> T
|
||||
): Result<T> = runCatchingSafely<T>(onException) {
|
||||
block()
|
||||
}
|
||||
|
||||
/**
|
||||
* [ExceptionHandler] wrapper which was created to make possible to use [handler] across all coroutines calls
|
||||
*
|
||||
* @see safelyWithContextExceptionHandler
|
||||
* @see ContextSafelyExceptionHandlerKey
|
||||
* Launching [runCatchingSafely] with [defaultSafelyExceptionHandler] as `onException` parameter
|
||||
*/
|
||||
class ContextSafelyExceptionHandler(
|
||||
val handler: ExceptionHandler<Unit>
|
||||
) : CoroutineContext.Element {
|
||||
override val key: CoroutineContext.Key<*>
|
||||
get() = ContextSafelyExceptionHandlerKey
|
||||
suspend inline fun <T> runCatchingSafely(
|
||||
block: suspend () -> T
|
||||
): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
|
||||
|
||||
suspend inline fun <T, R> R.runCatchingSafely(
|
||||
block: suspend R.() -> T
|
||||
): Result<T> = runCatchingSafely<T> {
|
||||
block()
|
||||
}
|
||||
|
||||
//suspend inline fun <T, R> T.runCatchingSafely(
|
||||
// onException: ExceptionHandler<R>,
|
||||
// block: suspend T.() -> R
|
||||
//): Result<R> = runCatchingSafely(onException) {
|
||||
// block()
|
||||
//}
|
||||
|
||||
/**
|
||||
* @return [ContextSafelyExceptionHandler] from [coroutineContext] by key [ContextSafelyExceptionHandlerKey] if
|
||||
* exists
|
||||
@@ -64,7 +76,7 @@ suspend fun contextSafelyExceptionHandler() = coroutineContext[ContextSafelyExce
|
||||
suspend fun <T> safelyWithContextExceptionHandler(
|
||||
contextExceptionHandler: ExceptionHandler<Unit>,
|
||||
safelyExceptionHandler: ExceptionHandler<T> = defaultSafelyExceptionHandler,
|
||||
block: suspend CoroutineScope.() -> T
|
||||
block: suspend () -> T
|
||||
): T {
|
||||
val contextSafelyExceptionHandler = contextSafelyExceptionHandler() ?.handler ?.let { oldHandler ->
|
||||
ContextSafelyExceptionHandler {
|
||||
@@ -78,57 +90,35 @@ suspend fun <T> safelyWithContextExceptionHandler(
|
||||
}
|
||||
|
||||
/**
|
||||
* It will run [block] inside of [supervisorScope] to avoid problems with catching of exceptions
|
||||
* Calls [runCatchingSafely] and getting the result via [Result.getOrThrow]
|
||||
*
|
||||
* Priorities of [ExceptionHandler]s:
|
||||
*
|
||||
* * [onException] In case if custom (will be used anyway if not [defaultSafelyExceptionHandler])
|
||||
* * [CoroutineContext.get] with [SafelyExceptionHandlerKey] as key
|
||||
* * [defaultSafelyExceptionHandler]
|
||||
*
|
||||
* Remember, that [ExceptionHandler] from [CoroutineContext.get] will be used anyway if it is available. After it will
|
||||
* be called [onException]
|
||||
*
|
||||
* @param [onException] Will be called when happen exception inside of [block]. By default will throw exception - this
|
||||
* exception will be available for catching
|
||||
*
|
||||
* @see defaultSafelyExceptionHandler
|
||||
* @see safelyWithoutExceptions
|
||||
* @see safelyWithContextExceptionHandler
|
||||
* @see runCatchingSafely
|
||||
*/
|
||||
suspend fun <T> safely(
|
||||
onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
|
||||
block: suspend CoroutineScope.() -> T
|
||||
): T {
|
||||
return try {
|
||||
supervisorScope(block)
|
||||
} catch (e: Throwable) {
|
||||
coroutineContext[ContextSafelyExceptionHandlerKey] ?.handler ?.invoke(e)
|
||||
onException(e)
|
||||
}
|
||||
}
|
||||
suspend inline fun <T> safely(
|
||||
onException: ExceptionHandler<T>,
|
||||
block: suspend () -> T
|
||||
): T = runCatchingSafely(onException, block).getOrThrow()
|
||||
|
||||
suspend fun <T> runCatchingSafely(
|
||||
onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
|
||||
block: suspend CoroutineScope.() -> T
|
||||
): Result<T> = runCatching {
|
||||
safely(onException, block)
|
||||
}
|
||||
|
||||
suspend fun <T, R> T.runCatchingSafely(
|
||||
onException: ExceptionHandler<R> = defaultSafelyExceptionHandler,
|
||||
block: suspend T.() -> R
|
||||
): Result<R> = runCatching {
|
||||
safely(onException) { block() }
|
||||
}
|
||||
/**
|
||||
* Calls [safely] with passing of [defaultSafelyExceptionHandler] as `onException`
|
||||
*
|
||||
* @see runCatchingSafely
|
||||
*/
|
||||
suspend inline fun <T> safely(
|
||||
block: suspend () -> T
|
||||
): T = safely(defaultSafelyExceptionHandler, block)
|
||||
suspend inline fun <T, R> R.safely(
|
||||
block: suspend R.() -> T
|
||||
): T = safely<T> { block() }
|
||||
|
||||
@Deprecated("Renamed", ReplaceWith("runCatchingSafely(block)", "dev.inmo.micro_utils.coroutines.runCatchingSafely"))
|
||||
suspend fun <T> safelyWithResult(
|
||||
block: suspend CoroutineScope.() -> T
|
||||
block: suspend () -> T
|
||||
): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
|
||||
|
||||
suspend fun <T, R> T.safelyWithResult(
|
||||
block: suspend T.() -> R
|
||||
): Result<R> = runCatchingSafely(defaultSafelyExceptionHandler, block)
|
||||
@Deprecated("Renamed", ReplaceWith("this.runCatchingSafely(block)", "dev.inmo.micro_utils.coroutines.runCatchingSafely"))
|
||||
suspend fun <T, R> R.safelyWithResult(
|
||||
block: suspend R.() -> T
|
||||
): Result<T> = safelyWithResult<T> { block() }
|
||||
|
||||
/**
|
||||
* Use this handler in cases you wish to include handling of exceptions by [defaultSafelyWithoutExceptionHandler] and
|
||||
@@ -148,18 +138,20 @@ val defaultSafelyWithoutExceptionHandlerWithNull: ExceptionHandler<Nothing?> = {
|
||||
* result from exception (instead of throwing it, by default always returns null)
|
||||
*/
|
||||
suspend fun <T> safelyWithoutExceptions(
|
||||
onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
|
||||
block: suspend CoroutineScope.() -> T
|
||||
): T? = safely(onException, block)
|
||||
onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
|
||||
block: suspend () -> T
|
||||
): T? = runCatchingSafely(onException, block).getOrNull()
|
||||
|
||||
suspend fun <T> runCatchingSafelyWithoutExceptions(
|
||||
onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
|
||||
block: suspend CoroutineScope.() -> T
|
||||
): Result<T?> = runCatching {
|
||||
safelyWithoutExceptions(onException, block)
|
||||
onException: ExceptionHandler<T?> = defaultSafelyExceptionHandler,
|
||||
block: suspend () -> T
|
||||
): Result<T?> = runCatchingSafely(onException, block).let {
|
||||
if (it.isFailure) return Result.success<T?>(null)
|
||||
|
||||
it
|
||||
}
|
||||
|
||||
fun CoroutineScope(
|
||||
fun CoroutineScopeWithDefaultFallback(
|
||||
context: CoroutineContext,
|
||||
defaultExceptionsHandler: ExceptionHandler<Unit>
|
||||
) = CoroutineScope(
|
||||
|
@@ -0,0 +1,64 @@
|
||||
package dev.inmo.micro_utils.coroutines
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlin.coroutines.*
|
||||
|
||||
/**
|
||||
* Trying to [Job.join] on all [this] [Job]s. The first [Job] completed its work will interrupt all others joins
|
||||
* and, if [cancelOthers] passed as true (**by default**), will also cancel all the others [Job]s
|
||||
*
|
||||
* @param scope Will be used to create [CoroutineScope.LinkedSupervisorScope] and launch joining of all [Job]s in it
|
||||
*/
|
||||
suspend fun Iterable<Job>.joinFirst(
|
||||
scope: CoroutineScope,
|
||||
cancelOthers: Boolean = true
|
||||
): Job {
|
||||
val resultDeferred = CompletableDeferred<Job>()
|
||||
val scope = scope.LinkedSupervisorScope()
|
||||
forEach {
|
||||
scope.launch {
|
||||
it.join()
|
||||
resultDeferred.complete(it)
|
||||
scope.cancel()
|
||||
}
|
||||
}
|
||||
return resultDeferred.await().also {
|
||||
if (cancelOthers) {
|
||||
forEach {
|
||||
runCatchingSafely { it.cancel() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Trying to [Job.join] on all [this] [Job]s. The first [Job] completed its work will interrupt all others joins
|
||||
* and, if [cancelOthers] passed as true (**by default**), will also cancel all the others [Job]s
|
||||
*
|
||||
* Creates [CoroutineScope] using [coroutineContext] for internal purposes
|
||||
*/
|
||||
suspend fun Iterable<Job>.joinFirst(
|
||||
cancelOthers: Boolean = true
|
||||
): Job = joinFirst(CoroutineScope(coroutineContext), cancelOthers)
|
||||
|
||||
/**
|
||||
* Trying to [Job.join] on all [jobs]. The first [Job] completed its work will interrupt all others joins
|
||||
* and, if [cancelOthers] passed as true (**by default**), will also cancel all the others [Job]s
|
||||
*
|
||||
* @param scope Will be used to create [CoroutineScope.LinkedSupervisorScope] and launch joining of all [Job]s in it
|
||||
*/
|
||||
suspend fun joinFirst(
|
||||
vararg jobs: Job,
|
||||
scope: CoroutineScope,
|
||||
cancelOthers: Boolean = true
|
||||
): Job = jobs.toList().joinFirst(scope, cancelOthers)
|
||||
|
||||
/**
|
||||
* Trying to [Job.join] on all [jobs]. The first [Job] completed its work will interrupt all others joins
|
||||
* and, if [cancelOthers] passed as true (**by default**), will also cancel all the others [Job]s
|
||||
*
|
||||
* Creates [CoroutineScope] using [coroutineContext] for internal purposes
|
||||
*/
|
||||
suspend fun joinFirst(
|
||||
vararg jobs: Job,
|
||||
cancelOthers: Boolean = true
|
||||
): Job = joinFirst(*jobs, scope = CoroutineScope(coroutineContext), cancelOthers = cancelOthers)
|
@@ -10,7 +10,9 @@ fun CoroutineScope.launchSafely(
|
||||
onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,
|
||||
block: suspend CoroutineScope.() -> Unit
|
||||
) = launch(context, start) {
|
||||
safely(onException, block)
|
||||
runCatchingSafely(onException) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
fun CoroutineScope.launchSafelyWithoutExceptions(
|
||||
@@ -19,7 +21,9 @@ fun CoroutineScope.launchSafelyWithoutExceptions(
|
||||
onException: ExceptionHandler<Unit?> = defaultSafelyWithoutExceptionHandlerWithNull,
|
||||
block: suspend CoroutineScope.() -> Unit
|
||||
) = launch(context, start) {
|
||||
safelyWithoutExceptions(onException, block)
|
||||
runCatchingSafelyWithoutExceptions(onException) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> CoroutineScope.asyncSafely(
|
||||
@@ -28,7 +32,9 @@ fun <T> CoroutineScope.asyncSafely(
|
||||
onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
|
||||
block: suspend CoroutineScope.() -> T
|
||||
) = async(context, start) {
|
||||
safely(onException, block)
|
||||
runCatchingSafely(onException) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> CoroutineScope.asyncSafelyWithoutExceptions(
|
||||
@@ -37,5 +43,7 @@ fun <T> CoroutineScope.asyncSafelyWithoutExceptions(
|
||||
onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
|
||||
block: suspend CoroutineScope.() -> T
|
||||
) = async(context, start) {
|
||||
safelyWithoutExceptions(onException, block)
|
||||
runCatchingSafelyWithoutExceptions(onException) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ import kotlin.contracts.contract
|
||||
* * [unlockWrite] will just unlock [writeMutex]
|
||||
*/
|
||||
class SmartRWLocker(private val readPermits: Int = Int.MAX_VALUE, writeIsLocked: Boolean = false) {
|
||||
private val _readSemaphore = SmartSemaphore.Mutable(permits = readPermits, acquiredPermits = 0)
|
||||
private val _readSemaphore = SmartSemaphore.Mutable(permits = readPermits, acquiredPermits = if (writeIsLocked) readPermits else 0)
|
||||
private val _writeMutex = SmartMutex.Mutable(locked = writeIsLocked)
|
||||
|
||||
val readSemaphore: SmartSemaphore.Immutable = _readSemaphore.immutable()
|
||||
|
@@ -10,8 +10,12 @@ fun CoroutineScope.LinkedSupervisorJob(
|
||||
additionalContext: CoroutineContext? = null
|
||||
) = coroutineContext.LinkedSupervisorJob(additionalContext)
|
||||
|
||||
fun CoroutineScope.LinkedSupervisorScope(
|
||||
|
||||
fun CoroutineContext.LinkedSupervisorScope(
|
||||
additionalContext: CoroutineContext? = null
|
||||
) = CoroutineScope(
|
||||
coroutineContext + LinkedSupervisorJob(additionalContext)
|
||||
this + LinkedSupervisorJob(additionalContext)
|
||||
)
|
||||
fun CoroutineScope.LinkedSupervisorScope(
|
||||
additionalContext: CoroutineContext? = null
|
||||
) = coroutineContext.LinkedSupervisorScope(additionalContext)
|
||||
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -130,4 +130,4 @@ tasks.dokkaHtml {
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "$defaultAndroidSettingsPresetPath"
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
@@ -1,42 +1,32 @@
|
||||
allprojects {
|
||||
ext {
|
||||
projectByName = { name ->
|
||||
for (subproject in rootProject.subprojects) {
|
||||
if (subproject.name == name) {
|
||||
return subproject
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
File templatesFolder = new File("$rootProject.projectDir.absolutePath${File.separatorChar}gradle${File.separatorChar}templates")
|
||||
|
||||
internalProject = { name ->
|
||||
// if (releaseMode) {
|
||||
// "$group:$name:$version"
|
||||
// } else {
|
||||
// projectByName("$name")
|
||||
// }
|
||||
projectByName("$name")
|
||||
}
|
||||
Map properties = new HashMap<String, String>()
|
||||
|
||||
releaseMode = (project.hasProperty('RELEASE_MODE') && project.property('RELEASE_MODE') == "true") || System.getenv('RELEASE_MODE') == "true"
|
||||
// String compilerPluginVersionFromProperties = (String) project.properties["compose.kotlinCompilerPluginVersion"]
|
||||
// String compilerPluginVersionFromLibrariesVersions = libs.versions.compose.kotlin.get()
|
||||
// composePluginKotlinVersion = compilerPluginVersionFromProperties
|
||||
// if (compilerPluginVersionFromProperties == null) {
|
||||
// composePluginKotlinVersion = compilerPluginVersionFromLibrariesVersions
|
||||
// }
|
||||
|
||||
mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppJvmJsAndroidProject.gradle"
|
||||
mppProjectWithSerializationAndComposePresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerializationAndCompose.gradle"
|
||||
mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject.gradle"
|
||||
mppJvmJsLinuxMingwProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJvmJsLinuxMingwProject.gradle"
|
||||
mppJvmJsLinuxMingwLinuxArm64ProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJvmJsLinuxMingwLinuxArm64Project.gradle"
|
||||
mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJvmJsAndroidLinuxMingwLinuxArm64Project.gradle"
|
||||
mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject.gradle"
|
||||
|
||||
defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle"
|
||||
|
||||
publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle"
|
||||
publishJvmOnlyPath = "${rootProject.projectDir.absolutePath}/jvm.publish.gradle"
|
||||
if (templatesFolder.exists() && templatesFolder.isDirectory()) {
|
||||
templatesFolder.listFiles().each {
|
||||
properties[it.name - ".gradle"] = it.absolutePath
|
||||
}
|
||||
}
|
||||
|
||||
properties["projectByName"] = { name ->
|
||||
for (subproject in rootProject.subprojects) {
|
||||
if (subproject.name == name) {
|
||||
return subproject
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
properties["internalProject"] = { name ->
|
||||
projectByName("$name")
|
||||
}
|
||||
|
||||
allprojects {
|
||||
ext {
|
||||
releaseMode = (project.hasProperty('RELEASE_MODE') && project.property('RELEASE_MODE') == "true") || System.getenv('RELEASE_MODE') == "true"
|
||||
|
||||
properties.forEach { k, v ->
|
||||
it[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package dev.inmo.micro_utils.fsm.common
|
||||
|
||||
import dev.inmo.micro_utils.common.Optional
|
||||
import dev.inmo.micro_utils.common.onPresented
|
||||
import dev.inmo.micro_utils.coroutines.*
|
||||
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
|
||||
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
|
||||
@@ -118,23 +117,28 @@ open class DefaultStatesMachine <T: State>(
|
||||
* [launchStateHandling] will returns some [State] then [statesManager] [StatesManager.update] will be used, otherwise
|
||||
* [StatesManager.endChain].
|
||||
*/
|
||||
override fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions {
|
||||
(statesManager.getActiveStates().asFlow() + statesManager.onStartChain).subscribeSafelyWithoutExceptions(this) {
|
||||
launch { performStateUpdate(Optional.absent(), it, scope.LinkedSupervisorScope()) }
|
||||
}
|
||||
statesManager.onChainStateUpdated.subscribeSafelyWithoutExceptions(this) {
|
||||
launch { performStateUpdate(Optional.presented(it.first), it.second, scope.LinkedSupervisorScope()) }
|
||||
}
|
||||
statesManager.onEndChain.subscribeSafelyWithoutExceptions(this) { removedState ->
|
||||
launch {
|
||||
statesJobsMutex.withLock {
|
||||
val stateInMap = statesJobs.keys.firstOrNull { stateInMap -> stateInMap == removedState }
|
||||
if (stateInMap === removedState) {
|
||||
statesJobs[stateInMap] ?.cancel()
|
||||
override fun start(scope: CoroutineScope): Job {
|
||||
val supervisorScope = scope.LinkedSupervisorScope()
|
||||
supervisorScope.launchSafelyWithoutExceptions {
|
||||
(statesManager.getActiveStates().asFlow() + statesManager.onStartChain).subscribeSafelyWithoutExceptions(supervisorScope) {
|
||||
supervisorScope.launch { performStateUpdate(Optional.absent(), it, supervisorScope) }
|
||||
}
|
||||
statesManager.onChainStateUpdated.subscribeSafelyWithoutExceptions(supervisorScope) {
|
||||
supervisorScope.launch { performStateUpdate(Optional.presented(it.first), it.second, supervisorScope) }
|
||||
}
|
||||
statesManager.onEndChain.subscribeSafelyWithoutExceptions(supervisorScope) { removedState ->
|
||||
supervisorScope.launch {
|
||||
statesJobsMutex.withLock {
|
||||
val stateInMap = statesJobs.keys.firstOrNull { stateInMap -> stateInMap == removedState }
|
||||
if (stateInMap === removedState) {
|
||||
statesJobs[stateInMap] ?.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return supervisorScope.coroutineContext.job
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -15,5 +15,5 @@ crypto_js_version=4.1.1
|
||||
# Project data
|
||||
|
||||
group=dev.inmo
|
||||
version=0.20.52
|
||||
android_code_version=258
|
||||
version=0.22.3
|
||||
android_code_version=269
|
||||
|
@@ -1,45 +1,47 @@
|
||||
[versions]
|
||||
|
||||
kt = "1.9.23"
|
||||
kt-serialization = "1.6.3"
|
||||
kt-coroutines = "1.8.1"
|
||||
kt = "2.0.20"
|
||||
kt-serialization = "1.7.3"
|
||||
kt-coroutines = "1.9.0"
|
||||
|
||||
kslog = "1.3.4"
|
||||
kslog = "1.3.6"
|
||||
|
||||
jb-compose = "1.6.2"
|
||||
jb-exposed = "0.50.1"
|
||||
jb-compose = "1.7.0-beta02"
|
||||
jb-exposed = "0.54.0"
|
||||
jb-dokka = "1.9.20"
|
||||
|
||||
sqlite = "3.46.1.0"
|
||||
|
||||
korlibs = "5.4.0"
|
||||
uuid = "0.8.4"
|
||||
|
||||
ktor = "2.3.11"
|
||||
ktor = "2.3.12"
|
||||
|
||||
gh-release = "2.5.2"
|
||||
|
||||
koin = "3.5.6"
|
||||
koin = "4.0.0"
|
||||
|
||||
okio = "3.9.0"
|
||||
okio = "3.9.1"
|
||||
|
||||
ksp = "1.9.23-1.0.20"
|
||||
kotlin-poet = "1.16.0"
|
||||
ksp = "2.0.20-1.0.25"
|
||||
kotlin-poet = "1.18.1"
|
||||
|
||||
versions = "0.51.0"
|
||||
|
||||
android-gradle = "8.2.0"
|
||||
android-gradle = "8.2.2"
|
||||
dexcount = "4.0.0"
|
||||
|
||||
android-coreKtx = "1.13.1"
|
||||
android-recyclerView = "1.3.2"
|
||||
android-appCompat = "1.6.1"
|
||||
android-fragment = "1.7.1"
|
||||
android-espresso = "3.5.1"
|
||||
android-test = "1.1.5"
|
||||
android-compose-material3 = "1.2.1"
|
||||
android-appCompat = "1.7.0"
|
||||
android-fragment = "1.8.3"
|
||||
android-espresso = "3.6.1"
|
||||
android-test = "1.2.1"
|
||||
android-compose-material3 = "1.3.0"
|
||||
|
||||
android-props-minSdk = "21"
|
||||
android-props-compileSdk = "34"
|
||||
android-props-buildTools = "34.0.0"
|
||||
android-props-compileSdk = "35"
|
||||
android-props-buildTools = "35.0.0"
|
||||
|
||||
[libraries]
|
||||
|
||||
@@ -52,6 +54,7 @@ kt-serialization-cbor = { module = "org.jetbrains.kotlinx:kotlinx-serialization-
|
||||
kt-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kt-coroutines" }
|
||||
kt-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kt-coroutines" }
|
||||
kt-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kt-coroutines" }
|
||||
kt-coroutines-debug = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-debug", version.ref = "kt-coroutines" }
|
||||
|
||||
|
||||
ktor-io = { module = "io.ktor:ktor-io", version.ref = "ktor" }
|
||||
@@ -79,12 +82,14 @@ koin = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
||||
|
||||
|
||||
jb-exposed = { module = "org.jetbrains.exposed:exposed-core", version.ref = "jb-exposed" }
|
||||
jb-exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "jb-exposed" }
|
||||
sqlite = { module = "org.xerial:sqlite-jdbc", version.ref = "sqlite" }
|
||||
|
||||
jb-compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "jb-compose" }
|
||||
|
||||
android-coreKtx = { module = "androidx.core:core-ktx", version.ref = "android-coreKtx" }
|
||||
android-recyclerView = { module = "androidx.recyclerview:recyclerview", version.ref = "android-recyclerView" }
|
||||
android-appCompat-resources = { module = "androidx.appcompat:appcompat-resources", version.ref = "android-appCompat" }
|
||||
android-compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "android-compose-material3" }
|
||||
android-fragment = { module = "androidx.fragment:fragment", version.ref = "android-fragment" }
|
||||
android-espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "android-espresso" }
|
||||
android-test-junit = { module = "androidx.test.ext:junit", version.ref = "android-test" }
|
||||
@@ -113,5 +118,6 @@ buildscript-android-dexcount = { module = "com.getkeepsafe.dexcount:dexcount-gra
|
||||
[plugins]
|
||||
|
||||
jb-compose = { id = "org.jetbrains.compose", version.ref = "jb-compose" }
|
||||
kt-jb-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kt" }
|
||||
|
||||
versions = { id = "com.github.ben-manes.versions", version.ref = "versions" }
|
||||
|
@@ -6,6 +6,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion libs.versions.android.props.minSdk.get().toInteger()
|
||||
compileSdkVersion libs.versions.android.props.compileSdk.get().toInteger()
|
||||
targetSdkVersion libs.versions.android.props.compileSdk.get().toInteger()
|
||||
versionCode "${android_code_version}".toInteger()
|
||||
versionName "$version"
|
@@ -1,7 +1,7 @@
|
||||
project.version = "$version"
|
||||
project.group = "$group"
|
||||
|
||||
apply from: "$publishGradlePath"
|
||||
apply from: "$publish"
|
||||
|
||||
kotlin {
|
||||
androidTarget {
|
||||
@@ -29,7 +29,7 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "$defaultAndroidSettingsPresetPath"
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
@@ -1,7 +1,7 @@
|
||||
project.version = "$version"
|
||||
project.group = "$group"
|
||||
|
||||
apply from: "$publishGradlePath"
|
||||
apply from: "$publish"
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
@@ -12,8 +12,20 @@ kotlin {
|
||||
}
|
||||
}
|
||||
js (IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
browser {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
androidTarget {
|
||||
publishAllLibraryVariants()
|
||||
@@ -25,11 +37,13 @@ kotlin {
|
||||
}
|
||||
linuxX64()
|
||||
mingwX64()
|
||||
linuxArm64()
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib')
|
||||
implementation compose.runtime
|
||||
api libs.kt.serialization
|
||||
}
|
||||
}
|
||||
@@ -40,40 +54,53 @@ kotlin {
|
||||
implementation libs.kt.coroutines.test
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
jsTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
|
||||
androidUnitTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
implementation libs.android.test.junit
|
||||
implementation libs.android.espresso
|
||||
implementation compose.uiTest
|
||||
}
|
||||
}
|
||||
androidInstrumentedTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
implementation libs.android.test.junit
|
||||
implementation libs.android.espresso
|
||||
}
|
||||
}
|
||||
mingwX64Test {
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
implementation compose.desktop.currentOs
|
||||
}
|
||||
}
|
||||
linuxX64Test {
|
||||
jvmTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
implementation compose.uiTest
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation compose.web.core
|
||||
}
|
||||
}
|
||||
jsTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
}
|
||||
}
|
||||
nativeMain.dependsOn commonMain
|
||||
linuxX64Main.dependsOn nativeMain
|
||||
mingwX64Main.dependsOn nativeMain
|
||||
linuxArm64Main.dependsOn nativeMain
|
||||
|
||||
androidMain.dependsOn jvmMain
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "$defaultAndroidSettingsPresetPath"
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
@@ -1,7 +1,7 @@
|
||||
project.version = "$version"
|
||||
project.group = "$group"
|
||||
|
||||
apply from: "$publishGradlePath"
|
||||
apply from: "$publish"
|
||||
|
||||
kotlin {
|
||||
jvm {
|
@@ -1,7 +1,7 @@
|
||||
project.version = "$version"
|
||||
project.group = "$group"
|
||||
|
||||
apply from: "$publishGradlePath"
|
||||
apply from: "$publish"
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
@@ -12,8 +12,20 @@ kotlin {
|
||||
}
|
||||
}
|
||||
js (IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
browser {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
androidTarget {
|
||||
publishAllLibraryVariants()
|
||||
@@ -36,7 +48,7 @@ kotlin {
|
||||
}
|
||||
commonTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-common')
|
||||
implementation kotlin('test')
|
||||
implementation kotlin('test-annotations-common')
|
||||
implementation libs.kt.coroutines.test
|
||||
}
|
||||
@@ -57,7 +69,6 @@ kotlin {
|
||||
jsTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
nativeMain.dependsOn commonMain
|
||||
@@ -69,7 +80,7 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "$defaultAndroidSettingsPresetPath"
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
@@ -1,7 +1,7 @@
|
||||
project.version = "$version"
|
||||
project.group = "$group"
|
||||
|
||||
apply from: "$publishGradlePath"
|
||||
apply from: "$publish"
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
@@ -12,8 +12,20 @@ kotlin {
|
||||
}
|
||||
}
|
||||
js (IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
browser {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
linuxX64()
|
||||
mingwX64()
|
||||
@@ -41,17 +53,6 @@ kotlin {
|
||||
jsTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
mingwX64Test {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
linuxX64Test {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
project.version = "$version"
|
||||
project.group = "$group"
|
||||
|
||||
apply from: "$publishGradlePath"
|
||||
apply from: "$publish"
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
@@ -12,8 +12,20 @@ kotlin {
|
||||
}
|
||||
}
|
||||
js (IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
browser {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
androidTarget {
|
||||
publishAllLibraryVariants()
|
||||
@@ -58,7 +70,6 @@ kotlin {
|
||||
jsTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
androidUnitTest {
|
||||
@@ -69,17 +80,10 @@ kotlin {
|
||||
implementation compose.uiTest
|
||||
}
|
||||
}
|
||||
androidInstrumentedTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
implementation libs.android.test.junit
|
||||
implementation libs.android.espresso
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "$defaultAndroidSettingsPresetPath"
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
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.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppProjectWithSerializationPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -2,7 +2,7 @@ plugins {
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
}
|
||||
|
||||
apply from: "$publishJvmOnlyPath"
|
||||
apply from: "$publish_jvm"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@@ -5,7 +5,7 @@ plugins {
|
||||
id "com.google.devtools.ksp"
|
||||
}
|
||||
|
||||
apply from: "$mppProjectWithSerializationPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
|
||||
kotlin {
|
||||
|
7
ksp/classcasts/build.gradle
Normal file
7
ksp/classcasts/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: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
21
ksp/classcasts/generator/build.gradle
Normal file
21
ksp/classcasts/generator/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
}
|
||||
|
||||
apply from: "$publish_jvm"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(":micro_utils.ksp.generator")
|
||||
api project(":micro_utils.ksp.classcasts")
|
||||
api libs.kotlin.poet
|
||||
api libs.ksp
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
90
ksp/classcasts/generator/src/main/kotlin/ClassCastsFiller.kt
Normal file
90
ksp/classcasts/generator/src/main/kotlin/ClassCastsFiller.kt
Normal file
@@ -0,0 +1,90 @@
|
||||
package dev.inmo.micro_utils.ksp.classcasts.generator
|
||||
|
||||
import com.google.devtools.ksp.symbol.ClassKind
|
||||
import com.google.devtools.ksp.symbol.KSClassDeclaration
|
||||
import com.squareup.kotlinpoet.*
|
||||
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||
import com.squareup.kotlinpoet.ksp.toClassName
|
||||
import com.squareup.kotlinpoet.ksp.toTypeName
|
||||
|
||||
|
||||
private fun FileSpec.Builder.addTopLevelImport(className: ClassName) {
|
||||
className.topLevelClassName().let {
|
||||
addImport(it.packageName, it.simpleNames)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FileSpec.Builder.createTypeDefinition(ksClassDeclaration: KSClassDeclaration): TypeName {
|
||||
val className = ksClassDeclaration.toClassName()
|
||||
return if (ksClassDeclaration.typeParameters.isNotEmpty()) {
|
||||
className.parameterizedBy(
|
||||
ksClassDeclaration.typeParameters.map {
|
||||
it.bounds.first().resolve().also {
|
||||
val typeClassName = it.toClassName()
|
||||
addTopLevelImport(typeClassName)
|
||||
}.toTypeName()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
className
|
||||
}
|
||||
}
|
||||
|
||||
internal fun FileSpec.Builder.fill(
|
||||
sourceKSClassDeclaration: KSClassDeclaration,
|
||||
subtypes: Set<KSClassDeclaration>
|
||||
) {
|
||||
subtypes.forEach { targetClassDeclaration ->
|
||||
val sourceClassName = sourceKSClassDeclaration.toClassName()
|
||||
val targetClassClassName = targetClassDeclaration.toClassName()
|
||||
val targetClassTypeDefinition = createTypeDefinition(targetClassDeclaration)
|
||||
val simpleName = targetClassDeclaration.simpleName.asString()
|
||||
val withFirstLowerCase = simpleName.replaceFirstChar { it.lowercase() }
|
||||
val castedOrNullName = "${withFirstLowerCase}OrNull"
|
||||
|
||||
addTopLevelImport(targetClassClassName)
|
||||
addFunction(
|
||||
FunSpec.builder(castedOrNullName).apply {
|
||||
receiver(sourceClassName)
|
||||
addCode(
|
||||
"return this as? %L",
|
||||
targetClassTypeDefinition
|
||||
)
|
||||
returns(targetClassTypeDefinition.copy(nullable = true))
|
||||
addModifiers(KModifier.INLINE)
|
||||
}.build()
|
||||
)
|
||||
addFunction(
|
||||
FunSpec.builder("${withFirstLowerCase}OrThrow").apply {
|
||||
receiver(sourceClassName)
|
||||
addCode(
|
||||
"return this as %L",
|
||||
targetClassTypeDefinition
|
||||
)
|
||||
returns(targetClassTypeDefinition)
|
||||
addModifiers(KModifier.INLINE)
|
||||
}.build()
|
||||
)
|
||||
addFunction(
|
||||
FunSpec.builder("if$simpleName").apply {
|
||||
val genericType = TypeVariableName("T", null)
|
||||
addTypeVariable(genericType)
|
||||
receiver(sourceClassName)
|
||||
addParameter(
|
||||
"block",
|
||||
LambdaTypeName.get(
|
||||
null,
|
||||
targetClassTypeDefinition,
|
||||
returnType = genericType
|
||||
)
|
||||
)
|
||||
addCode(
|
||||
"return ${castedOrNullName}() ?.let(block)",
|
||||
targetClassTypeDefinition
|
||||
)
|
||||
returns(genericType.copy(nullable = true))
|
||||
addModifiers(KModifier.INLINE)
|
||||
}.build()
|
||||
)
|
||||
}
|
||||
}
|
119
ksp/classcasts/generator/src/main/kotlin/Processor.kt
Normal file
119
ksp/classcasts/generator/src/main/kotlin/Processor.kt
Normal file
@@ -0,0 +1,119 @@
|
||||
package dev.inmo.micro_utils.ksp.classcasts.generator
|
||||
|
||||
import com.google.devtools.ksp.KspExperimental
|
||||
import com.google.devtools.ksp.getAllSuperTypes
|
||||
import com.google.devtools.ksp.getAnnotationsByType
|
||||
import com.google.devtools.ksp.isAnnotationPresent
|
||||
import com.google.devtools.ksp.processing.CodeGenerator
|
||||
import com.google.devtools.ksp.processing.Resolver
|
||||
import com.google.devtools.ksp.processing.SymbolProcessor
|
||||
import com.google.devtools.ksp.symbol.*
|
||||
import com.squareup.kotlinpoet.*
|
||||
import com.squareup.kotlinpoet.ksp.toClassName
|
||||
import dev.inmo.micro_ksp.generator.writeFile
|
||||
import dev.inmo.micro_utils.ksp.classcasts.ClassCastsExcluded
|
||||
import dev.inmo.micro_utils.ksp.classcasts.ClassCastsIncluded
|
||||
import java.io.File
|
||||
|
||||
class Processor(
|
||||
private val codeGenerator: CodeGenerator
|
||||
) : SymbolProcessor {
|
||||
@OptIn(KspExperimental::class)
|
||||
private fun FileSpec.Builder.generateClassCasts(
|
||||
ksClassDeclaration: KSClassDeclaration,
|
||||
resolver: Resolver
|
||||
) {
|
||||
val rootAnnotation = ksClassDeclaration.getAnnotationsByType(ClassCastsIncluded::class).first()
|
||||
val (includeRegex: Regex?, excludeRegex: Regex?) = rootAnnotation.let {
|
||||
it.typesRegex.takeIf { it.isNotEmpty() } ?.let(::Regex) to it.excludeRegex.takeIf { it.isNotEmpty() } ?.let(::Regex)
|
||||
}
|
||||
val classesSubtypes = mutableMapOf<KSClassDeclaration, MutableSet<KSClassDeclaration>>()
|
||||
|
||||
fun KSClassDeclaration.checkSupertypeLevel(levelsAllowed: Int?): Boolean {
|
||||
val supertypes by lazy {
|
||||
superTypes.map { it.resolve().declaration }
|
||||
}
|
||||
return when {
|
||||
levelsAllowed == null -> true
|
||||
levelsAllowed <= 0 -> false
|
||||
supertypes.any { it == ksClassDeclaration } -> true
|
||||
else -> supertypes.any {
|
||||
(it as? KSClassDeclaration) ?.checkSupertypeLevel(levelsAllowed - 1) == true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleDeclaration(ksDeclarationContainer: KSDeclarationContainer) {
|
||||
ksDeclarationContainer.declarations.forEach { potentialSubtype ->
|
||||
val simpleName = potentialSubtype.simpleName.getShortName()
|
||||
when {
|
||||
potentialSubtype === ksClassDeclaration -> {}
|
||||
potentialSubtype.isAnnotationPresent(ClassCastsExcluded::class) -> return@forEach
|
||||
potentialSubtype !is KSClassDeclaration || !potentialSubtype.checkSupertypeLevel(rootAnnotation.levelsToInclude.takeIf { it >= 0 }) -> return@forEach
|
||||
excludeRegex ?.matches(simpleName) == true -> return@forEach
|
||||
includeRegex ?.matches(simpleName) == false -> {}
|
||||
else -> classesSubtypes.getOrPut(ksClassDeclaration) { mutableSetOf() }.add(potentialSubtype)
|
||||
}
|
||||
handleDeclaration(potentialSubtype as? KSDeclarationContainer ?: return@forEach)
|
||||
}
|
||||
}
|
||||
resolver.getAllFiles().forEach {
|
||||
handleDeclaration(it)
|
||||
}
|
||||
fun fillWithSealeds(current: KSClassDeclaration) {
|
||||
current.getSealedSubclasses().forEach {
|
||||
val simpleName = it.simpleName.getShortName()
|
||||
if (
|
||||
includeRegex ?.matches(simpleName) == false
|
||||
|| excludeRegex ?.matches(simpleName) == true
|
||||
|| it.isAnnotationPresent(ClassCastsExcluded::class)
|
||||
) {
|
||||
return@forEach
|
||||
}
|
||||
classesSubtypes.getOrPut(ksClassDeclaration) { mutableSetOf() }.add(it)
|
||||
fillWithSealeds(it)
|
||||
}
|
||||
}
|
||||
fillWithSealeds(ksClassDeclaration)
|
||||
|
||||
addAnnotation(
|
||||
AnnotationSpec.builder(Suppress::class).apply {
|
||||
addMember("\"unused\"")
|
||||
addMember("\"RemoveRedundantQualifierName\"")
|
||||
addMember("\"RedundantVisibilityModifier\"")
|
||||
addMember("\"NOTHING_TO_INLINE\"")
|
||||
addMember("\"UNCHECKED_CAST\"")
|
||||
addMember("\"OPT_IN_USAGE\"")
|
||||
useSiteTarget(AnnotationSpec.UseSiteTarget.FILE)
|
||||
}.build()
|
||||
)
|
||||
fill(
|
||||
ksClassDeclaration,
|
||||
classesSubtypes.values.flatten().toSet()
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(KspExperimental::class)
|
||||
override fun process(resolver: Resolver): List<KSAnnotated> {
|
||||
(resolver.getSymbolsWithAnnotation(ClassCastsIncluded::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach {
|
||||
val prefix = it.getAnnotationsByType(ClassCastsIncluded::class).first().outputFilePrefix
|
||||
it.writeFile(prefix = prefix, suffix = "ClassCasts") {
|
||||
FileSpec.builder(
|
||||
it.packageName.asString(),
|
||||
"${it.simpleName.getShortName()}ClassCasts"
|
||||
).apply {
|
||||
addFileComment(
|
||||
"""
|
||||
THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
|
||||
TO REGENERATE IT JUST DELETE FILE
|
||||
ORIGINAL FILE: ${it.containingFile ?.fileName}
|
||||
""".trimIndent()
|
||||
)
|
||||
generateClassCasts(it, resolver)
|
||||
}.build()
|
||||
}
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
}
|
11
ksp/classcasts/generator/src/main/kotlin/Provider.kt
Normal file
11
ksp/classcasts/generator/src/main/kotlin/Provider.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package dev.inmo.micro_utils.ksp.classcasts.generator
|
||||
|
||||
import com.google.devtools.ksp.processing.SymbolProcessor
|
||||
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
|
||||
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
||||
|
||||
class Provider : SymbolProcessorProvider {
|
||||
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = Processor(
|
||||
environment.codeGenerator
|
||||
)
|
||||
}
|
@@ -0,0 +1 @@
|
||||
dev.inmo.micro_utils.ksp.classcasts.generator.Provider
|
27
ksp/classcasts/generator/test/build.gradle
Normal file
27
ksp/classcasts/generator/test/build.gradle
Normal file
@@ -0,0 +1,27 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.google.devtools.ksp"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api project(":micro_utils.ksp.classcasts")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
add("kspCommonMainMetadata", project(":micro_utils.ksp.classcasts.generator"))
|
||||
}
|
||||
|
||||
ksp {
|
||||
}
|
15
ksp/classcasts/generator/test/src/commonMain/kotlin/Test.kt
Normal file
15
ksp/classcasts/generator/test/src/commonMain/kotlin/Test.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
package dev.inmo.micro_utils.ksp.classcasts.generator.test
|
||||
|
||||
import dev.inmo.micro_utils.ksp.classcasts.ClassCastsExcluded
|
||||
import dev.inmo.micro_utils.ksp.classcasts.ClassCastsIncluded
|
||||
|
||||
@ClassCastsIncluded(levelsToInclude = 1)
|
||||
sealed interface Test {
|
||||
object A : Test
|
||||
@ClassCastsExcluded
|
||||
object B : Test // Will not be included in class casts due to annotation ClassCastsExcluded
|
||||
object C : Test
|
||||
interface D : Test {
|
||||
object DD : D // Will not be included in class casts due to levelsToInclude
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
// THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
|
||||
// TO REGENERATE IT JUST DELETE FILE
|
||||
// ORIGINAL FILE: Test.kt
|
||||
@file:Suppress(
|
||||
"unused",
|
||||
"RemoveRedundantQualifierName",
|
||||
"RedundantVisibilityModifier",
|
||||
"NOTHING_TO_INLINE",
|
||||
"UNCHECKED_CAST",
|
||||
"OPT_IN_USAGE",
|
||||
)
|
||||
|
||||
package dev.inmo.micro_utils.ksp.classcasts.generator.test
|
||||
|
||||
import dev.inmo.micro_utils.ksp.classcasts.generator.test.Test
|
||||
import kotlin.Suppress
|
||||
|
||||
public inline fun Test.aOrNull(): Test.A? = this as?
|
||||
dev.inmo.micro_utils.ksp.classcasts.generator.test.Test.A
|
||||
|
||||
public inline fun Test.aOrThrow(): Test.A = this as
|
||||
dev.inmo.micro_utils.ksp.classcasts.generator.test.Test.A
|
||||
|
||||
public inline fun <T> Test.ifA(block: (Test.A) -> T): T? = aOrNull() ?.let(block)
|
||||
|
||||
public inline fun Test.cOrNull(): Test.C? = this as?
|
||||
dev.inmo.micro_utils.ksp.classcasts.generator.test.Test.C
|
||||
|
||||
public inline fun Test.cOrThrow(): Test.C = this as
|
||||
dev.inmo.micro_utils.ksp.classcasts.generator.test.Test.C
|
||||
|
||||
public inline fun <T> Test.ifC(block: (Test.C) -> T): T? = cOrNull() ?.let(block)
|
||||
|
||||
public inline fun Test.dOrNull(): Test.D? = this as?
|
||||
dev.inmo.micro_utils.ksp.classcasts.generator.test.Test.D
|
||||
|
||||
public inline fun Test.dOrThrow(): Test.D = this as
|
||||
dev.inmo.micro_utils.ksp.classcasts.generator.test.Test.D
|
||||
|
||||
public inline fun <T> Test.ifD(block: (Test.D) -> T): T? = dOrNull() ?.let(block)
|
@@ -0,0 +1,5 @@
|
||||
package dev.inmo.micro_utils.ksp.classcasts
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ClassCastsExcluded
|
10
ksp/classcasts/src/commonMain/kotlin/ClassCastsIncluded.kt
Normal file
10
ksp/classcasts/src/commonMain/kotlin/ClassCastsIncluded.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package dev.inmo.micro_utils.ksp.classcasts
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ClassCastsIncluded(
|
||||
val typesRegex: String = "",
|
||||
val excludeRegex: String = "",
|
||||
val outputFilePrefix: String = "",
|
||||
val levelsToInclude: Int = -1
|
||||
)
|
20
ksp/generator/build.gradle
Normal file
20
ksp/generator/build.gradle
Normal file
@@ -0,0 +1,20 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
}
|
||||
|
||||
apply from: "$publish_jvm"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(":micro_utils.common")
|
||||
api libs.kotlin.poet
|
||||
api libs.ksp
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
49
ksp/generator/src/main/kotlin/FilesWorkaround.kt
Normal file
49
ksp/generator/src/main/kotlin/FilesWorkaround.kt
Normal file
@@ -0,0 +1,49 @@
|
||||
package dev.inmo.micro_ksp.generator
|
||||
|
||||
import com.google.devtools.ksp.symbol.KSClassDeclaration
|
||||
import com.google.devtools.ksp.symbol.KSFile
|
||||
import com.squareup.kotlinpoet.FileSpec
|
||||
import java.io.File
|
||||
|
||||
fun KSClassDeclaration.writeFile(
|
||||
prefix: String = "",
|
||||
suffix: String = "",
|
||||
relatedPath: String = "",
|
||||
force: Boolean = false,
|
||||
fileSpecBuilder: () -> FileSpec
|
||||
) {
|
||||
val containingFile = containingFile!!
|
||||
File(
|
||||
File(
|
||||
File(containingFile.filePath).parent,
|
||||
relatedPath
|
||||
),
|
||||
"$prefix${simpleName.asString()}$suffix.kt"
|
||||
).takeIf { force || !it.exists() } ?.apply {
|
||||
parentFile.mkdirs()
|
||||
writer().use { writer ->
|
||||
fileSpecBuilder().writeTo(writer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun KSFile.writeFile(
|
||||
prefix: String = "",
|
||||
suffix: String = "",
|
||||
relatedPath: String = "",
|
||||
force: Boolean = false,
|
||||
fileSpecBuilder: () -> FileSpec
|
||||
) {
|
||||
File(
|
||||
File(
|
||||
File(filePath).parent,
|
||||
relatedPath
|
||||
),
|
||||
"$prefix${fileName.dropLastWhile { it != '.' }.removeSuffix(".")}$suffix.kt"
|
||||
).takeIf { force || !it.exists() } ?.apply {
|
||||
parentFile.mkdirs()
|
||||
writer().use { writer ->
|
||||
fileSpecBuilder().writeTo(writer)
|
||||
}
|
||||
}
|
||||
}
|
30
ksp/generator/src/main/kotlin/WalkOnKSFiles.kt
Normal file
30
ksp/generator/src/main/kotlin/WalkOnKSFiles.kt
Normal file
@@ -0,0 +1,30 @@
|
||||
package dev.inmo.micro_ksp.generator
|
||||
|
||||
import com.google.devtools.ksp.getAllSuperTypes
|
||||
import com.google.devtools.ksp.symbol.KSAnnotated
|
||||
import com.google.devtools.ksp.symbol.KSClassDeclaration
|
||||
import com.google.devtools.ksp.symbol.KSDeclarationContainer
|
||||
import com.google.devtools.ksp.symbol.KSFile
|
||||
|
||||
fun KSClassDeclaration.findSubClasses(subSymbol: KSAnnotated): Sequence<KSClassDeclaration> {
|
||||
return when (subSymbol) {
|
||||
is KSClassDeclaration -> if (subSymbol.getAllSuperTypes().map { it.declaration }.contains(this)) {
|
||||
sequenceOf(subSymbol)
|
||||
} else {
|
||||
sequenceOf()
|
||||
}
|
||||
else -> sequenceOf()
|
||||
} + if (subSymbol is KSDeclarationContainer) {
|
||||
subSymbol.declarations.flatMap {
|
||||
findSubClasses(it)
|
||||
}
|
||||
} else {
|
||||
sequenceOf()
|
||||
}
|
||||
}
|
||||
|
||||
fun KSClassDeclaration.findSubClasses(files: Sequence<KSAnnotated>): Sequence<KSClassDeclaration> {
|
||||
return files.flatMap {
|
||||
findSubClasses(it)
|
||||
}
|
||||
}
|
7
ksp/sealed/build.gradle
Normal file
7
ksp/sealed/build.gradle
Normal file
@@ -0,0 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
21
ksp/sealed/generator/build.gradle
Normal file
21
ksp/sealed/generator/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
}
|
||||
|
||||
apply from: "$publish_jvm"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(":micro_utils.ksp.generator")
|
||||
api project(":micro_utils.ksp.sealed")
|
||||
api libs.kotlin.poet
|
||||
api libs.ksp
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
130
ksp/sealed/generator/src/main/kotlin/Processor.kt
Normal file
130
ksp/sealed/generator/src/main/kotlin/Processor.kt
Normal file
@@ -0,0 +1,130 @@
|
||||
package dev.inmo.micro_utils.ksp.sealed.generator
|
||||
|
||||
import com.google.devtools.ksp.KspExperimental
|
||||
import com.google.devtools.ksp.getAnnotationsByType
|
||||
import com.google.devtools.ksp.processing.CodeGenerator
|
||||
import com.google.devtools.ksp.processing.Resolver
|
||||
import com.google.devtools.ksp.processing.SymbolProcessor
|
||||
import com.google.devtools.ksp.symbol.*
|
||||
import com.squareup.kotlinpoet.ClassName
|
||||
import com.squareup.kotlinpoet.CodeBlock
|
||||
import com.squareup.kotlinpoet.FileSpec
|
||||
import com.squareup.kotlinpoet.FunSpec
|
||||
import com.squareup.kotlinpoet.KModifier
|
||||
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||
import com.squareup.kotlinpoet.PropertySpec
|
||||
import com.squareup.kotlinpoet.asTypeName
|
||||
import com.squareup.kotlinpoet.ksp.toClassName
|
||||
import dev.inmo.micro_ksp.generator.findSubClasses
|
||||
import dev.inmo.micro_ksp.generator.writeFile
|
||||
import dev.inmo.microutils.kps.sealed.GenerateSealedWorkaround
|
||||
import java.io.File
|
||||
|
||||
class Processor(
|
||||
private val codeGenerator: CodeGenerator
|
||||
) : SymbolProcessor {
|
||||
private fun KSClassDeclaration.findSealedConnection(potentialSealedParent: KSClassDeclaration): Boolean {
|
||||
val targetClassname = potentialSealedParent.qualifiedName ?.asString()
|
||||
return superTypes.any {
|
||||
val itAsDeclaration = it.resolve().declaration as? KSClassDeclaration ?: return@any false
|
||||
targetClassname == (itAsDeclaration.qualifiedName ?.asString()) || (itAsDeclaration.getSealedSubclasses().any() && itAsDeclaration.findSealedConnection(potentialSealedParent))
|
||||
}
|
||||
}
|
||||
|
||||
private fun KSClassDeclaration.resolveSubclasses(
|
||||
searchIn: Sequence<KSAnnotated>,
|
||||
allowNonSealed: Boolean
|
||||
): Sequence<KSClassDeclaration> {
|
||||
return findSubClasses(searchIn).let {
|
||||
if (allowNonSealed) {
|
||||
it
|
||||
} else {
|
||||
it.filter {
|
||||
it.findSealedConnection(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(KspExperimental::class)
|
||||
private fun FileSpec.Builder.generateSealedWorkaround(
|
||||
ksClassDeclaration: KSClassDeclaration,
|
||||
resolver: Resolver
|
||||
) {
|
||||
val annotation = ksClassDeclaration.getAnnotationsByType(GenerateSealedWorkaround::class).first()
|
||||
val subClasses = ksClassDeclaration.resolveSubclasses(
|
||||
searchIn = resolver.getAllFiles(),
|
||||
allowNonSealed = annotation.includeNonSealedSubTypes
|
||||
).distinct()
|
||||
val subClassesNames = subClasses.filter {
|
||||
when (it.classKind) {
|
||||
ClassKind.ENUM_ENTRY,
|
||||
ClassKind.OBJECT -> true
|
||||
ClassKind.INTERFACE,
|
||||
ClassKind.CLASS,
|
||||
ClassKind.ENUM_CLASS,
|
||||
ClassKind.ANNOTATION_CLASS -> false
|
||||
}
|
||||
}.filter {
|
||||
it.getAnnotationsByType(GenerateSealedWorkaround.Exclude::class).count() == 0
|
||||
}.sortedBy {
|
||||
(it.getAnnotationsByType(GenerateSealedWorkaround.Order::class).firstOrNull()) ?.order ?: 0
|
||||
}.map {
|
||||
it.toClassName()
|
||||
}.toList()
|
||||
val className = ksClassDeclaration.toClassName()
|
||||
val setType = Set::class.asTypeName().parameterizedBy(
|
||||
ksClassDeclaration.toClassName()
|
||||
)
|
||||
addProperty(
|
||||
PropertySpec.builder(
|
||||
"values",
|
||||
setType
|
||||
).apply {
|
||||
modifiers.add(
|
||||
KModifier.PRIVATE
|
||||
)
|
||||
initializer(
|
||||
CodeBlock.of(
|
||||
"""setOf(${subClassesNames.joinToString(",\n") {it.simpleNames.joinToString(".")}})"""
|
||||
)
|
||||
)
|
||||
}.build()
|
||||
)
|
||||
addFunction(
|
||||
FunSpec.builder("values").apply {
|
||||
receiver(ClassName(className.packageName, *className.simpleNames.toTypedArray(), "Companion"))
|
||||
returns(setType)
|
||||
addCode(
|
||||
CodeBlock.of(
|
||||
"""return values"""
|
||||
)
|
||||
)
|
||||
}.build()
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(KspExperimental::class)
|
||||
override fun process(resolver: Resolver): List<KSAnnotated> {
|
||||
(resolver.getSymbolsWithAnnotation(GenerateSealedWorkaround::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach {
|
||||
val prefix = it.getAnnotationsByType(GenerateSealedWorkaround::class).first().prefix
|
||||
it.writeFile(prefix = prefix, suffix = "SealedWorkaround") {
|
||||
FileSpec.builder(
|
||||
it.packageName.asString(),
|
||||
"${it.simpleName.getShortName()}SealedWorkaround"
|
||||
).apply {
|
||||
addFileComment(
|
||||
"""
|
||||
THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
|
||||
TO REGENERATE IT JUST DELETE FILE
|
||||
ORIGINAL FILE: ${it.containingFile ?.fileName}
|
||||
""".trimIndent()
|
||||
)
|
||||
generateSealedWorkaround(it, resolver)
|
||||
}.build()
|
||||
}
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
}
|
11
ksp/sealed/generator/src/main/kotlin/Provider.kt
Normal file
11
ksp/sealed/generator/src/main/kotlin/Provider.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package dev.inmo.micro_utils.ksp.sealed.generator
|
||||
|
||||
import com.google.devtools.ksp.processing.SymbolProcessor
|
||||
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
|
||||
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
||||
|
||||
class Provider : SymbolProcessorProvider {
|
||||
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = Processor(
|
||||
environment.codeGenerator
|
||||
)
|
||||
}
|
@@ -0,0 +1 @@
|
||||
dev.inmo.micro_utils.ksp.sealed.generator.Provider
|
27
ksp/sealed/generator/test/build.gradle
Normal file
27
ksp/sealed/generator/test/build.gradle
Normal file
@@ -0,0 +1,27 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.google.devtools.ksp"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api project(":micro_utils.ksp.sealed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
add("kspCommonMainMetadata", project(":micro_utils.ksp.sealed.generator"))
|
||||
}
|
||||
|
||||
ksp {
|
||||
}
|
16
ksp/sealed/generator/test/src/commonMain/kotlin/Test.kt
Normal file
16
ksp/sealed/generator/test/src/commonMain/kotlin/Test.kt
Normal file
@@ -0,0 +1,16 @@
|
||||
package dev.inmo.micro_utils.ksp.sealed.generator.test
|
||||
|
||||
import dev.inmo.microutils.kps.sealed.GenerateSealedWorkaround
|
||||
|
||||
@GenerateSealedWorkaround
|
||||
sealed interface Test {
|
||||
@GenerateSealedWorkaround.Order(2)
|
||||
object A : Test
|
||||
@GenerateSealedWorkaround.Exclude
|
||||
object B : Test
|
||||
@GenerateSealedWorkaround.Order(0)
|
||||
object C : Test
|
||||
|
||||
// Required for successful sealed workaround generation
|
||||
companion object
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
// THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
|
||||
// TO REGENERATE IT JUST DELETE FILE
|
||||
// ORIGINAL FILE: Test.kt
|
||||
package dev.inmo.micro_utils.ksp.`sealed`.generator.test
|
||||
|
||||
import kotlin.collections.Set
|
||||
|
||||
private val values: Set<Test> = setOf(Test.C,
|
||||
Test.A)
|
||||
|
||||
public fun Test.Companion.values(): Set<Test> = values
|
15
ksp/sealed/src/commonMain/kotlin/GenerateSealedWorkaround.kt
Normal file
15
ksp/sealed/src/commonMain/kotlin/GenerateSealedWorkaround.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
package dev.inmo.microutils.kps.sealed
|
||||
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class GenerateSealedWorkaround(
|
||||
val prefix: String = "",
|
||||
val includeNonSealedSubTypes: Boolean = false
|
||||
) {
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class Order(val order: Int)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class Exclude
|
||||
}
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -3,7 +3,7 @@ plugins {
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
}
|
||||
|
||||
apply from: "$mppJavaProjectPresetPath"
|
||||
apply from: "$mppJavaProject"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -41,7 +41,7 @@ class TemporalFilesRoutingConfigurator(
|
||||
filesMutex: Mutex,
|
||||
onNewFileFlow: Flow<TemporalFileId>
|
||||
): Job = scope.launchSafelyWithoutExceptions {
|
||||
while (isActive) {
|
||||
while (currentCoroutineContext().isActive) {
|
||||
val filesWithCreationInfo = filesMap.mapNotNull { (fileId, file) ->
|
||||
fileId to ((Files.getAttribute(file.toPath(), "creationTime") as? FileTime) ?.toMillis() ?: return@mapNotNull null)
|
||||
}
|
||||
|
@@ -4,4 +4,4 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
1
language_codes/generator/full.json
Normal file
1
language_codes/generator/full.json
Normal file
File diff suppressed because one or more lines are too long
@@ -22,17 +22,24 @@ private const val baseClassSerializerAnnotationName = "@Serializable(${baseClass
|
||||
|
||||
@Serializable
|
||||
private data class LanguageCode(
|
||||
@SerialName("alpha2")
|
||||
val tag: String,
|
||||
@SerialName("English")
|
||||
val title: String
|
||||
)
|
||||
val title: String,
|
||||
@SerialName("alpha2")
|
||||
val alpha: String? = null,
|
||||
@SerialName("alpha3-b")
|
||||
val alpha2: String? = null,
|
||||
@SerialName("alpha3-t")
|
||||
val alpha3: String? = null,
|
||||
) {
|
||||
val tag: String
|
||||
get() = alpha ?: alpha2 ?: alpha3!!
|
||||
}
|
||||
|
||||
fun String.adaptAsTitle() = if (first().isDigit()) {
|
||||
fun String.adaptAsTitle() = (if (first().isDigit()) {
|
||||
"L$this"
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}).replace(".", "_").replace("'", "_")
|
||||
|
||||
fun String.normalized() = Normalizer.normalize(this, Normalizer.Form.NFD).replace(Regex("[^\\p{ASCII}]"), "")
|
||||
|
||||
@@ -73,7 +80,10 @@ data class Tag(
|
||||
val title: String,
|
||||
val tag: String,
|
||||
val subtags: List<Tag>
|
||||
)
|
||||
) {
|
||||
val adaptedTitle
|
||||
get() = title.adaptAsTitle()
|
||||
}
|
||||
|
||||
private fun printLanguageCodeAndTags(
|
||||
tag: Tag,
|
||||
@@ -81,17 +91,19 @@ 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}"${parent ?.let { parent -> "; override val parentLang: ${parent.title} get() = ${parent.title};" } ?: ""} }"""
|
||||
${indents}object ${tag.adaptedTitle} : ${parent ?.adaptedTitle ?: baseClassName} { override val code: String = "${tag.tag}"${parent ?.let { parent -> "; override val parentLang: ${parent.adaptedTitle} get() = ${parent.adaptedTitle}" } ?: ""}; override fun toString() = code }"""
|
||||
} else {
|
||||
"""
|
||||
${indents}${baseClassSerializerAnnotationName}
|
||||
${indents}sealed class ${tag.title} : ${parent ?.title ?: baseClassName}() {
|
||||
${indents} override val code: String = "${tag.tag}"${parent ?.let { parent -> "\n${indents} override val parentLang: ${parent.title} get() = ${parent.title};" } ?: ""}
|
||||
${indents}sealed interface ${tag.adaptedTitle} : ${parent ?.adaptedTitle ?: baseClassName} {
|
||||
|
||||
${tag.subtags.joinToString("\n") { printLanguageCodeAndTags(it, tag, "${indents} ") }}
|
||||
|
||||
${indents} ${baseClassSerializerAnnotationName}
|
||||
${indents} companion object : ${tag.title}()
|
||||
${indents} companion object : ${tag.adaptedTitle} {
|
||||
${indents} override val code: String = "${tag.tag}"${parent ?.let { parent -> "\n${indents} override val parentLang: ${parent.adaptedTitle} get() = ${parent.adaptedTitle};" } ?: ""}
|
||||
${indents} override fun toString() = code
|
||||
${indents} }
|
||||
${indents}}
|
||||
"""
|
||||
}
|
||||
@@ -105,23 +117,22 @@ import kotlinx.serialization.Serializable
|
||||
* https://datahub.io/core/language-codes/ files (base and tags) and create the whole hierarchy using it.
|
||||
*/
|
||||
${baseClassSerializerAnnotationName}
|
||||
sealed class $baseClassName {
|
||||
abstract val code: String
|
||||
open val parentLang: $baseClassName?
|
||||
get() = null
|
||||
open val withoutDialect: String
|
||||
sealed interface $baseClassName {
|
||||
val code: String
|
||||
val parentLang: $baseClassName?
|
||||
get() = code.split("-").takeIf { it.size > 1 } ?.first() ?.let(::$unknownBaseClassName)
|
||||
val withoutDialect: String
|
||||
get() = parentLang ?.code ?: code
|
||||
|
||||
${tags.joinToString("\n") { printLanguageCodeAndTags(it, indents = " ") } }
|
||||
|
||||
$baseClassSerializerAnnotationName
|
||||
data class $unknownBaseClassName (override val code: String) : $baseClassName() {
|
||||
data class $unknownBaseClassName (override val code: String) : $baseClassName {
|
||||
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
|
||||
val $oldUnknownBaseClassName
|
||||
get() = $unknownBaseClassName
|
||||
}
|
||||
@Deprecated("Renamed", ReplaceWith("$baseClassName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName"))
|
||||
typealias $oldBaseClassName = $baseClassName
|
||||
@@ -133,7 +144,7 @@ fun createStringConverterCode(tags: List<Tag>, prePackage: String): String {
|
||||
pretitle: String = baseClassName,
|
||||
indents: String = " "
|
||||
): String {
|
||||
val currentTitle = "$pretitle.${tag.title}"
|
||||
val currentTitle = "$pretitle.${tag.adaptedTitle}"
|
||||
return """${indents}$currentTitle.code -> $currentTitle${if (tag.subtags.isNotEmpty()) tag.subtags.joinToString("\n", "\n") { createDeserializeVariantForTag(it, currentTitle, indents) } else ""}"""
|
||||
}
|
||||
fun createInheritorVariantForTag(
|
||||
@@ -141,7 +152,7 @@ fun createStringConverterCode(tags: List<Tag>, prePackage: String): String {
|
||||
pretitle: String = baseClassName,
|
||||
indents: String = " "
|
||||
): String {
|
||||
val currentTitle = "$pretitle.${tag.title}"
|
||||
val currentTitle = "$pretitle.${tag.adaptedTitle}"
|
||||
val subtags = if (tag.subtags.isNotEmpty()) {
|
||||
tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForTag(it, currentTitle, "$indents ") }
|
||||
} else {
|
||||
@@ -155,7 +166,7 @@ fun createStringConverterCode(tags: List<Tag>, prePackage: String): String {
|
||||
indents: String = " ",
|
||||
codeSuffix: String = ""
|
||||
): String {
|
||||
val currentTitle = "$pretitle.${tag.title}"
|
||||
val currentTitle = "$pretitle.${tag.adaptedTitle}"
|
||||
val subtags = if (tag.subtags.isNotEmpty()) {
|
||||
tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForMapForTag(it, currentTitle, "$indents ", codeSuffix) }
|
||||
} else {
|
||||
@@ -266,7 +277,7 @@ suspend fun main(vararg args: String) {
|
||||
File(outputFolder, "LanguageCodes.kt").apply {
|
||||
delete()
|
||||
createNewFile()
|
||||
writeText(targetPackagePrefix + buildKtFileContent(tags, targetPackage ?: ""))
|
||||
writeText("@file:Suppress(\"SERIALIZER_TYPE_INCOMPATIBLE\")\n\n" + targetPackagePrefix + buildKtFileContent(tags, targetPackage ?: ""))
|
||||
}
|
||||
|
||||
File(outputFolder, "StringToLanguageCodes.kt").apply {
|
||||
|
1
language_codes/generator/tags.json
Normal file
1
language_codes/generator/tags.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,4 +4,4 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
@@ -4,4 +4,4 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -3,7 +3,7 @@ plugins {
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
}
|
||||
|
||||
apply from: "$mppJavaProjectPresetPath"
|
||||
apply from: "$mppJavaProject"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -3,7 +3,7 @@ plugins {
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
}
|
||||
|
||||
apply from: "$mppJavaProjectPresetPath"
|
||||
apply from: "$mppJavaProject"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
2
repos/cache/build.gradle
vendored
2
repos/cache/build.gradle
vendored
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -98,7 +98,7 @@ open class FullCRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
||||
kvCache: KeyValueRepo<IdType, ObjectType>,
|
||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||
skipStartInvalidate: Boolean = false,
|
||||
locker: SmartRWLocker = SmartRWLocker(),
|
||||
locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
||||
idGetter: (ObjectType) -> IdType
|
||||
) : FullReadCRUDCacheRepo<ObjectType, IdType>(
|
||||
parentRepo,
|
||||
@@ -116,10 +116,23 @@ open class FullCRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
||||
CRUDRepo<ObjectType, IdType, InputValueType> {
|
||||
init {
|
||||
if (!skipStartInvalidate) {
|
||||
scope.launchSafelyWithoutExceptions { invalidate() }
|
||||
scope.launchSafelyWithoutExceptions {
|
||||
if (locker.writeMutex.isLocked) {
|
||||
initialInvalidate()
|
||||
} else {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open suspend fun initialInvalidate() {
|
||||
try {
|
||||
kvCache.actualizeAll(parentRepo, locker = null)
|
||||
} finally {
|
||||
locker.unlockWrite()
|
||||
}
|
||||
}
|
||||
override suspend fun invalidate() {
|
||||
actualizeAll()
|
||||
}
|
||||
|
@@ -126,26 +126,40 @@ fun <Key, Value> WriteKeyValueRepo<Key, Value>.caching(
|
||||
) = FullWriteKeyValueCacheRepo(this, kvCache, scope)
|
||||
|
||||
open class FullKeyValueCacheRepo<Key,Value>(
|
||||
protected open val parentRepo: KeyValueRepo<Key, Value>,
|
||||
override val parentRepo: KeyValueRepo<Key, Value>,
|
||||
kvCache: KeyValueRepo<Key, Value>,
|
||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||
skipStartInvalidate: Boolean = false,
|
||||
locker: SmartRWLocker = SmartRWLocker()
|
||||
) : FullWriteKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, scope),
|
||||
locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
||||
) : //FullWriteKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, scope),
|
||||
KeyValueRepo<Key,Value>,
|
||||
ReadKeyValueRepo<Key, Value> by FullReadKeyValueCacheRepo(
|
||||
WriteKeyValueRepo<Key,Value> by parentRepo,
|
||||
FullReadKeyValueCacheRepo<Key, Value>(
|
||||
parentRepo,
|
||||
kvCache,
|
||||
locker
|
||||
) {
|
||||
init {
|
||||
if (!skipStartInvalidate) {
|
||||
scope.launchSafelyWithoutExceptions { invalidate() }
|
||||
scope.launchSafelyWithoutExceptions {
|
||||
if (locker.writeMutex.isLocked) {
|
||||
initialInvalidate()
|
||||
} else {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun unsetWithValues(toUnset: List<Value>) = parentRepo.unsetWithValues(toUnset)
|
||||
|
||||
protected open suspend fun initialInvalidate() {
|
||||
try {
|
||||
kvCache.actualizeAll(parentRepo, locker = null)
|
||||
} finally {
|
||||
locker.unlockWrite()
|
||||
}
|
||||
}
|
||||
override suspend fun invalidate() {
|
||||
kvCache.actualizeAll(parentRepo, locker)
|
||||
}
|
||||
@@ -154,6 +168,28 @@ open class FullKeyValueCacheRepo<Key,Value>(
|
||||
parentRepo.clear()
|
||||
kvCache.clear()
|
||||
}
|
||||
|
||||
override suspend fun set(toSet: Map<Key, Value>) {
|
||||
locker.withWriteLock {
|
||||
parentRepo.set(toSet)
|
||||
kvCache.set(
|
||||
toSet.filter {
|
||||
parentRepo.contains(it.key)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun unset(toUnset: List<Key>) {
|
||||
locker.withWriteLock {
|
||||
parentRepo.unset(toUnset)
|
||||
kvCache.unset(
|
||||
toUnset.filter {
|
||||
!parentRepo.contains(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <Key, Value> KeyValueRepo<Key, Value>.fullyCached(
|
||||
|
@@ -10,6 +10,7 @@ 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 dev.inmo.micro_utils.repos.pagination.maxPagePagination
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.*
|
||||
@@ -65,6 +66,42 @@ open class FullReadKeyValuesCacheRepo<Key,Value>(
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getAll(k: Key, reversed: Boolean): List<Value> {
|
||||
return doOrTakeAndActualizeWithWriteLock(
|
||||
{
|
||||
get(k) ?.optionallyReverse(reversed).optionalOrAbsentIfNull
|
||||
},
|
||||
{ getAll(k, reversed) },
|
||||
{ kvCache.set(k, it.optionallyReverse(reversed)) }
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getAll(reverseLists: Boolean): Map<Key, List<Value>> {
|
||||
return doOrTakeAndActualizeWithWriteLock(
|
||||
{
|
||||
getAll().takeIf { it.isNotEmpty() } ?.let {
|
||||
if (reverseLists) {
|
||||
it.mapValues { it.value.reversed() }
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}.optionalOrAbsentIfNull
|
||||
},
|
||||
{ getAll(reverseLists) },
|
||||
{
|
||||
kvCache.set(
|
||||
it.let {
|
||||
if (reverseLists) {
|
||||
it.mapValues { it.value.reversed() }
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
|
||||
return doOrTakeAndActualize(
|
||||
{
|
||||
@@ -163,17 +200,23 @@ fun <Key, Value> WriteKeyValuesRepo<Key, Value>.caching(
|
||||
) = FullWriteKeyValuesCacheRepo(this, kvCache, scope, locker)
|
||||
|
||||
open class FullKeyValuesCacheRepo<Key,Value>(
|
||||
protected open val parentRepo: KeyValuesRepo<Key, Value>,
|
||||
override val parentRepo: KeyValuesRepo<Key, Value>,
|
||||
kvCache: KeyValueRepo<Key, List<Value>>,
|
||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||
skipStartInvalidate: Boolean = false,
|
||||
locker: SmartRWLocker = SmartRWLocker(),
|
||||
) : FullWriteKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, scope, locker),
|
||||
KeyValuesRepo<Key, Value>,
|
||||
ReadKeyValuesRepo<Key, Value> by FullReadKeyValuesCacheRepo(parentRepo, kvCache, locker) {
|
||||
locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
||||
) : KeyValuesRepo<Key, Value>,
|
||||
FullReadKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, locker),
|
||||
WriteKeyValuesRepo<Key, Value> by parentRepo {
|
||||
init {
|
||||
if (!skipStartInvalidate) {
|
||||
scope.launchSafelyWithoutExceptions { invalidate() }
|
||||
scope.launchSafelyWithoutExceptions {
|
||||
if (locker.writeMutex.isLocked) {
|
||||
initialInvalidate()
|
||||
} else {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,16 +228,75 @@ open class FullKeyValuesCacheRepo<Key,Value>(
|
||||
}
|
||||
}
|
||||
|
||||
protected open suspend fun initialInvalidate() {
|
||||
try {
|
||||
kvCache.actualizeAll(parentRepo, locker = null)
|
||||
} finally {
|
||||
locker.unlockWrite()
|
||||
}
|
||||
}
|
||||
override suspend fun invalidate() {
|
||||
kvCache.actualizeAll(parentRepo, locker = locker)
|
||||
}
|
||||
|
||||
override suspend fun set(toSet: Map<Key, List<Value>>) {
|
||||
super<KeyValuesRepo>.set(toSet)
|
||||
locker.withWriteLock {
|
||||
parentRepo.set(toSet)
|
||||
kvCache.set(
|
||||
toSet.filter {
|
||||
parentRepo.contains(it.key)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun removeWithValue(v: Value) {
|
||||
super<FullWriteKeyValuesCacheRepo>.removeWithValue(v)
|
||||
override suspend fun add(toAdd: Map<Key, List<Value>>) {
|
||||
locker.withWriteLock {
|
||||
parentRepo.add(toAdd)
|
||||
toAdd.forEach {
|
||||
val filtered = it.value.filter { v ->
|
||||
parentRepo.contains(it.key, v)
|
||||
}.ifEmpty {
|
||||
return@forEach
|
||||
}
|
||||
kvCache.set(
|
||||
it.key,
|
||||
(kvCache.get(it.key) ?: emptyList()) + filtered
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
||||
locker.withWriteLock {
|
||||
parentRepo.remove(toRemove)
|
||||
toRemove.forEach {
|
||||
val filtered = it.value.filter { v ->
|
||||
!parentRepo.contains(it.key, v)
|
||||
}.ifEmpty {
|
||||
return@forEach
|
||||
}.toSet()
|
||||
val resultList = (kvCache.get(it.key) ?: emptyList()) - filtered
|
||||
if (resultList.isEmpty()) {
|
||||
kvCache.unset(it.key)
|
||||
} else {
|
||||
kvCache.set(
|
||||
it.key,
|
||||
resultList
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun clear(k: Key) {
|
||||
locker.withWriteLock {
|
||||
parentRepo.clear(k)
|
||||
if (parentRepo.contains(k)) {
|
||||
return@withWriteLock
|
||||
}
|
||||
kvCache.unset(k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
142
repos/cache/src/commonTest/kotlin/full/FullCRUDCacheRepoTests.kt
vendored
Normal file
142
repos/cache/src/commonTest/kotlin/full/FullCRUDCacheRepoTests.kt
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
package full
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.repos.MapCRUDRepo
|
||||
import dev.inmo.micro_utils.repos.MapKeyValueRepo
|
||||
import dev.inmo.micro_utils.repos.cache.full.FullCRUDCacheRepo
|
||||
import dev.inmo.micro_utils.repos.create
|
||||
import dev.inmo.micro_utils.repos.deleteById
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class FullCRUDCacheRepoTests {
|
||||
data class New(
|
||||
val data: String
|
||||
)
|
||||
data class Registered(
|
||||
val id: String,
|
||||
val data: String
|
||||
)
|
||||
@Test
|
||||
fun creatingWorksProperly() = runTest {
|
||||
val testData = (0 until 1000).map {
|
||||
("$it-" + uuid4().toString())
|
||||
}
|
||||
val updatedTestData = (0 until 1000).map {
|
||||
("$it-" + uuid4().toString())
|
||||
}
|
||||
val kvCache = MapKeyValueRepo<String, Registered>()
|
||||
val crudRepo = MapCRUDRepo<Registered, String, New>(
|
||||
{ new, id, old ->
|
||||
Registered(id, new.data)
|
||||
}
|
||||
) {
|
||||
val id = uuid4().toString()
|
||||
id to Registered(id, it.data)
|
||||
}
|
||||
|
||||
val cacheRepo = FullCRUDCacheRepo(
|
||||
crudRepo,
|
||||
kvCache,
|
||||
idGetter = { it.id }
|
||||
)
|
||||
|
||||
val registereds = testData.map {
|
||||
val created = cacheRepo.create(New(it)).first()
|
||||
assertEquals(it, created.data)
|
||||
assertEquals(kvCache.get(created.id), created)
|
||||
assertEquals(crudRepo.getById(created.id), created)
|
||||
created
|
||||
}
|
||||
|
||||
cacheRepo.getAll().forEach { (id, value) ->
|
||||
assertTrue {
|
||||
registereds.first {
|
||||
it.id == id
|
||||
} == value
|
||||
}
|
||||
}
|
||||
val updatedRegistereds = registereds.mapIndexed { i, it ->
|
||||
val updated = cacheRepo.update(it.id, New(updatedTestData[i])) ?: error("Unable to update data for $it")
|
||||
assertEquals(updatedTestData[i], updated.data)
|
||||
assertEquals(kvCache.get(updated.id), updated)
|
||||
assertEquals(crudRepo.getById(updated.id), updated)
|
||||
updated
|
||||
}
|
||||
cacheRepo.getAll().forEach { (id, value) ->
|
||||
assertTrue {
|
||||
updatedRegistereds.first {
|
||||
it.id == id
|
||||
} == value
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun precachingWorksProperly() = runTest {
|
||||
val testData = (0 until 1000).map {
|
||||
(it.toString() + uuid4().toString())
|
||||
}
|
||||
val kvCache = MapKeyValueRepo<String, Registered>()
|
||||
val crudRepo = MapCRUDRepo<Registered, String, New>(
|
||||
{ new, id, old ->
|
||||
Registered(id, new.data)
|
||||
}
|
||||
) {
|
||||
val id = uuid4().toString()
|
||||
id to Registered(id, it.data)
|
||||
}
|
||||
val registereds = crudRepo.create(testData.map { New(it) })
|
||||
|
||||
val cacheRepo = FullCRUDCacheRepo(
|
||||
crudRepo,
|
||||
kvCache,
|
||||
idGetter = { it.id }
|
||||
)
|
||||
|
||||
cacheRepo.getAll().forEach { (id, value) ->
|
||||
assertTrue {
|
||||
registereds.first {
|
||||
it.id == id
|
||||
} == value
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun removingWorksProperly() = runTest {
|
||||
val testData = (0 until 1000).map {
|
||||
(it.toString() + uuid4().toString())
|
||||
}
|
||||
val kvCache = MapKeyValueRepo<String, Registered>()
|
||||
val crudRepo = MapCRUDRepo<Registered, String, New>(
|
||||
{ new, id, old ->
|
||||
Registered(id, new.data)
|
||||
}
|
||||
) {
|
||||
val id = uuid4().toString()
|
||||
id to Registered(id, it.data)
|
||||
}
|
||||
val registereds = crudRepo.create(testData.map { New(it) })
|
||||
|
||||
val cacheRepo = FullCRUDCacheRepo(
|
||||
crudRepo,
|
||||
kvCache,
|
||||
idGetter = { it.id }
|
||||
)
|
||||
|
||||
registereds.forEach {
|
||||
val id = it.id
|
||||
assertTrue {
|
||||
cacheRepo.getAll()[id] == it
|
||||
}
|
||||
|
||||
cacheRepo.deleteById(id)
|
||||
assertFalse {
|
||||
cacheRepo.contains(id)
|
||||
}
|
||||
}
|
||||
assertEquals(0, cacheRepo.count())
|
||||
}
|
||||
}
|
92
repos/cache/src/commonTest/kotlin/full/FullKeyValueCacheRepoTests.kt
vendored
Normal file
92
repos/cache/src/commonTest/kotlin/full/FullKeyValueCacheRepoTests.kt
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
package full
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
import dev.inmo.micro_utils.repos.cache.full.FullKeyValueCacheRepo
|
||||
import korlibs.time.seconds
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class FullKeyValueCacheRepoTests {
|
||||
@Test
|
||||
fun creatingWorksProperly() = runTest(timeout = 120.seconds) {
|
||||
val testData = (0 until 1000).associate {
|
||||
("$it-" + uuid4().toString()) to "$it-" + uuid4().toString()
|
||||
}
|
||||
val updatedTestData = testData.keys.associateWith {
|
||||
"$it-" + uuid4().toString()
|
||||
}
|
||||
val kvCache = MapKeyValueRepo<String, String>()
|
||||
val kvRepo = MapKeyValueRepo<String, String>()
|
||||
|
||||
val cacheRepo = FullKeyValueCacheRepo(
|
||||
kvRepo,
|
||||
kvCache
|
||||
)
|
||||
|
||||
testData.forEach {
|
||||
cacheRepo.set(it.key, it.value)
|
||||
assertEquals(it.value, cacheRepo.get(it.key))
|
||||
assertEquals(it.value, kvRepo.get(it.key))
|
||||
assertEquals(it.value, kvCache.get(it.key))
|
||||
}
|
||||
|
||||
updatedTestData.forEach {
|
||||
cacheRepo.set(it.key, it.value)
|
||||
assertEquals(cacheRepo.get(it.key), it.value)
|
||||
assertEquals(kvRepo.get(it.key), it.value)
|
||||
assertEquals(kvCache.get(it.key), it.value)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun precachingWorksProperly() = runTest {
|
||||
val testData = (0 until 1000).associate {
|
||||
(it.toString() + uuid4().toString()) to uuid4().toString()
|
||||
}
|
||||
val kvCache = MapKeyValueRepo<String, String>()
|
||||
val kvRepo = MapKeyValueRepo<String, String>()
|
||||
kvRepo.set(testData)
|
||||
|
||||
val cacheRepo = FullKeyValueCacheRepo(
|
||||
kvRepo,
|
||||
kvCache
|
||||
)
|
||||
|
||||
|
||||
cacheRepo.getAll().forEach { (id, value) ->
|
||||
assertTrue {
|
||||
testData[id] == value
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun unsettingWorksProperly() = runTest {
|
||||
val testData = (0 until 1000).associate {
|
||||
(it.toString() + uuid4().toString()) to uuid4().toString()
|
||||
}
|
||||
val kvCache = MapKeyValueRepo<String, String>()
|
||||
val kvRepo = MapKeyValueRepo<String, String>()
|
||||
kvRepo.set(testData)
|
||||
|
||||
val cacheRepo = FullKeyValueCacheRepo(
|
||||
kvRepo,
|
||||
kvCache
|
||||
)
|
||||
|
||||
testData.forEach {
|
||||
val id = it.key
|
||||
assertTrue {
|
||||
cacheRepo.getAll()[id] == it.value
|
||||
}
|
||||
|
||||
cacheRepo.unset(id)
|
||||
assertFalse {
|
||||
cacheRepo.contains(id)
|
||||
}
|
||||
}
|
||||
assertEquals(0, cacheRepo.count())
|
||||
}
|
||||
}
|
58
repos/cache/src/commonTest/kotlin/full/FullKeyValuesCacheRepoTests.kt
vendored
Normal file
58
repos/cache/src/commonTest/kotlin/full/FullKeyValuesCacheRepoTests.kt
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package full
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
import dev.inmo.micro_utils.repos.cache.full.FullKeyValuesCacheRepo
|
||||
import dev.inmo.micro_utils.repos.pagination.maxPagePagination
|
||||
import korlibs.time.days
|
||||
import korlibs.time.seconds
|
||||
import korlibs.time.years
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.*
|
||||
|
||||
class FullKeyValuesCacheRepoTests {
|
||||
@Test
|
||||
fun creatingWorksProperly() = runTest(timeout = 120.seconds) {
|
||||
val testData = (0 until 1000).associate {
|
||||
("$it-" + uuid4().toString()) to (0 until 1000).map {
|
||||
"$it-" + uuid4().toString()
|
||||
}.sorted()
|
||||
}
|
||||
val updatedTestData = testData.keys.associateWith {
|
||||
(0 until 1000).map {
|
||||
"$it-" + uuid4().toString()
|
||||
}.sorted()
|
||||
}
|
||||
val addedData = testData.keys.associateWith {
|
||||
"$it-" + uuid4().toString()
|
||||
}
|
||||
val kvCache = MapKeyValueRepo<String, List<String>>()
|
||||
val kvRepo = MapKeyValuesRepo<String, String>()
|
||||
|
||||
val cacheRepo = FullKeyValuesCacheRepo(
|
||||
kvRepo,
|
||||
kvCache
|
||||
)
|
||||
|
||||
testData.forEach {
|
||||
cacheRepo.set(it.key, it.value)
|
||||
assertContentEquals(it.value, cacheRepo.getAll(it.key))
|
||||
assertContentEquals(it.value, kvRepo.getAll(it.key))
|
||||
assertContentEquals(it.value, kvCache.get(it.key) ?.sorted())
|
||||
}
|
||||
|
||||
updatedTestData.forEach {
|
||||
cacheRepo.set(it.key, it.value)
|
||||
assertContentEquals(it.value, cacheRepo.getAll(it.key))
|
||||
assertContentEquals(it.value, kvRepo.getAll(it.key))
|
||||
assertContentEquals(it.value, kvCache.get(it.key) ?.sorted())
|
||||
}
|
||||
|
||||
addedData.forEach {
|
||||
cacheRepo.add(it.key, it.value)
|
||||
assertTrue(cacheRepo.contains(it.key, it.value))
|
||||
assertTrue(kvRepo.contains(it.key, it.value))
|
||||
assertTrue(kvCache.get(it.key) !!.contains(it.value))
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@ plugins {
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
|
@@ -1,8 +1,12 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
project.version = "$version"
|
||||
project.group = "$group"
|
||||
|
||||
apply from: "$publishGradlePath"
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
compilations.main {
|
||||
@@ -15,6 +19,13 @@ kotlin {
|
||||
browser()
|
||||
nodejs()
|
||||
}
|
||||
androidTarget {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
}
|
||||
linuxX64()
|
||||
mingwX64()
|
||||
linuxArm64()
|
||||
@@ -24,28 +35,23 @@ kotlin {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib')
|
||||
api libs.kt.serialization
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-common')
|
||||
implementation kotlin('test-annotations-common')
|
||||
implementation libs.kt.coroutines.test
|
||||
api kotlin('test')
|
||||
api kotlin('test-annotations-common')
|
||||
api libs.kt.coroutines.test
|
||||
api project(":micro_utils.repos.common")
|
||||
}
|
||||
}
|
||||
|
||||
jvmTest {
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
jsTest {
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
|
||||
nativeMain.dependsOn commonMain
|
||||
linuxX64Main.dependsOn nativeMain
|
||||
mingwX64Main.dependsOn nativeMain
|
||||
@@ -55,7 +61,9 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
package dev.inmo.micro_utils.repos.common.tests
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.repos.CRUDRepo
|
||||
import dev.inmo.micro_utils.repos.create
|
||||
import dev.inmo.micro_utils.repos.deleteById
|
||||
import korlibs.time.seconds
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.test.*
|
||||
|
||||
abstract class CommonCRUDRepoTests : CommonRepoTests<CRUDRepo<CommonCRUDRepoTests.Registered, String, CommonCRUDRepoTests.New>>() {
|
||||
@Serializable
|
||||
data class New(
|
||||
val data: String
|
||||
)
|
||||
@Serializable
|
||||
data class Registered(
|
||||
val id: String,
|
||||
val data: String
|
||||
)
|
||||
|
||||
@Test
|
||||
fun creatingWorksProperly() = runTest(timeout = 120.seconds) {
|
||||
val crudRepo = repoCreator()
|
||||
val testData = (0 until testSequencesSize).map {
|
||||
("$it-" + uuid4().toString())
|
||||
}
|
||||
val updatedTestData = (0 until 1000).map {
|
||||
("$it-" + uuid4().toString())
|
||||
}
|
||||
|
||||
val registereds = testData.map {
|
||||
val created = crudRepo.create(New(it)).first()
|
||||
assertEquals(it, created.data)
|
||||
assertEquals(crudRepo.getById(created.id), created)
|
||||
created
|
||||
}
|
||||
|
||||
crudRepo.getAll().forEach { (id, value) ->
|
||||
assertTrue {
|
||||
registereds.first {
|
||||
it.id == id
|
||||
} == value
|
||||
}
|
||||
}
|
||||
val updatedRegistereds = registereds.mapIndexed { i, it ->
|
||||
val updated = crudRepo.update(it.id, New(updatedTestData[i])) ?: error("Unable to update data for $it")
|
||||
assertEquals(updatedTestData[i], updated.data)
|
||||
assertEquals(crudRepo.getById(updated.id), updated)
|
||||
updated
|
||||
}
|
||||
crudRepo.getAll().forEach { (id, value) ->
|
||||
assertTrue {
|
||||
updatedRegistereds.first {
|
||||
it.id == id
|
||||
} == value
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun removingWorksProperly() = runTest {
|
||||
val crudRepo = repoCreator()
|
||||
val testData = (0 until testSequencesSize).map {
|
||||
(it.toString() + uuid4().toString())
|
||||
}
|
||||
val registereds = crudRepo.create(testData.map { New(it) })
|
||||
|
||||
registereds.forEach {
|
||||
val id = it.id
|
||||
assertTrue {
|
||||
crudRepo.getAll()[id] == it
|
||||
}
|
||||
|
||||
crudRepo.deleteById(id)
|
||||
assertFalse {
|
||||
crudRepo.contains(id)
|
||||
}
|
||||
}
|
||||
assertEquals(0, crudRepo.count())
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user