mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2026-09-13 11:44:20 +00:00
Merge branch 'refs/heads/0.22.0' into bpavuk.tests
# Conflicts: # CHANGELOG.md # gradle.properties
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
.idea
|
||||
.kotlin
|
||||
out/*
|
||||
*.iml
|
||||
target
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
## 0.22.0
|
||||
|
||||
## 0.21.6
|
||||
|
||||
* `KSP`:
|
||||
|
||||
@@ -3,6 +3,7 @@ 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"
|
||||
|
||||
@@ -3,6 +3,7 @@ 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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -3,6 +3,7 @@ plugins {
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
alias(libs.plugins.jb.compose)
|
||||
alias(libs.plugins.kt.jb.compose)
|
||||
}
|
||||
|
||||
apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
|
||||
@@ -3,6 +3,7 @@ plugins {
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
alias(libs.plugins.jb.compose)
|
||||
alias(libs.plugins.kt.jb.compose)
|
||||
}
|
||||
|
||||
apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -15,5 +15,5 @@ crypto_js_version=4.1.1
|
||||
# Project data
|
||||
|
||||
group=dev.inmo
|
||||
version=0.21.6
|
||||
version=0.22.0
|
||||
android_code_version=265
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[versions]
|
||||
|
||||
kt = "1.9.23"
|
||||
kt-serialization = "1.6.3"
|
||||
kt = "2.0.0"
|
||||
kt-serialization = "1.7.1"
|
||||
kt-coroutines = "1.8.1"
|
||||
|
||||
kslog = "1.3.4"
|
||||
|
||||
jb-compose = "1.6.2"
|
||||
jb-exposed = "0.50.1"
|
||||
jb-compose = "1.7.0-alpha02"
|
||||
jb-exposed = "0.53.0"
|
||||
jb-dokka = "1.9.20"
|
||||
|
||||
korlibs = "5.4.0"
|
||||
@@ -21,20 +21,20 @@ koin = "3.5.6"
|
||||
|
||||
okio = "3.9.0"
|
||||
|
||||
ksp = "1.9.23-1.0.20"
|
||||
kotlin-poet = "1.16.0"
|
||||
ksp = "2.0.0-1.0.23"
|
||||
kotlin-poet = "1.18.0"
|
||||
|
||||
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-appCompat = "1.7.0"
|
||||
android-fragment = "1.8.2"
|
||||
android-espresso = "3.6.1"
|
||||
android-test = "1.2.1"
|
||||
android-compose-material3 = "1.2.1"
|
||||
|
||||
android-props-minSdk = "21"
|
||||
@@ -113,5 +113,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" }
|
||||
|
||||
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
|
||||
|
||||
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
@@ -12,8 +12,20 @@ kotlin {
|
||||
}
|
||||
}
|
||||
js (IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
browser {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
androidTarget {
|
||||
publishAllLibraryVariants()
|
||||
@@ -77,7 +89,6 @@ kotlin {
|
||||
jsTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
nativeMain.dependsOn commonMain
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,8 +12,20 @@ kotlin {
|
||||
}
|
||||
}
|
||||
js (IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
browser {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
androidTarget {
|
||||
publishAllLibraryVariants()
|
||||
@@ -48,7 +60,6 @@ kotlin {
|
||||
jsTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
androidUnitTest {
|
||||
@@ -58,16 +69,6 @@ kotlin {
|
||||
implementation libs.android.espresso
|
||||
}
|
||||
}
|
||||
mingwX64Test {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
linuxX64Test {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
|
||||
androidMain.dependsOn jvmMain
|
||||
}
|
||||
|
||||
@@ -12,8 +12,20 @@ kotlin {
|
||||
}
|
||||
}
|
||||
js (IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
browser {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha {
|
||||
timeout = "60000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
linuxX64()
|
||||
mingwX64()
|
||||
@@ -42,7 +54,6 @@ kotlin {
|
||||
jsTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,13 +80,6 @@ kotlin {
|
||||
implementation compose.uiTest
|
||||
}
|
||||
}
|
||||
androidInstrumentedTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
implementation libs.android.test.junit
|
||||
implementation libs.android.espresso
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
91
repos/cache/src/commonTest/kotlin/full/FullKeyValueCacheRepoTests.kt
vendored
Normal file
91
repos/cache/src/commonTest/kotlin/full/FullKeyValueCacheRepoTests.kt
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package full
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
import dev.inmo.micro_utils.repos.cache.full.FullKeyValueCacheRepo
|
||||
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 {
|
||||
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())
|
||||
}
|
||||
}
|
||||
57
repos/cache/src/commonTest/kotlin/full/FullKeyValuesCacheRepoTests.kt
vendored
Normal file
57
repos/cache/src/commonTest/kotlin/full/FullKeyValuesCacheRepoTests.kt
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
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.years
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.*
|
||||
|
||||
class FullKeyValuesCacheRepoTests {
|
||||
@Test
|
||||
fun creatingWorksProperly() = runTest {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,9 @@ class ReadMapKeyValueRepo<Key, Value>(
|
||||
) : ReadKeyValueRepo<Key, Value> {
|
||||
constructor(map: Map<Key, Value> = emptyMap()) : this(map, SmartRWLocker())
|
||||
|
||||
override suspend fun get(k: Key): Value? = locker.withReadAcquire { map[k] }
|
||||
override suspend fun get(k: Key): Value? = locker.withReadAcquire {
|
||||
map[k]
|
||||
}
|
||||
|
||||
override suspend fun values(
|
||||
pagination: Pagination,
|
||||
|
||||
@@ -4,6 +4,7 @@ import dev.inmo.micro_utils.coroutines.SmartRWLocker
|
||||
import dev.inmo.micro_utils.coroutines.withReadAcquire
|
||||
import dev.inmo.micro_utils.coroutines.withWriteLock
|
||||
import dev.inmo.micro_utils.pagination.*
|
||||
import dev.inmo.micro_utils.pagination.utils.optionallyReverse
|
||||
import dev.inmo.micro_utils.pagination.utils.paginate
|
||||
import dev.inmo.micro_utils.pagination.utils.reverse
|
||||
import kotlinx.coroutines.flow.*
|
||||
@@ -33,6 +34,20 @@ class MapReadKeyValuesRepo<Key, Value>(
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getAll(k: Key, reversed: Boolean): List<Value> {
|
||||
return locker.withReadAcquire { map[k] ?.optionallyReverse(reversed) ?: return emptyList() }
|
||||
}
|
||||
|
||||
override suspend fun getAll(reverseLists: Boolean): Map<Key, List<Value>> {
|
||||
return locker.withReadAcquire {
|
||||
if (reverseLists) {
|
||||
map.mapValues { it.value.reversed() }
|
||||
} else {
|
||||
map.toMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
|
||||
val keys = locker.withReadAcquire {
|
||||
map.keys
|
||||
@@ -108,6 +123,19 @@ class MapWriteKeyValuesRepo<Key, Value>(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun set(toSet: Map<Key, List<Value>>) {
|
||||
locker.withWriteLock {
|
||||
map.putAll(
|
||||
toSet.mapValues { it.value.toMutableList() }
|
||||
)
|
||||
}
|
||||
toSet.forEach { (k, v) ->
|
||||
v.forEach {
|
||||
_onNewValue.emit(k to it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
||||
val removed = mutableListOf<Pair<Key, Value>>()
|
||||
val cleared = mutableListOf<Key>()
|
||||
|
||||
@@ -13,6 +13,7 @@ kotlin {
|
||||
api libs.kslog
|
||||
api libs.kt.reflect
|
||||
api project(":micro_utils.coroutines")
|
||||
api libs.uuid
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
|
||||
@@ -1,5 +1,42 @@
|
||||
package dev.inmo.micro_utils.startup.plugin
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
expect object StartPluginSerializer : KSerializer<StartPlugin>
|
||||
object StartPluginSerializer : KSerializer<StartPlugin> {
|
||||
private val registeredPlugins = mutableMapOf<String, StartPlugin>()
|
||||
private val registeredPluginsByPlugin = mutableMapOf<StartPlugin, String>()
|
||||
override val descriptor: SerialDescriptor = String.serializer().descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): StartPlugin {
|
||||
alternativeDeserialize(decoder) ?.let { return it }
|
||||
val name = decoder.decodeString()
|
||||
return registeredPlugins[name] ?: error("Unable to find startup plugin for $name")
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: StartPlugin) {
|
||||
if (alternativeSerialize(encoder, value)) {
|
||||
return
|
||||
}
|
||||
val name = registeredPluginsByPlugin[value] ?: uuid4().toString().also {
|
||||
registeredPlugins[it] = value
|
||||
registeredPluginsByPlugin[value] = it
|
||||
}
|
||||
encoder.encodeString(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Register plugin inside of this [KSerializer]. Since plugin has been registered, you may use its [name] in any
|
||||
* serialized [dev.inmo.micro_utils.startup.launcher.Config] to retrieve [plugin] you passed here
|
||||
*/
|
||||
fun registerPlugin(name: String, plugin: StartPlugin) {
|
||||
registeredPlugins[name] = plugin
|
||||
}
|
||||
}
|
||||
|
||||
internal expect fun alternativeDeserialize(decoder: Decoder): StartPlugin?
|
||||
internal expect fun alternativeSerialize(encoder: Encoder, value: StartPlugin): Boolean
|
||||
|
||||
13
startup/plugin/src/jsMain/kotlin/StartPluginSerializer.js.kt
Normal file
13
startup/plugin/src/jsMain/kotlin/StartPluginSerializer.js.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package dev.inmo.micro_utils.startup.plugin
|
||||
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
internal actual fun alternativeDeserialize(decoder: Decoder): StartPlugin? {
|
||||
return null
|
||||
}
|
||||
|
||||
internal actual fun alternativeSerialize(
|
||||
encoder: Encoder,
|
||||
value: StartPlugin
|
||||
): Boolean = false
|
||||
@@ -1,35 +0,0 @@
|
||||
package dev.inmo.micro_utils.startup.plugin
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
actual object StartPluginSerializer : KSerializer<StartPlugin> {
|
||||
private val registeredPlugins = mutableMapOf<String, StartPlugin>()
|
||||
private val registeredPluginsByPlugin = mutableMapOf<StartPlugin, String>()
|
||||
override val descriptor: SerialDescriptor = String.serializer().descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): StartPlugin {
|
||||
val name = decoder.decodeString()
|
||||
return registeredPlugins[name] ?: error("Unable to find startup plugin for $name")
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: StartPlugin) {
|
||||
val name = registeredPluginsByPlugin[value] ?: uuid4().toString().also {
|
||||
registeredPlugins[it] = value
|
||||
registeredPluginsByPlugin[value] = it
|
||||
}
|
||||
encoder.encodeString(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Register plugin inside of this [KSerializer]. Since plugin has been registered, you may use its [name] in any
|
||||
* serialized [dev.inmo.micro_utils.startup.launcher.Config] to retrieve [plugin] you passed here
|
||||
*/
|
||||
fun registerPlugin(name: String, plugin: StartPlugin) {
|
||||
registeredPlugins[name] = plugin
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package dev.inmo.micro_utils.startup.plugin
|
||||
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
internal actual fun alternativeDeserialize(decoder: Decoder): StartPlugin? {
|
||||
val kclass = Class.forName(decoder.decodeString()).kotlin
|
||||
return (kclass.objectInstance ?: kclass.constructors.first { it.parameters.isEmpty() }.call()) as StartPlugin
|
||||
}
|
||||
|
||||
internal actual fun alternativeSerialize(
|
||||
encoder: Encoder,
|
||||
value: StartPlugin
|
||||
): Boolean {
|
||||
encoder.encodeString(value::class.java.canonicalName ?: return false)
|
||||
return true
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package dev.inmo.micro_utils.startup.plugin
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
actual object StartPluginSerializer : KSerializer<StartPlugin> {
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = String.serializer().descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): StartPlugin {
|
||||
val kclass = Class.forName(decoder.decodeString()).kotlin
|
||||
return (kclass.objectInstance ?: kclass.constructors.first { it.parameters.isEmpty() }.call()) as StartPlugin
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: StartPlugin) {
|
||||
encoder.encodeString(
|
||||
value::class.java.canonicalName
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package dev.inmo.micro_utils.startup.plugin
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
actual object StartPluginSerializer : KSerializer<StartPlugin> {
|
||||
private val registeredPlugins = mutableMapOf<String, StartPlugin>()
|
||||
private val registeredPluginsByPlugin = mutableMapOf<StartPlugin, String>()
|
||||
override val descriptor: SerialDescriptor = String.serializer().descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): StartPlugin {
|
||||
val name = decoder.decodeString()
|
||||
return registeredPlugins[name] ?: error("Unable to find startup plugin for $name")
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: StartPlugin) {
|
||||
val name = registeredPluginsByPlugin[value] ?: uuid4().toString().also {
|
||||
registeredPlugins[it] = value
|
||||
registeredPluginsByPlugin[value] = it
|
||||
}
|
||||
encoder.encodeString(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Register plugin inside of this [KSerializer]. Since plugin has been registered, you may use its [name] in any
|
||||
* serialized [dev.inmo.micro_utils.startup.launcher.Config] to retrieve [plugin] you passed here
|
||||
*/
|
||||
fun registerPlugin(name: String, plugin: StartPlugin) {
|
||||
registeredPlugins[name] = plugin
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package dev.inmo.micro_utils.startup.plugin
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
actual object StartPluginSerializer : KSerializer<StartPlugin> {
|
||||
private val registeredPlugins = mutableMapOf<String, StartPlugin>()
|
||||
private val registeredPluginsByPlugin = mutableMapOf<StartPlugin, String>()
|
||||
override val descriptor: SerialDescriptor = String.serializer().descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): StartPlugin {
|
||||
val name = decoder.decodeString()
|
||||
return registeredPlugins[name] ?: error("Unable to find startup plugin for $name")
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: StartPlugin) {
|
||||
val name = registeredPluginsByPlugin[value] ?: uuid4().toString().also {
|
||||
registeredPlugins[it] = value
|
||||
registeredPluginsByPlugin[value] = it
|
||||
}
|
||||
encoder.encodeString(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Register plugin inside of this [KSerializer]. Since plugin has been registered, you may use its [name] in any
|
||||
* serialized [dev.inmo.micro_utils.startup.launcher.Config] to retrieve [plugin] you passed here
|
||||
*/
|
||||
fun registerPlugin(name: String, plugin: StartPlugin) {
|
||||
registeredPlugins[name] = plugin
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package dev.inmo.micro_utils.startup.plugin
|
||||
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
internal actual fun alternativeDeserialize(decoder: Decoder): StartPlugin? {
|
||||
return null
|
||||
}
|
||||
|
||||
internal actual fun alternativeSerialize(
|
||||
encoder: Encoder,
|
||||
value: StartPlugin
|
||||
): Boolean = false
|
||||
Reference in New Issue
Block a user