Compare commits

...

34 Commits

Author SHA1 Message Date
1bd671685b update dependencies and remove redundant usages of old IetfLanguageCode 2023-12-21 23:25:36 +06:00
48d3fe41f2 a lot of improvements in language codes 2023-12-21 15:41:11 +06:00
7ab21871cd Revert "add klock module"
This reverts commit 65d01b1fb3.
2023-12-21 14:05:27 +06:00
65d01b1fb3 add klock module 2023-12-17 23:37:45 +06:00
6230accb68 start 0.20.23 2023-12-17 22:30:25 +06:00
10e03bb951 allow to create own Diff with constructor 2023-12-14 23:56:56 +06:00
aa4f392948 start 0.20.22 2023-12-14 23:52:28 +06:00
f51b59ec02 Update libs.versions.toml 2023-12-14 13:47:34 +06:00
8c76834ae4 Merge pull request #359 from InsanusMokrassar/0.20.21
0.20.21
2023-12-12 23:49:26 +06:00
4a454f3d67 add opportunity to use translation with default locale 2023-12-12 23:45:22 +06:00
151aa1863d update gradle wrapper 2023-12-12 21:41:53 +06:00
3bf6896296 get back translations with android and java resources packages 2023-12-12 21:41:09 +06:00
0d01561476 optimizations and improvements in resources 2023-12-12 21:39:58 +06:00
f6ded92251 init resources module 2023-12-12 20:58:08 +06:00
d01b735cc6 start 0.20.21 2023-12-12 20:21:37 +06:00
6c12001080 Merge pull request #358 from InsanusMokrassar/0.20.20
0.20.20
2023-12-12 00:12:34 +06:00
1afbf03606 improvements in AbstractExposedCRUDRepo 2023-12-12 00:01:58 +06:00
f6ef5c61c5 start 0.20.20 2023-12-11 23:54:25 +06:00
c18fee8107 update gradle publishing scripts
one more potential ix of publishing scripts

drop new configs of publish scripts

revert publish scripts and update gradle properties
2023-12-09 20:38:44 +06:00
d9df7a4384 Revert gradle wrapper version 2023-12-09 01:38:45 +06:00
87c2230e8e revert xmx 2500 2023-12-08 23:42:26 +06:00
da7eb6de0a change xmx 2023-12-08 21:34:05 +06:00
ed3815118f update jvmargs of gradle 2023-12-08 19:34:47 +06:00
be726f42bd Merge pull request #356 from InsanusMokrassar/0.20.19
update publishing scripts
2023-12-08 16:06:22 +06:00
a91006132f update publishing scripts 2023-12-08 16:06:07 +06:00
9a9f741a0b Merge pull request #355 from InsanusMokrassar/0.20.19
0.20.19
2023-12-08 16:03:05 +06:00
5028f130e9 update dependencies 2023-12-08 15:58:16 +06:00
77fa019651 start 0.20.19 2023-12-08 15:56:44 +06:00
9715da9384 Merge pull request #353 from InsanusMokrassar/0.20.18
0.20.18
2023-12-04 16:13:33 +06:00
f6d5035c1a small refactor in SpecialMutableStateFlow 2023-12-04 15:44:06 +06:00
43e782ab6f SpecialMutableStateFlow : MutableStateFlow 2023-12-04 15:37:02 +06:00
f3f9920bfb deprecate FlowState 2023-12-04 15:08:52 +06:00
2bfd615812 start 0.20.18 2023-12-04 15:01:42 +06:00
ebfacb3659 Merge pull request #352 from InsanusMokrassar/0.20.17
0.20.17
2023-12-01 02:51:00 +06:00
25 changed files with 3237 additions and 2534 deletions

View File

@@ -1,5 +1,44 @@
# Changelog # Changelog
## 0.20.23
* `Versions`:
* `Koin`: `3.5.0` -> `3.5.3`
* `Okio`: `3.6.0` -> `3.7.0`
* `LanguageCodes`:
* Fixes in intermediate language codes (like `Chinese.Hans`)
* Rename `IetfLanguageCode` to `IetfLang`
* Rename all subsequent functions (including serializer)
* New lazy properties `knownLanguageCodesMap`, `knownLanguageCodesMapByLowerCasedKeys` and several others
## 0.20.22
* `Common`:
* Add opportunity to create own `Diff` with base constructor
## 0.20.21
* `Resources`:
* Inited
## 0.20.20
* `Repos`:
* `Exposed`:
* Add opportunity for setup flows in `AbstractExposedCRUDRepo`
## 0.20.19
* `Versions`:
* `Ktor`: `2.3.6` -> `2.3.7`
## 0.20.18
* `Coroutines`:
* `SpecialMutableStateFlow` now extends `MutableStateFlow`
* `Compose`:
* Deprecate `FlowState` due to its complexity in fixes
## 0.20.17 ## 0.20.17
* `Versions`: * `Versions`:

View File

@@ -27,7 +27,7 @@ allprojects {
mavenCentral() mavenCentral()
google() google()
maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" } maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" }
maven { url "https://git.inmo.dev/api/packages/InsanusMokrassar/maven" } maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
} }
// temporal crutch until legacy tests will be stabled or legacy target will be removed // temporal crutch until legacy tests will be stabled or legacy target will be removed

View File

@@ -27,7 +27,7 @@ private inline fun <T> getObject(
* @see calculateDiff * @see calculateDiff
*/ */
@Serializable @Serializable
data class Diff<T> internal constructor( data class Diff<T> @Warning(warning) constructor(
val removed: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>>, val removed: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>>,
/** /**
* Old-New values pairs * Old-New values pairs
@@ -36,6 +36,10 @@ data class Diff<T> internal constructor(
val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>> val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>>
) { ) {
fun isEmpty(): Boolean = removed.isEmpty() && replaced.isEmpty() && added.isEmpty() fun isEmpty(): Boolean = removed.isEmpty() && replaced.isEmpty() && added.isEmpty()
companion object {
private const val warning = "This feature can be changed without any warranties. Use with caution and only in case you know what you are doing"
}
} }
fun <T> emptyDiff(): Diff<T> = Diff(emptyList(), emptyList(), emptyList()) fun <T> emptyDiff(): Diff<T> = Diff(emptyList(), emptyList(), emptyList())

View File

@@ -1,10 +1,7 @@
package dev.inmo.micro_utils.coroutines.compose package dev.inmo.micro_utils.coroutines.compose
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
import dev.inmo.micro_utils.coroutines.doInUI
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -12,6 +9,7 @@ import kotlinx.coroutines.Dispatchers
* This type works like [MutableState], [kotlinx.coroutines.flow.StateFlow] and [kotlinx.coroutines.flow.MutableSharedFlow]. * This type works like [MutableState], [kotlinx.coroutines.flow.StateFlow] and [kotlinx.coroutines.flow.MutableSharedFlow].
* Based on [SpecialMutableStateFlow] * Based on [SpecialMutableStateFlow]
*/ */
@Deprecated("Will be removed soon")
class FlowState<T>( class FlowState<T>(
initial: T, initial: T,
internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default) internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
@@ -25,9 +23,9 @@ class FlowState<T>(
tryEmit(value) tryEmit(value)
} }
override suspend fun onChange(value: T) { override fun onChangeWithoutSync(value: T) {
internalValue = value internalValue = value
super.onChange(value) super.onChangeWithoutSync(value)
} }
override fun component1(): T = value override fun component1(): T = value

View File

@@ -3,10 +3,14 @@ package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.internal.SynchronizedObject
import kotlinx.coroutines.internal.synchronized
/** /**
* Works like [StateFlow], but guarantee that latest value update will always be delivered to * Works like [StateFlow], but guarantee that latest value update will always be delivered to
@@ -15,7 +19,9 @@ import kotlinx.coroutines.flow.StateFlow
open class SpecialMutableStateFlow<T>( open class SpecialMutableStateFlow<T>(
initialValue: T, initialValue: T,
internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default) internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : StateFlow<T>, FlowCollector<T>, MutableSharedFlow<T> { ) : MutableStateFlow<T>, FlowCollector<T>, MutableSharedFlow<T> {
@OptIn(InternalCoroutinesApi::class)
private val syncObject = SynchronizedObject()
protected val internalSharedFlow: MutableSharedFlow<T> = MutableSharedFlow( protected val internalSharedFlow: MutableSharedFlow<T> = MutableSharedFlow(
replay = 0, replay = 0,
extraBufferCapacity = 2, extraBufferCapacity = 2,
@@ -28,16 +34,13 @@ open class SpecialMutableStateFlow<T>(
) )
protected var _value: T = initialValue protected var _value: T = initialValue
override val value: T override var value: T
get() = _value get() = _value
protected open suspend fun onChange(value: T) { set(value) {
_value = value doOnChangeAction(value)
publicSharedFlow.emit(value)
}
protected val job = internalSharedFlow.subscribe(internalScope) {
if (_value != it) {
onChange(it)
} }
protected val job = internalSharedFlow.subscribe(internalScope) {
doOnChangeAction(it)
} }
override val replayCache: List<T> override val replayCache: List<T>
@@ -45,6 +48,29 @@ open class SpecialMutableStateFlow<T>(
override val subscriptionCount: StateFlow<Int> override val subscriptionCount: StateFlow<Int>
get() = publicSharedFlow.subscriptionCount get() = publicSharedFlow.subscriptionCount
@OptIn(InternalCoroutinesApi::class)
override fun compareAndSet(expect: T, update: T): Boolean {
return synchronized(syncObject) {
if (expect == _value && update != _value) {
doOnChangeAction(update)
}
expect == _value
}
}
protected open fun onChangeWithoutSync(value: T) {
_value = value
publicSharedFlow.tryEmit(value)
}
@OptIn(InternalCoroutinesApi::class)
protected open fun doOnChangeAction(value: T) {
synchronized(syncObject) {
if (_value != value) {
onChangeWithoutSync(value)
}
}
}
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
override fun resetReplayCache() = publicSharedFlow.resetReplayCache() override fun resetReplayCache() = publicSharedFlow.resetReplayCache()

View File

@@ -6,7 +6,7 @@ kotlin.incremental.js=true
#kotlin.experimental.tryK2=true #kotlin.experimental.tryK2=true
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
org.gradle.jvmargs=-Xmx2500m org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=2g
# JS NPM # JS NPM
@@ -15,5 +15,5 @@ crypto_js_version=4.1.1
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.20.17 version=0.20.23
android_code_version=223 android_code_version=229

View File

@@ -13,16 +13,16 @@ jb-dokka = "1.9.10"
korlibs = "4.0.10" korlibs = "4.0.10"
uuid = "0.8.2" uuid = "0.8.2"
ktor = "2.3.6" ktor = "2.3.7"
gh-release = "2.4.1" gh-release = "2.4.1"
koin = "3.5.0" koin = "3.5.3"
okio = "3.6.0" okio = "3.7.0"
ksp = "1.9.21-1.0.15" ksp = "1.9.21-1.0.16"
kotlin-poet = "1.15.2" kotlin-poet = "1.15.3"
versions = "0.50.0" versions = "0.50.0"

View File

@@ -68,18 +68,14 @@ publishing {
} }
} }
if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) { if ((project.hasProperty('INMONEXUS_USER') || System.getenv('INMONEXUS_USER') != null) && (project.hasProperty('INMONEXUS_PASSWORD') || System.getenv('INMONEXUS_PASSWORD') != null)) {
maven { maven {
name = "Gitea" name = "InmoNexus"
url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven") url = uri("https://nexus.inmo.dev/repository/maven-releases/")
credentials(HttpHeaderCredentials) { credentials {
name = "Authorization" username = project.hasProperty('INMONEXUS_USER') ? project.property('INMONEXUS_USER') : System.getenv('INMONEXUS_USER')
value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN') password = project.hasProperty('INMONEXUS_PASSWORD') ? project.property('INMONEXUS_PASSWORD') : System.getenv('INMONEXUS_PASSWORD')
}
authentication {
header(HttpHeaderAuthentication)
} }
} }
@@ -121,4 +117,21 @@ if (project.hasProperty("signing.gnupg.keyName")) {
def signingTasks = project.getTasks().withType(Sign.class) def signingTasks = project.getTasks().withType(Sign.class)
mustRunAfter(signingTasks) mustRunAfter(signingTasks)
} }
// Workaround to make test tasks use sign
project.getTasks().withType(Sign.class).configureEach { signTask ->
def withoutSign = (signTask.name.startsWith("sign") ? signTask.name.minus("sign") : signTask.name)
def pubName = withoutSign.endsWith("Publication") ? withoutSign.substring(0, withoutSign.length() - "Publication".length()) : withoutSign
// These tasks only exist for native targets, hence findByName() to avoid trying to find them for other targets
// Task ':linkDebugTest<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def debugTestTask = tasks.findByName("linkDebugTest$pubName")
if (debugTestTask != null) {
signTask.mustRunAfter(debugTestTask)
}
// Task ':compileTestKotlin<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def testTask = tasks.findByName("compileTestKotlin$pubName")
if (testTask != null) {
signTask.mustRunAfter(testTask)
}
}
} }

View File

@@ -1 +1 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"It is set of projects with micro tools for avoiding of routines coding","url":"https://github.com/InsanusMokrassar/MicroUtils/","vcsUrl":"https://github.com/InsanusMokrassar/MicroUtils.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/MicroUtils"},{"name":"Gitea","url":"https://git.inmo.dev/api/packages/InsanusMokrassar/maven","credsType":{"type":"dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository.CredentialsType.HttpHeaderCredentials","headerName":"Authorization","headerValueProperty":"GITEA_TOKEN"}},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}},"type":"JVM"} {"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"It is set of projects with micro tools for avoiding of routines coding","url":"https://github.com/InsanusMokrassar/MicroUtils/","vcsUrl":"https://github.com/InsanusMokrassar/MicroUtils.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/MicroUtils"},{"name":"InmoNexus","url":"https://nexus.inmo.dev/repository/maven-releases/"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}},"type":"JVM"}

View File

@@ -12,9 +12,12 @@ private val json = Json {
ignoreUnknownKeys = true ignoreUnknownKeys = true
} }
private const val baseClassName = "IetfLanguageCode" private const val baseClassName = "IetfLang"
private const val oldBaseClassName = "IetfLanguageCode"
private const val unknownBaseClassName = "Unknown$baseClassName" private const val unknownBaseClassName = "Unknown$baseClassName"
private const val baseClassSerializerName = "IetfLanguageCodeSerializer" private const val oldUnknownBaseClassName = "Unknown$oldBaseClassName"
private const val baseClassSerializerName = "${baseClassName}Serializer"
private const val oldBaseClassSerializerName = "IetfLanguageCodeSerializer"
private const val baseClassSerializerAnnotationName = "@Serializable(${baseClassSerializerName}::class)" private const val baseClassSerializerAnnotationName = "@Serializable(${baseClassSerializerName}::class)"
@Serializable @Serializable
@@ -78,14 +81,12 @@ private fun printLanguageCodeAndTags(
indents: String = " " indents: String = " "
): String = if (tag.subtags.isEmpty()) { ): String = if (tag.subtags.isEmpty()) {
"""${indents}${baseClassSerializerAnnotationName} """${indents}${baseClassSerializerAnnotationName}
${indents}object ${tag.title} : ${parent ?.title ?: baseClassName}() { override val code: String = "${tag.tag}"; override val withoutDialect: String get() = ${parent ?.title ?.let { "$it.code" } ?: "code"} }""" ${indents}object ${tag.title} : ${parent ?.title ?: baseClassName}() { override val code: String = "${tag.tag}"${parent ?.let { parent -> "; override val parentLang: ${parent.title} get() = ${parent.title};" } ?: ""} }"""
} else { } else {
""" """
${indents}${baseClassSerializerAnnotationName} ${indents}${baseClassSerializerAnnotationName}
${indents}sealed class ${tag.title} : ${parent ?.title ?: baseClassName}() { ${indents}sealed class ${tag.title} : ${parent ?.title ?: baseClassName}() {
${indents} override val code: String = "${tag.tag}" ${indents} override val code: String = "${tag.tag}"${parent ?.let { parent -> "\n${indents} override val parentLang: ${parent.title} get() = ${parent.title};" } ?: ""}
${indents} override val withoutDialect: String
${indents} get() = code
${tag.subtags.joinToString("\n") { printLanguageCodeAndTags(it, tag, "${indents} ") }} ${tag.subtags.joinToString("\n") { printLanguageCodeAndTags(it, tag, "${indents} ") }}
@@ -95,7 +96,7 @@ ${indents}}
""" """
} }
fun buildKtFileContent(tags: List<Tag>): String = """ fun buildKtFileContent(tags: List<Tag>, prePackage: String): String = """
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
/** /**
@@ -106,20 +107,27 @@ import kotlinx.serialization.Serializable
${baseClassSerializerAnnotationName} ${baseClassSerializerAnnotationName}
sealed class $baseClassName { sealed class $baseClassName {
abstract val code: String abstract val code: String
abstract val withoutDialect: String open val parentLang: $baseClassName?
get() = null
open val withoutDialect: String
get() = parentLang ?.code ?: code
${tags.joinToString("\n") { printLanguageCodeAndTags(it, indents = " ") } } ${tags.joinToString("\n") { printLanguageCodeAndTags(it, indents = " ") } }
$baseClassSerializerAnnotationName $baseClassSerializerAnnotationName
data class $unknownBaseClassName (override val code: String) : $baseClassName() { data class $unknownBaseClassName (override val code: String) : $baseClassName() {
override val withoutDialect: String = code.takeWhile { it != '-' } override val parentLang = code.dropLastWhile { it != '-' }.removeSuffix("-").takeIf { it.length > 0 } ?.let(::$unknownBaseClassName)
} }
@Deprecated("Renamed", ReplaceWith("$baseClassName.$unknownBaseClassName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName.$unknownBaseClassName"))
val $oldUnknownBaseClassName = $unknownBaseClassName
override fun toString() = code override fun toString() = code
} }
@Deprecated("Renamed", ReplaceWith("$baseClassName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName"))
typealias $oldBaseClassName = $baseClassName
""".trimIndent() """.trimIndent()
fun createStringConverterCode(tags: List<Tag>): String { fun createStringConverterCode(tags: List<Tag>, prePackage: String): String {
fun createDeserializeVariantForTag( fun createDeserializeVariantForTag(
tag: Tag, tag: Tag,
pretitle: String = baseClassName, pretitle: String = baseClassName,
@@ -128,19 +136,70 @@ fun createStringConverterCode(tags: List<Tag>): String {
val currentTitle = "$pretitle.${tag.title}" val currentTitle = "$pretitle.${tag.title}"
return """${indents}$currentTitle.code -> $currentTitle${if (tag.subtags.isNotEmpty()) tag.subtags.joinToString("\n", "\n") { createDeserializeVariantForTag(it, currentTitle, indents) } else ""}""" return """${indents}$currentTitle.code -> $currentTitle${if (tag.subtags.isNotEmpty()) tag.subtags.joinToString("\n", "\n") { createDeserializeVariantForTag(it, currentTitle, indents) } else ""}"""
} }
fun createInheritorVariantForTag(
return """fun String.as$baseClassName(): $baseClassName { tag: Tag,
return when (this) { pretitle: String = baseClassName,
${tags.joinToString("\n") { createDeserializeVariantForTag(it) }} indents: String = " "
else -> $baseClassName.${unknownBaseClassName}(this) ): String {
val currentTitle = "$pretitle.${tag.title}"
val subtags = if (tag.subtags.isNotEmpty()) {
tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForTag(it, currentTitle, "$indents ") }
} else {
""
}
return "${indents}$currentTitle$subtags"
} }
fun createInheritorVariantForMapForTag(
tag: Tag,
pretitle: String = baseClassName,
indents: String = " ",
codeSuffix: String = ""
): String {
val currentTitle = "$pretitle.${tag.title}"
val subtags = if (tag.subtags.isNotEmpty()) {
tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForMapForTag(it, currentTitle, "$indents ", codeSuffix) }
} else {
""
}
return "${indents}$currentTitle.code${codeSuffix} to $currentTitle$subtags"
}
return """val knownLanguageCodesMap: Map<String, $baseClassName> by lazy {
mapOf(
${tags.joinToString(",\n") { createInheritorVariantForMapForTag(it, indents = " ") }}
)
} }
val knownLanguageCodesMapByLowerCasedKeys: Map<String, $baseClassName> by lazy {
mapOf(
${tags.joinToString(",\n") { createInheritorVariantForMapForTag(it, indents = " ", codeSuffix = ".lowercase()") }}
)
}
val knownLanguageCodes: List<$baseClassName> by lazy {
knownLanguageCodesMap.values.toList()
}
fun String.as$baseClassName(): $baseClassName {
return knownLanguageCodesMap[this] ?: $baseClassName.${unknownBaseClassName}(this)
}
fun String.as${baseClassName}CaseInsensitive(): $baseClassName {
return knownLanguageCodesMapByLowerCasedKeys[this.lowercase()] ?: $baseClassName.${unknownBaseClassName}(this)
}
@Deprecated("Renamed", ReplaceWith("this.as$baseClassName()", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}as$baseClassName"))
fun String.as$oldBaseClassName(): $baseClassName = as$baseClassName()
fun convertTo$baseClassName(code: String) = code.as$baseClassName() fun convertTo$baseClassName(code: String) = code.as$baseClassName()
fun convertTo${baseClassName}CaseInsensitive(code: String) = code.as${baseClassName}CaseInsensitive()
@Deprecated("Renamed", ReplaceWith("convertTo$baseClassName(code)", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}convertTo$baseClassName"))
fun convertTo$oldBaseClassName(code: String) = convertTo$baseClassName(code)
fun $baseClassName(code: String) = code.as$baseClassName() fun $baseClassName(code: String) = code.as$baseClassName()
fun ${baseClassName}CaseInsensitive(code: String) = code.as${baseClassName}CaseInsensitive()
@Deprecated("Renamed", ReplaceWith("$baseClassName(code)", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName"))
fun $oldBaseClassName(code: String) = $baseClassName(code)
""" """
} }
fun createSerializerCode(tags: List<Tag>): String { fun createSerializerCode(tags: List<Tag>, prePackage: String): String {
return """import kotlinx.serialization.KSerializer return """import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Decoder
@@ -153,16 +212,20 @@ object $baseClassSerializerName : KSerializer<$baseClassName> {
return $baseClassName(decoder.decodeString()) return $baseClassName(decoder.decodeString())
} }
override fun serialize(encoder: Encoder, value: IetfLanguageCode) { override fun serialize(encoder: Encoder, value: $baseClassName) {
encoder.encodeString(value.code) encoder.encodeString(value.code)
} }
} }
@Deprecated("Renamed", ReplaceWith("$baseClassSerializerName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassSerializerName"))
typealias $oldBaseClassSerializerName = $baseClassSerializerName
""" """
} }
suspend fun main(vararg args: String) { suspend fun main(vararg args: String) {
val outputFolder = args.firstOrNull() ?.let { File(it) } val outputFolder = args.firstOrNull() ?.let { File(it) }
outputFolder ?.mkdirs() outputFolder ?.mkdirs()
val targetPackage = args.getOrNull(1)
val targetPackagePrefix = targetPackage ?.let { "package $it\n\n" } ?: ""
val ietfLanguageCodesLink = "https://datahub.io/core/language-codes/r/language-codes.json" val ietfLanguageCodesLink = "https://datahub.io/core/language-codes/r/language-codes.json"
val ietfLanguageCodesAdditionalTagsLink = "https://datahub.io/core/language-codes/r/ietf-language-tags.json" val ietfLanguageCodesAdditionalTagsLink = "https://datahub.io/core/language-codes/r/ietf-language-tags.json"
@@ -203,18 +266,18 @@ suspend fun main(vararg args: String) {
File(outputFolder, "LanguageCodes.kt").apply { File(outputFolder, "LanguageCodes.kt").apply {
delete() delete()
createNewFile() createNewFile()
writeText(buildKtFileContent(tags)) writeText(targetPackagePrefix + buildKtFileContent(tags, targetPackage ?: ""))
} }
File(outputFolder, "StringToLanguageCodes.kt").apply { File(outputFolder, "StringToLanguageCodes.kt").apply {
delete() delete()
createNewFile() createNewFile()
writeText(createStringConverterCode(tags)) writeText(targetPackagePrefix + createStringConverterCode(tags, targetPackage ?: ""))
} }
File(outputFolder, "$baseClassSerializerName.kt").apply { File(outputFolder, "$baseClassSerializerName.kt").apply {
delete() delete()
createNewFile() createNewFile()
writeText(createSerializerCode(tags)) writeText(targetPackagePrefix + createSerializerCode(tags, targetPackage ?: ""))
} }
} }

View File

@@ -0,0 +1,20 @@
package dev.inmo.micro_utils.language_codes
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
object IetfLangSerializer : KSerializer<IetfLang> {
override val descriptor = String.serializer().descriptor
override fun deserialize(decoder: Decoder): IetfLang {
return IetfLang(decoder.decodeString())
}
override fun serialize(encoder: Encoder, value: IetfLang) {
encoder.encodeString(value.code)
}
}
@Deprecated("Renamed", ReplaceWith("IetfLangSerializer", "dev.inmo.micro_utils.language_codes.IetfLangSerializer"))
typealias IetfLanguageCodeSerializer = IetfLangSerializer

View File

@@ -1,18 +0,0 @@
package dev.inmo.micro_utils.language_codes
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
object IetfLanguageCodeSerializer : KSerializer<IetfLanguageCode> {
override val descriptor = String.serializer().descriptor
override fun deserialize(decoder: Decoder): IetfLanguageCode {
return IetfLanguageCode(decoder.decodeString())
}
override fun serialize(encoder: Encoder, value: IetfLanguageCode) {
encoder.encodeString(value.code)
}
}

View File

@@ -0,0 +1,16 @@
package dev.inmo.micro_utils.language_codes
import kotlin.test.Test
import kotlin.test.assertEquals
class UnknownIetfLanguageTests {
@Test
fun commonTestOfParentCode() {
knownLanguageCodes.forEach {
val language = IetfLang.UnknownIetfLang(it.code)
assertEquals(it.code, language.code)
assertEquals(it.withoutDialect, language.withoutDialect)
assertEquals(it.parentLang ?.code, language.parentLang ?.code)
}
}
}

View File

@@ -2,7 +2,9 @@ package dev.inmo.micro_utils.language_codes
import java.util.Locale import java.util.Locale
fun IetfLanguageCode.toJavaLocale(): Locale = Locale.forLanguageTag(code) fun IetfLang.toJavaLocale(): Locale = Locale.forLanguageTag(code)
fun IetfLanguageCode?.toJavaLocaleOrDefault(): Locale = this ?.toJavaLocale() ?: Locale.getDefault() fun IetfLang?.toJavaLocaleOrDefault(): Locale = this?.toJavaLocale() ?: Locale.getDefault()
fun Locale.toIetfLanguageCode(): IetfLanguageCode = IetfLanguageCode(toLanguageTag()) fun Locale.toIetfLang(): IetfLang = IetfLang(toLanguageTag())
@Deprecated("Renamed", ReplaceWith("this.toIetfLang()", "dev.inmo.micro_utils.language_codes.toIetfLang"))
fun Locale.toIetfLanguageCode(): IetfLang = toIetfLang()

View File

@@ -57,18 +57,14 @@ publishing {
} }
} }
if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) { if ((project.hasProperty('INMONEXUS_USER') || System.getenv('INMONEXUS_USER') != null) && (project.hasProperty('INMONEXUS_PASSWORD') || System.getenv('INMONEXUS_PASSWORD') != null)) {
maven { maven {
name = "Gitea" name = "InmoNexus"
url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven") url = uri("https://nexus.inmo.dev/repository/maven-releases/")
credentials(HttpHeaderCredentials) { credentials {
name = "Authorization" username = project.hasProperty('INMONEXUS_USER') ? project.property('INMONEXUS_USER') : System.getenv('INMONEXUS_USER')
value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN') password = project.hasProperty('INMONEXUS_PASSWORD') ? project.property('INMONEXUS_PASSWORD') : System.getenv('INMONEXUS_PASSWORD')
}
authentication {
header(HttpHeaderAuthentication)
} }
} }
@@ -109,4 +105,21 @@ if (project.hasProperty("signing.gnupg.keyName")) {
def signingTasks = project.getTasks().withType(Sign.class) def signingTasks = project.getTasks().withType(Sign.class)
mustRunAfter(signingTasks) mustRunAfter(signingTasks)
} }
// Workaround to make test tasks use sign
project.getTasks().withType(Sign.class).configureEach { signTask ->
def withoutSign = (signTask.name.startsWith("sign") ? signTask.name.minus("sign") : signTask.name)
def pubName = withoutSign.endsWith("Publication") ? withoutSign.substring(0, withoutSign.length() - "Publication".length()) : withoutSign
// These tasks only exist for native targets, hence findByName() to avoid trying to find them for other targets
// Task ':linkDebugTest<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def debugTestTask = tasks.findByName("linkDebugTest$pubName")
if (debugTestTask != null) {
signTask.mustRunAfter(debugTestTask)
}
// Task ':compileTestKotlin<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def testTask = tasks.findByName("compileTestKotlin$pubName")
if (testTask != null) {
signTask.mustRunAfter(testTask)
}
}
} }

View File

@@ -1 +1 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"It is set of projects with micro tools for avoiding of routines coding","url":"https://github.com/InsanusMokrassar/MicroUtils/","vcsUrl":"https://github.com/InsanusMokrassar/MicroUtils.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/MicroUtils"},{"name":"Gitea","url":"https://git.inmo.dev/api/packages/InsanusMokrassar/maven","credsType":{"type":"dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository.CredentialsType.HttpHeaderCredentials","headerName":"Authorization","headerValueProperty":"GITEA_TOKEN"}},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}} {"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"It is set of projects with micro tools for avoiding of routines coding","url":"https://github.com/InsanusMokrassar/MicroUtils/","vcsUrl":"https://github.com/InsanusMokrassar/MicroUtils.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/MicroUtils"},{"name":"InmoNexus","url":"https://nexus.inmo.dev/repository/maven-releases/"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -1,14 +1,19 @@
package dev.inmo.micro_utils.repos.exposed package dev.inmo.micro_utils.repos.exposed
import dev.inmo.micro_utils.repos.CRUDRepo import dev.inmo.micro_utils.repos.CRUDRepo
import kotlinx.coroutines.channels.BufferOverflow
abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>( abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = 0, flowsChannelsSize: Int = 0,
tableName: String = "" tableName: String = "",
replyCacheInFlows: Int = 0,
onBufferOverflowBehaviour: BufferOverflow = BufferOverflow.SUSPEND
) : ) :
AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>( AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize, flowsChannelsSize,
tableName tableName,
replyCacheInFlows,
onBufferOverflowBehaviour
), ),
ExposedCRUDRepo<ObjectType, IdType>, ExposedCRUDRepo<ObjectType, IdType>,
CRUDRepo<ObjectType, IdType, InputValueType> CRUDRepo<ObjectType, IdType, InputValueType>

View File

@@ -2,6 +2,7 @@ package dev.inmo.micro_utils.repos.exposed
import dev.inmo.micro_utils.repos.UpdatedValuePair import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.micro_utils.repos.WriteCRUDRepo import dev.inmo.micro_utils.repos.WriteCRUDRepo
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.* import org.jetbrains.exposed.sql.statements.*
@@ -11,19 +12,26 @@ import java.util.Objects
abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>( abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = 0, flowsChannelsSize: Int = 0,
tableName: String = "", tableName: String = "",
replyCacheInFlows: Int = 0 replyCacheInFlows: Int = 0,
onBufferOverflowBehaviour: BufferOverflow = BufferOverflow.SUSPEND
) : ) :
AbstractExposedReadCRUDRepo<ObjectType, IdType>(tableName), AbstractExposedReadCRUDRepo<ObjectType, IdType>(tableName),
ExposedCRUDRepo<ObjectType, IdType>, ExposedCRUDRepo<ObjectType, IdType>,
WriteCRUDRepo<ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>
{ {
protected val _newObjectsFlow = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize) protected open val _newObjectsFlow = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize, onBufferOverflowBehaviour)
protected val _updatedObjectsFlow = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize) protected open val _updatedObjectsFlow = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize, onBufferOverflowBehaviour)
protected val _deletedObjectsIdsFlow = MutableSharedFlow<IdType>(replyCacheInFlows, flowsChannelsSize) protected open val _deletedObjectsIdsFlow = MutableSharedFlow<IdType>(replyCacheInFlows, flowsChannelsSize, onBufferOverflowBehaviour)
override val newObjectsFlow: Flow<ObjectType> = _newObjectsFlow.asSharedFlow() override val newObjectsFlow: Flow<ObjectType> by lazy {
override val updatedObjectsFlow: Flow<ObjectType> = _updatedObjectsFlow.asSharedFlow() _newObjectsFlow.asSharedFlow()
override val deletedObjectsIdsFlow: Flow<IdType> = _deletedObjectsIdsFlow.asSharedFlow() }
override val updatedObjectsFlow: Flow<ObjectType> by lazy {
_updatedObjectsFlow.asSharedFlow()
}
override val deletedObjectsIdsFlow: Flow<IdType> by lazy {
_deletedObjectsIdsFlow.asSharedFlow()
}
protected abstract fun InsertStatement<Number>.asObject(value: InputValueType): ObjectType protected abstract fun InsertStatement<Number>.asObject(value: InputValueType): ObjectType

21
resources/build.gradle Normal file
View File

@@ -0,0 +1,21 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":micro_utils.language_codes")
}
}
androidMain {
dependsOn(jvmMain)
}
}
}

View File

@@ -0,0 +1,20 @@
package dev.inmo.micro_utils.strings
import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
fun StringResource.translation(configuration: Configuration): String = translation(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
configuration.locales[0]
} else {
configuration.locale
}
)
fun StringResource.translation(resources: Resources): String = translation(resources.configuration)
fun StringResource.translation(context: Context): String = translation(context.resources)
fun Configuration.translation(resource: StringResource): String = resource.translation(this)
fun Resources.translation(resource: StringResource): String = configuration.translation(resource)
fun Context.translation(resource: StringResource): String = resources.translation(resource)

View File

@@ -0,0 +1,56 @@
package dev.inmo.micro_utils.strings
import dev.inmo.micro_utils.language_codes.IetfLang
/**
* Use this class as a type of your strings object fields. For example:
*
* ```kotlin
* object Strings {
* val someResource: StringResource
* }
* ```
*
* Use [buildStringResource] for useful creation of string resource
*
* @see buildStringResource
*/
class StringResource(
val default: String,
val map: Map<IetfLang, Lazy<String>>
) {
class Builder(
var default: String
) {
private val map = mutableMapOf<IetfLang, Lazy<String>>()
infix fun IetfLang.variant(value: Lazy<String>) {
map[this] = value
}
infix fun IetfLang.variant(value: () -> String) = this variant lazy(value)
infix fun IetfLang.variant(value: String) = this variant lazyOf(value)
infix fun String.variant(value: Lazy<String>) = IetfLang(this) variant value
infix fun String.variant(value: () -> String) = IetfLang(this) variant lazy(value)
infix fun String.variant(value: String) = this variant lazyOf(value)
fun build() = StringResource(default, map.toMap())
}
fun translation(languageCode: IetfLang): String {
map[languageCode] ?.let { return it.value }
return languageCode.parentLang ?.let {
map[it] ?.value
} ?: default
}
}
inline fun buildStringResource(
default: String,
builder: StringResource.Builder.() -> Unit
): StringResource {
return StringResource.Builder(default).apply(builder).build()
}

View File

@@ -0,0 +1,10 @@
package dev.inmo.micro_utils.strings
import dev.inmo.micro_utils.language_codes.toIetfLang
import java.util.Locale
fun StringResource.translation(locale: Locale = Locale.getDefault()): String {
return translation(locale.toIetfLang())
}
fun Locale.translation(resource: StringResource): String = resource.translation(this)

View File

@@ -43,6 +43,8 @@ String[] includes = [
":startup:plugin", ":startup:plugin",
":startup:launcher", ":startup:launcher",
":resources",
":fsm:common", ":fsm:common",
":fsm:repos:common", ":fsm:repos:common",