mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-11-25 19:48:45 +00:00
commit
1ee9c88ffd
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
.idea
|
||||
.kotlin
|
||||
out/*
|
||||
*.iml
|
||||
target
|
||||
@ -17,5 +18,6 @@ publishing.sh
|
||||
|
||||
local.*
|
||||
local/
|
||||
**/*.local.*
|
||||
|
||||
.kotlin/
|
||||
|
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,5 +1,18 @@
|
||||
# Changelog
|
||||
|
||||
## 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`:
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
android_code_version=265
|
||||
version=0.22.0
|
||||
android_code_version=266
|
||||
|
@ -1,15 +1,17 @@
|
||||
[versions]
|
||||
|
||||
kt = "1.9.23"
|
||||
kt-serialization = "1.6.3"
|
||||
kt = "2.0.10"
|
||||
kt-serialization = "1.7.1"
|
||||
kt-coroutines = "1.8.1"
|
||||
|
||||
kslog = "1.3.4"
|
||||
kslog = "1.3.5"
|
||||
|
||||
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"
|
||||
|
||||
sqlite = "3.46.0.1"
|
||||
|
||||
korlibs = "5.4.0"
|
||||
uuid = "0.8.4"
|
||||
|
||||
@ -21,25 +23,25 @@ koin = "3.5.6"
|
||||
|
||||
okio = "3.9.0"
|
||||
|
||||
ksp = "1.9.23-1.0.20"
|
||||
kotlin-poet = "1.16.0"
|
||||
ksp = "2.0.10-1.0.24"
|
||||
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-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"
|
||||
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,6 +82,8 @@ 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" }
|
||||
|
||||
|
||||
android-coreKtx = { module = "androidx.core:core-ktx", version.ref = "android-coreKtx" }
|
||||
@ -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" }
|
||||
|
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())
|
||||
}
|
||||
}
|
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))
|
||||
}
|
||||
}
|
||||
}
|
69
repos/common/tests/build.gradle
Normal file
69
repos/common/tests/build.gradle
Normal file
@ -0,0 +1,69 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
}
|
||||
|
||||
project.version = "$version"
|
||||
project.group = "$group"
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
compilations.main {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
}
|
||||
js (IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
}
|
||||
androidTarget {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
}
|
||||
linuxX64()
|
||||
mingwX64()
|
||||
linuxArm64()
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib')
|
||||
api libs.kt.serialization
|
||||
api kotlin('test')
|
||||
api kotlin('test-annotations-common')
|
||||
api libs.kt.coroutines.test
|
||||
api project(":micro_utils.repos.common")
|
||||
}
|
||||
}
|
||||
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
}
|
||||
}
|
||||
nativeMain.dependsOn commonMain
|
||||
linuxX64Main.dependsOn nativeMain
|
||||
mingwX64Main.dependsOn nativeMain
|
||||
linuxArm64Main.dependsOn nativeMain
|
||||
|
||||
androidMain.dependsOn jvmMain
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "$defaultAndroidSettingsPresetPath"
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package dev.inmo.micro_utils.repos.common.tests
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
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
|
||||
|
||||
abstract class CommonKeyValueRepoTests : CommonRepoTests<KeyValueRepo<String, String>>() {
|
||||
@Test
|
||||
fun creatingWorksProperly() = runTest(timeout = 120.seconds) {
|
||||
val repo = repoCreator()
|
||||
val testData = (0 until testSequencesSize).associate {
|
||||
("$it-" + uuid4().toString()) to "$it-" + uuid4().toString()
|
||||
}
|
||||
val updatedTestData = testData.keys.associateWith {
|
||||
"$it-" + uuid4().toString()
|
||||
}
|
||||
|
||||
testData.forEach {
|
||||
repo.set(it.key, it.value)
|
||||
assertEquals(it.value, repo.get(it.key))
|
||||
}
|
||||
|
||||
updatedTestData.forEach {
|
||||
repo.set(it.key, it.value)
|
||||
assertEquals(repo.get(it.key), it.value)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun unsettingWorksProperly() = runTest {
|
||||
val repo = repoCreator()
|
||||
val testData = (0 until testSequencesSize).associate {
|
||||
(it.toString() + uuid4().toString()) to uuid4().toString()
|
||||
}
|
||||
|
||||
repo.set(testData)
|
||||
|
||||
testData.forEach {
|
||||
val id = it.key
|
||||
assertTrue {
|
||||
repo.getAll()[id] == it.value
|
||||
}
|
||||
|
||||
repo.unset(id)
|
||||
assertFalse {
|
||||
repo.contains(id)
|
||||
}
|
||||
}
|
||||
assertEquals(0, repo.count())
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package dev.inmo.micro_utils.repos.common.tests
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
import korlibs.time.seconds
|
||||
import kotlinx.coroutines.joinAll
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.*
|
||||
|
||||
abstract class CommonKeyValuesRepoTests : CommonRepoTests<KeyValuesRepo<String, String>>() {
|
||||
@Test
|
||||
fun creatingWorksProperly() = runTest(timeout = 120.seconds) {
|
||||
val repo = repoCreator()
|
||||
val testData = (0 until testSequencesSize).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()
|
||||
}
|
||||
|
||||
updatedTestData.map {
|
||||
launch {
|
||||
repo.set(it.key, it.value)
|
||||
assertContentEquals(it.value.sorted(), repo.getAll(it.key).sorted())
|
||||
}
|
||||
}.joinAll()
|
||||
|
||||
updatedTestData.map {
|
||||
launch {
|
||||
repo.set(it.key, it.value)
|
||||
val all = repo.getAll(it.key)
|
||||
assertContentEquals(it.value.sorted(), all.sorted())
|
||||
}
|
||||
}.joinAll()
|
||||
|
||||
addedData.forEach {
|
||||
repo.add(it.key, it.value)
|
||||
assertTrue(repo.contains(it.key, it.value))
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package dev.inmo.micro_utils.repos.common.tests
|
||||
|
||||
abstract class CommonRepoTests<T> {
|
||||
protected open val testSequencesSize = 1000
|
||||
protected abstract val repoCreator: suspend () -> T
|
||||
}
|
@ -14,5 +14,12 @@ kotlin {
|
||||
api internalProject("micro_utils.pagination.exposed")
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
dependencies {
|
||||
api libs.sqlite
|
||||
api libs.jb.exposed.jdbc
|
||||
api project(":micro_utils.repos.common.tests")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,9 +52,9 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
|
||||
*
|
||||
* @return In case when id for the model has been created new [IdType] should be returned
|
||||
*/
|
||||
protected open fun createAndInsertId(value: InputValueType, it: InsertStatement<Number>): IdType? = null
|
||||
protected open fun createAndInsertId(value: InputValueType, it: UpdateBuilder<Int>): IdType? = null
|
||||
|
||||
protected open fun insert(value: InputValueType, it: InsertStatement<Number>) {
|
||||
protected open fun insert(value: InputValueType, it: UpdateBuilder<Int>) {
|
||||
val id = createAndInsertId(value, it)
|
||||
update(id, value, it as UpdateBuilder<Int>)
|
||||
}
|
||||
|
@ -23,11 +23,11 @@ abstract class AbstractExposedKeyValueRepo<Key, Value>(
|
||||
override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()
|
||||
|
||||
protected abstract fun update(k: Key, v: Value, it: UpdateBuilder<Int>)
|
||||
protected abstract fun insertKey(k: Key, v: Value, it: InsertStatement<Number>)
|
||||
protected abstract fun insertKey(k: Key, v: Value, it: UpdateBuilder<Int>)
|
||||
|
||||
protected open fun insert(k: Key, v: Value, it: InsertStatement<Number>) {
|
||||
protected open fun insert(k: Key, v: Value, it: UpdateBuilder<Int>) {
|
||||
insertKey(k, v, it)
|
||||
update(k, v, it as UpdateBuilder<Int>)
|
||||
update(k, v, it)
|
||||
}
|
||||
|
||||
override suspend fun set(toSet: Map<Key, Value>) {
|
||||
|
@ -5,6 +5,7 @@ import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.statements.InsertStatement
|
||||
import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
abstract class AbstractExposedKeyValuesRepo<Key, Value>(
|
||||
@ -26,7 +27,7 @@ abstract class AbstractExposedKeyValuesRepo<Key, Value>(
|
||||
override val onDataCleared: Flow<Key>
|
||||
get() = _onDataCleared.asSharedFlow()
|
||||
|
||||
protected abstract fun insert(k: Key, v: Value, it: InsertStatement<Number>)
|
||||
protected abstract fun insert(k: Key, v: Value, it: UpdateBuilder<Int>)
|
||||
|
||||
override suspend fun add(toAdd: Map<Key, List<Value>>) {
|
||||
transaction(database) {
|
||||
@ -48,6 +49,28 @@ abstract class AbstractExposedKeyValuesRepo<Key, Value>(
|
||||
}.forEach { _onNewValue.emit(it) }
|
||||
}
|
||||
|
||||
override suspend fun set(toSet: Map<Key, List<Value>>) {
|
||||
if (toSet.isEmpty()) return
|
||||
val prepreparedData = toSet.flatMap { (k, vs) ->
|
||||
vs.map { v ->
|
||||
k to v
|
||||
}
|
||||
}
|
||||
|
||||
transaction(database) {
|
||||
deleteWhere {
|
||||
selectByIds(it, toSet.keys.toList())
|
||||
}
|
||||
batchInsert(
|
||||
prepreparedData,
|
||||
) { (k, v) ->
|
||||
insert(k, v, this)
|
||||
}.map {
|
||||
it.asKey to it.asObject
|
||||
}
|
||||
}.forEach { _onNewValue.emit(it) }
|
||||
}
|
||||
|
||||
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
||||
transaction(database) {
|
||||
toRemove.keys.flatMap { k ->
|
||||
|
@ -83,8 +83,8 @@ abstract class AbstractExposedReadKeyValuesRepo<Key, Value>(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getAll(k: Key, reverseLists: Boolean): List<Value> = transaction(database) {
|
||||
val query = if (reverseLists) {
|
||||
override suspend fun getAll(k: Key, reversed: Boolean): List<Value> = transaction(database) {
|
||||
val query = if (reversed) {
|
||||
selectAll().where { selectById(k) }.orderBy(keyColumn, SortOrder.DESC)
|
||||
} else {
|
||||
selectAll().where { selectById(k) }
|
||||
|
21
repos/exposed/src/jvmTest/kotlin/Database.kt
Normal file
21
repos/exposed/src/jvmTest/kotlin/Database.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package full
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.transactions.transactionManager
|
||||
import org.sqlite.JDBC
|
||||
import java.io.File
|
||||
import java.sql.Connection
|
||||
|
||||
fun filename() = "${uuid4()}.local.sql".also {
|
||||
val file = File(it)
|
||||
file.createNewFile()
|
||||
file.deleteOnExit()
|
||||
}
|
||||
fun createDatabase(filename: String) = Database.connect(
|
||||
url = "jdbc:sqlite:$filename",
|
||||
driver = JDBC::class.qualifiedName!!
|
||||
).also {
|
||||
it.transactionManager.defaultIsolationLevel = Connection.TRANSACTION_SERIALIZABLE
|
||||
it.connector().close()
|
||||
}
|
62
repos/exposed/src/jvmTest/kotlin/ExposedCRUDRepoTests.kt
Normal file
62
repos/exposed/src/jvmTest/kotlin/ExposedCRUDRepoTests.kt
Normal file
@ -0,0 +1,62 @@
|
||||
package full
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.repos.CRUDRepo
|
||||
import dev.inmo.micro_utils.repos.common.tests.CommonCRUDRepoTests
|
||||
import dev.inmo.micro_utils.repos.exposed.AbstractExposedCRUDRepo
|
||||
import dev.inmo.micro_utils.repos.exposed.initTable
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.ISqlExpressionBuilder
|
||||
import org.jetbrains.exposed.sql.Op
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
import org.jetbrains.exposed.sql.statements.InsertStatement
|
||||
import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
||||
import java.io.File
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.BeforeTest
|
||||
|
||||
class ExposedCRUDRepoTests : CommonCRUDRepoTests() {
|
||||
class Repo(override val database: Database) : AbstractExposedCRUDRepo<Registered, String, New>() {
|
||||
val idColumn = text("_id")
|
||||
val dataColumn = text("data")
|
||||
|
||||
override val primaryKey: PrimaryKey = PrimaryKey(idColumn)
|
||||
|
||||
override val ResultRow.asId: String
|
||||
get() = get(idColumn)
|
||||
override val ResultRow.asObject: Registered
|
||||
get() = Registered(
|
||||
asId,
|
||||
get(dataColumn)
|
||||
)
|
||||
override val selectById: ISqlExpressionBuilder.(String) -> Op<Boolean> = { idColumn.eq(it) }
|
||||
|
||||
init {
|
||||
initTable()
|
||||
}
|
||||
|
||||
override fun update(id: String?, value: New, it: UpdateBuilder<Int>) {
|
||||
it[idColumn] = id ?: uuid4().toString()
|
||||
it[dataColumn] = value.data
|
||||
}
|
||||
|
||||
override fun InsertStatement<Number>.asObject(value: New): Registered {
|
||||
return Registered(
|
||||
get(idColumn),
|
||||
get(dataColumn)
|
||||
)
|
||||
}
|
||||
}
|
||||
val filename = filename()
|
||||
var database: Database? = null
|
||||
override val repoCreator: suspend () -> CRUDRepo<Registered, String, New> = { Repo(database!!) }
|
||||
@BeforeTest
|
||||
fun beforeTest() {
|
||||
database = createDatabase(filename)
|
||||
}
|
||||
@AfterTest
|
||||
fun afterTest() {
|
||||
database = null
|
||||
File(filename).delete()
|
||||
}
|
||||
}
|
56
repos/exposed/src/jvmTest/kotlin/ExposedKeyValueRepoTests.kt
Normal file
56
repos/exposed/src/jvmTest/kotlin/ExposedKeyValueRepoTests.kt
Normal file
@ -0,0 +1,56 @@
|
||||
package full
|
||||
|
||||
import dev.inmo.micro_utils.repos.KeyValueRepo
|
||||
import dev.inmo.micro_utils.repos.common.tests.CommonKeyValueRepoTests
|
||||
import dev.inmo.micro_utils.repos.exposed.initTable
|
||||
import dev.inmo.micro_utils.repos.exposed.keyvalue.AbstractExposedKeyValueRepo
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.ISqlExpressionBuilder
|
||||
import org.jetbrains.exposed.sql.Op
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
||||
import java.io.File
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.BeforeTest
|
||||
|
||||
class ExposedKeyValueRepoTests : CommonKeyValueRepoTests() {
|
||||
class Repo(override val database: Database) : AbstractExposedKeyValueRepo<String, String>(database) {
|
||||
override val keyColumn = text("_id")
|
||||
val dataColumn = text("data")
|
||||
|
||||
override val primaryKey: PrimaryKey = PrimaryKey(keyColumn)
|
||||
|
||||
override val ResultRow.asKey: String
|
||||
get() = get(keyColumn)
|
||||
override val selectByValue: ISqlExpressionBuilder.(String) -> Op<Boolean> = { dataColumn.eq(it) }
|
||||
override val ResultRow.asObject: String
|
||||
get() = get(dataColumn)
|
||||
override val selectById: ISqlExpressionBuilder.(String) -> Op<Boolean> = { keyColumn.eq(it) }
|
||||
|
||||
init {
|
||||
initTable()
|
||||
}
|
||||
|
||||
override fun update(k: String, v: String, it: UpdateBuilder<Int>) {
|
||||
it[keyColumn] = k
|
||||
it[dataColumn] = v
|
||||
}
|
||||
|
||||
override fun insertKey(k: String, v: String, it: UpdateBuilder<Int>) {
|
||||
it[keyColumn] = k
|
||||
}
|
||||
}
|
||||
val filename = filename()
|
||||
var database: Database? = null
|
||||
@BeforeTest
|
||||
fun beforeTest() {
|
||||
database = createDatabase(filename)
|
||||
}
|
||||
@AfterTest
|
||||
fun afterTest() {
|
||||
database = null
|
||||
File(filename).delete()
|
||||
}
|
||||
|
||||
override val repoCreator: suspend () -> KeyValueRepo<String, String> = { Repo(database!!) }
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package full
|
||||
|
||||
import dev.inmo.micro_utils.repos.KeyValuesRepo
|
||||
import dev.inmo.micro_utils.repos.common.tests.CommonKeyValuesRepoTests
|
||||
import dev.inmo.micro_utils.repos.exposed.initTable
|
||||
import dev.inmo.micro_utils.repos.exposed.onetomany.AbstractExposedKeyValuesRepo
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.ISqlExpressionBuilder
|
||||
import org.jetbrains.exposed.sql.Op
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
||||
import java.io.File
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.BeforeTest
|
||||
|
||||
class ExposedKeyValuesRepoTests : CommonKeyValuesRepoTests() {
|
||||
override val testSequencesSize: Int = 100
|
||||
class Repo(override val database: Database) : AbstractExposedKeyValuesRepo<String, String>(database) {
|
||||
|
||||
override val keyColumn = text("_id")
|
||||
val dataColumn = text("data")
|
||||
|
||||
override val ResultRow.asKey: String
|
||||
get() = get(keyColumn)
|
||||
override val selectByValue: ISqlExpressionBuilder.(String) -> Op<Boolean> = { dataColumn.eq(it) }
|
||||
override val ResultRow.asObject: String
|
||||
get() = get(dataColumn)
|
||||
override val selectById: ISqlExpressionBuilder.(String) -> Op<Boolean> = { keyColumn.eq(it) }
|
||||
|
||||
init {
|
||||
initTable()
|
||||
}
|
||||
|
||||
override fun insert(k: String, v: String, it: UpdateBuilder<Int>) {
|
||||
it[keyColumn] = k
|
||||
it[dataColumn] = v
|
||||
}
|
||||
}
|
||||
val filename = filename()
|
||||
var database: Database? = null
|
||||
@BeforeTest
|
||||
fun beforeTest() {
|
||||
database = createDatabase(filename)
|
||||
}
|
||||
@AfterTest
|
||||
fun afterTest() {
|
||||
database = null
|
||||
File(filename).delete()
|
||||
}
|
||||
|
||||
override val repoCreator: suspend () -> KeyValuesRepo<String, String> = { Repo(database!!) }
|
||||
}
|
@ -14,5 +14,10 @@ kotlin {
|
||||
api internalProject("micro_utils.coroutines")
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
dependencies {
|
||||
api project(":micro_utils.repos.common.tests")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
@ -100,11 +102,13 @@ class WriteMapKeyValueRepo<Key, Value>(
|
||||
constructor(map: MutableMap<Key, Value> = mutableMapOf()) : this(map, SmartRWLocker())
|
||||
|
||||
override suspend fun set(toSet: Map<Key, Value>) {
|
||||
if (toSet.isEmpty()) return
|
||||
locker.withWriteLock { map.putAll(toSet) }
|
||||
toSet.forEach { (k, v) -> _onNewValue.emit(k to v) }
|
||||
}
|
||||
|
||||
override suspend fun unset(toUnset: List<Key>) {
|
||||
if (toUnset.isEmpty()) return
|
||||
locker.withWriteLock {
|
||||
toUnset.mapNotNull { k ->
|
||||
map.remove(k) ?.let { _ -> k }
|
||||
|
@ -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,7 +123,24 @@ class MapWriteKeyValuesRepo<Key, Value>(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun set(toSet: Map<Key, List<Value>>) {
|
||||
if (toSet.isEmpty()) return
|
||||
|
||||
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>>) {
|
||||
if (toRemove.isEmpty()) return
|
||||
|
||||
val removed = mutableListOf<Pair<Key, Value>>()
|
||||
val cleared = mutableListOf<Key>()
|
||||
locker.withWriteLock {
|
||||
|
@ -0,0 +1,19 @@
|
||||
package full
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.repos.CRUDRepo
|
||||
import dev.inmo.micro_utils.repos.MapCRUDRepo
|
||||
import dev.inmo.micro_utils.repos.common.tests.CommonCRUDRepoTests
|
||||
|
||||
class InMemoryCRUDRepoTests : CommonCRUDRepoTests() {
|
||||
override val repoCreator: suspend () -> CRUDRepo<Registered, String, New> = {
|
||||
MapCRUDRepo(
|
||||
{ new, id, old ->
|
||||
Registered(id, new.data)
|
||||
}
|
||||
) {
|
||||
val id = uuid4().toString()
|
||||
id to Registered(id, it.data)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package full
|
||||
|
||||
import dev.inmo.micro_utils.repos.KeyValueRepo
|
||||
import dev.inmo.micro_utils.repos.MapKeyValueRepo
|
||||
import dev.inmo.micro_utils.repos.common.tests.CommonKeyValueRepoTests
|
||||
|
||||
class InMemoryKeyValueRepoTests : CommonKeyValueRepoTests() {
|
||||
override val repoCreator: suspend () -> KeyValueRepo<String, String> = { MapKeyValueRepo() }
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package full
|
||||
|
||||
import dev.inmo.micro_utils.repos.KeyValuesRepo
|
||||
import dev.inmo.micro_utils.repos.MapKeyValuesRepo
|
||||
import dev.inmo.micro_utils.repos.common.tests.CommonKeyValuesRepoTests
|
||||
|
||||
class InMemoryKeyValuesRepoTests : CommonKeyValuesRepoTests() {
|
||||
override val repoCreator: suspend () -> KeyValuesRepo<String, String> = { MapKeyValuesRepo() }
|
||||
}
|
@ -16,6 +16,7 @@ kotlin {
|
||||
jvmTest {
|
||||
dependencies {
|
||||
implementation internalProject("micro_utils.repos.common")
|
||||
implementation internalProject("micro_utils.repos.common.tests")
|
||||
implementation internalProject("micro_utils.repos.ktor.client")
|
||||
implementation internalProject("micro_utils.repos.ktor.server")
|
||||
implementation internalProject("micro_utils.repos.inmemory")
|
||||
|
@ -1,3 +1,5 @@
|
||||
package dev.inmo.micro_utils.repos.ktor
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
package dev.inmo.micro_utils.repos.ktor
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
import dev.inmo.micro_utils.repos.common.tests.CommonCRUDRepoTests
|
||||
import dev.inmo.micro_utils.repos.ktor.client.crud.KtorCRUDRepoClient
|
||||
import dev.inmo.micro_utils.repos.ktor.server.crud.configureCRUDRepoRoutes
|
||||
import io.ktor.client.HttpClient
|
||||
@ -8,17 +12,53 @@ import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.server.routing.routing
|
||||
import io.ktor.server.websocket.WebSockets
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestResult
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class CRUDTests {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class KtorCRUDRepoTests : CommonCRUDRepoTests() {
|
||||
private var engine: ApplicationEngine? = null
|
||||
|
||||
@BeforeTest
|
||||
fun beforeTest() {
|
||||
engine = KtorRepoTestsHelper.beforeTest {
|
||||
configureCRUDRepoRoutes(
|
||||
MapCRUDRepo<Registered, String, New>(
|
||||
{ new, id, old ->
|
||||
Registered(id, new.data)
|
||||
}
|
||||
) {
|
||||
val id = uuid4().toString()
|
||||
id to Registered(id, it.data)
|
||||
}
|
||||
) {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
@AfterTest
|
||||
fun afterTest() {
|
||||
engine ?.let(KtorRepoTestsHelper::afterTest)
|
||||
}
|
||||
|
||||
override val repoCreator: suspend () -> CRUDRepo<Registered, String, New> = {
|
||||
KtorCRUDRepoClient<Registered, String, New>(
|
||||
"http://127.0.0.1:23456",
|
||||
KtorRepoTestsHelper.client(),
|
||||
ContentType.Application.Json
|
||||
) {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCRUDFunctions() {
|
||||
runTest {
|
||||
@ -33,7 +73,7 @@ class CRUDTests {
|
||||
}
|
||||
val server = io.ktor.server.engine.embeddedServer(
|
||||
CIO,
|
||||
23456,
|
||||
34567,
|
||||
"127.0.0.1"
|
||||
) {
|
||||
install(ContentNegotiation) {
|
||||
@ -60,7 +100,7 @@ class CRUDTests {
|
||||
}
|
||||
}
|
||||
val crudClient = KtorCRUDRepoClient<ComplexData, Int, SimpleData>(
|
||||
"http://127.0.0.1:23456",
|
||||
"http://127.0.0.1:34567",
|
||||
client,
|
||||
ContentType.Application.Json
|
||||
) {
|
@ -1,8 +1,13 @@
|
||||
package dev.inmo.micro_utils.repos.ktor
|
||||
|
||||
import dev.inmo.micro_utils.pagination.firstPageWithOneElementPagination
|
||||
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
import dev.inmo.micro_utils.repos.common.tests.CommonKeyValueRepoTests
|
||||
import dev.inmo.micro_utils.repos.ktor.client.key.value.KtorKeyValueRepoClient
|
||||
import dev.inmo.micro_utils.repos.ktor.client.key.values.KtorKeyValuesRepoClient
|
||||
import dev.inmo.micro_utils.repos.ktor.server.key.value.configureKeyValueRepoRoutes
|
||||
import dev.inmo.micro_utils.repos.ktor.server.key.values.configureKeyValuesRepoRoutes
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.plugins.logging.Logging
|
||||
import io.ktor.http.ContentType
|
||||
@ -10,17 +15,47 @@ import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.server.routing.routing
|
||||
import io.ktor.server.websocket.WebSockets
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestResult
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.test.*
|
||||
|
||||
class KVTests {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class KtorKeyValueRepoTests : CommonKeyValueRepoTests() {
|
||||
private var engine: ApplicationEngine? = null
|
||||
|
||||
override val repoCreator: suspend () -> KeyValueRepo<String, String> = {
|
||||
KtorKeyValueRepoClient(
|
||||
"http://127.0.0.1:23456",
|
||||
KtorRepoTestsHelper.client(),
|
||||
ContentType.Application.Json,
|
||||
String.serializer(),
|
||||
String.serializer(),
|
||||
Json
|
||||
)
|
||||
}
|
||||
|
||||
@BeforeTest
|
||||
fun beforeTest() {
|
||||
engine = KtorRepoTestsHelper.beforeTest {
|
||||
configureKeyValueRepoRoutes(
|
||||
MapKeyValueRepo(),
|
||||
String.serializer(),
|
||||
String.serializer(),
|
||||
Json
|
||||
)
|
||||
}
|
||||
}
|
||||
@AfterTest
|
||||
fun afterTest() {
|
||||
engine ?.let(KtorRepoTestsHelper::afterTest)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKVFunctions() {
|
||||
runTest {
|
||||
@ -28,7 +63,7 @@ class KVTests {
|
||||
val repo = MapKeyValueRepo<Int, ComplexData>(map)
|
||||
val server = io.ktor.server.engine.embeddedServer(
|
||||
CIO,
|
||||
23456,
|
||||
34567,
|
||||
"127.0.0.1"
|
||||
) {
|
||||
install(ContentNegotiation) {
|
||||
@ -56,7 +91,7 @@ class KVTests {
|
||||
}
|
||||
}
|
||||
val crudClient = KtorKeyValueRepoClient<Int, ComplexData>(
|
||||
"http://127.0.0.1:23456",
|
||||
"http://127.0.0.1:34567",
|
||||
client,
|
||||
ContentType.Application.Json,
|
||||
Int.serializer(),
|
@ -1,26 +1,59 @@
|
||||
package dev.inmo.micro_utils.repos.ktor
|
||||
|
||||
import dev.inmo.micro_utils.pagination.firstPageWithOneElementPagination
|
||||
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
import dev.inmo.micro_utils.repos.common.tests.CommonKeyValuesRepoTests
|
||||
import dev.inmo.micro_utils.repos.ktor.client.key.values.KtorKeyValuesRepoClient
|
||||
import dev.inmo.micro_utils.repos.ktor.server.key.values.configureKeyValuesRepoRoutes
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.plugins.logging.Logging
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.server.routing.routing
|
||||
import io.ktor.server.websocket.WebSockets
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import io.ktor.serialization.kotlinx.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.cio.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.websocket.*
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.test.*
|
||||
|
||||
class KVsTests {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class KtorKeyValuesRepoTests : CommonKeyValuesRepoTests() {
|
||||
private var engine: ApplicationEngine? = null
|
||||
override val testSequencesSize: Int
|
||||
get() = 100
|
||||
|
||||
override val repoCreator: suspend () -> KeyValuesRepo<String, String> = {
|
||||
KtorKeyValuesRepoClient(
|
||||
"http://127.0.0.1:23456",
|
||||
KtorRepoTestsHelper.client(),
|
||||
ContentType.Application.Json,
|
||||
String.serializer(),
|
||||
String.serializer(),
|
||||
Json
|
||||
)
|
||||
}
|
||||
|
||||
@BeforeTest
|
||||
fun beforeTest() {
|
||||
engine = KtorRepoTestsHelper.beforeTest {
|
||||
configureKeyValuesRepoRoutes(
|
||||
MapKeyValuesRepo(),
|
||||
String.serializer(),
|
||||
String.serializer(),
|
||||
Json
|
||||
)
|
||||
}
|
||||
}
|
||||
@AfterTest
|
||||
fun afterTest() {
|
||||
engine ?.let(KtorRepoTestsHelper::afterTest)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKVsFunctions() {
|
||||
runTest {
|
||||
@ -28,7 +61,7 @@ class KVsTests {
|
||||
val repo = MapKeyValuesRepo(map)
|
||||
val server = io.ktor.server.engine.embeddedServer(
|
||||
CIO,
|
||||
23456,
|
||||
34567,
|
||||
"127.0.0.1"
|
||||
) {
|
||||
install(ContentNegotiation) {
|
||||
@ -42,7 +75,7 @@ class KVsTests {
|
||||
repo,
|
||||
Int.serializer(),
|
||||
ComplexData.serializer(),
|
||||
Json {}
|
||||
Json
|
||||
)
|
||||
}
|
||||
}.start(false)
|
||||
@ -56,7 +89,7 @@ class KVsTests {
|
||||
}
|
||||
}
|
||||
val crudClient = KtorKeyValuesRepoClient(
|
||||
"http://127.0.0.1:23456",
|
||||
"http://127.0.0.1:34567",
|
||||
client,
|
||||
ContentType.Application.Json,
|
||||
Int.serializer(),
|
44
repos/ktor/common/src/jvmTest/kotlin/KtorRepoTestsHelper.kt
Normal file
44
repos/ktor/common/src/jvmTest/kotlin/KtorRepoTestsHelper.kt
Normal file
@ -0,0 +1,44 @@
|
||||
package dev.inmo.micro_utils.repos.ktor
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.serialization.kotlinx.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.cio.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.websocket.*
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
object KtorRepoTestsHelper {
|
||||
fun beforeTest(routingConfigurator: Routing.() -> Unit): ApplicationEngine {
|
||||
return embeddedServer(
|
||||
CIO,
|
||||
23456,
|
||||
"127.0.0.1"
|
||||
) {
|
||||
install(ContentNegotiation) {
|
||||
json()
|
||||
}
|
||||
install(WebSockets) {
|
||||
contentConverter = KotlinxWebsocketSerializationConverter(Json)
|
||||
}
|
||||
routing(routingConfigurator)
|
||||
}.start(false)
|
||||
}
|
||||
fun afterTest(engine: ApplicationEngine) {
|
||||
engine.stop()
|
||||
}
|
||||
fun client(): HttpClient = HttpClient {
|
||||
install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) {
|
||||
json()
|
||||
}
|
||||
install(Logging)
|
||||
install(io.ktor.client.plugins.websocket.WebSockets) {
|
||||
contentConverter = KotlinxWebsocketSerializationConverter(Json)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
package dev.inmo.micro_utils.repos.ktor
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
@ -18,6 +18,7 @@ String[] includes = [
|
||||
":language_codes",
|
||||
":language_codes:generator",
|
||||
":repos:common",
|
||||
":repos:common:tests",
|
||||
":repos:generator",
|
||||
":repos:generator:test",
|
||||
":repos:cache",
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user