Compare commits

..

7 Commits

57 changed files with 242 additions and 540 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
.idea .idea
.vscode
.kotlin .kotlin
out/* out/*
*.iml *.iml

View File

@@ -1,103 +1,17 @@
# Changelog # Changelog
## 0.28.0 ## 0.25.8.2
## 0.27.0
* `Versions`:
* `Ktor`: `3.3.2` -> `3.3.3`
* `Okio`: `3.16.2` -> `3.16.4`
* `KSP`: `2.3.2` -> `2.3.4`
* `Compose`: `1.9.3` -> `1.10.0`
* `Compose Material3`: `1.9.0` -> `1.10.0-alpha05`
## 0.26.8
* `Versions`:
* `KSLog`: `1.5.1` -> `1.5.2`
* `Compose`: `1.9.2` -> `1.9.3`
* `Ktor`: `3.3.1` -> `3.3.2`
* `Coroutines`: * `Coroutines`:
* Add simple suspend function `suspendPoint` which will ensure that current coroutine is active to let it be * `runCatchingLogging` updated to rethrow `CancellationException` and log other exceptions
destroyable even in case it have non-suspendable nature
## 0.26.7 ## 0.25.8.1
* `Versions`:
* `Kotlin`: `2.2.20` -> `2.2.21`
* `Compose`: `1.8.2` -> `1.9.2`
* `KSP`: `2.2.20-2.0.3` -> `2.3.1`
* `Coroutines`: * `Coroutines`:
* Fix `SmartSemaphore.waitRelease` to wait for the exact number of permits * New function `suspendPoint` to check coroutine cancellation status
* Improve `SmartKeyRWLocker` tests * `SpecialMutableStateFlow` renamed to `MutableRedeliverStateFlow` (old name deprecated with `ReplaceWith`)
* `KSP`: * `SmartSemaphore`:
* `Sealed`/`ClassCasts`/`Variations`: * Fix of `waitRelease` call to pass correct number of permits
* Add workaround for `NoSuchElementException` to improve processors stability on new `KSP`
* `Koin`:
* `Generator`:
* Handle missing annotation values safely (`NoSuchElementException` workaround)
* `Android`:
* `Pickers`:
* Add dependency `androidx.compose.material:material-icons-extended`
## 0.26.6
* `Versions`:
* `Ktor`: `3.3.0` -> `3.3.1`
* `Okio`: `3.16.0` -> `3.16.2`
## 0.26.5
* `Versions`:
* `Kotlin`: `2.2.10` -> `2.2.20`
* `KSLog`: `1.5.0` -> `1.5.1`
* `Ktor`: `3.2.3` -> `3.3.0`
* `KotlinX Browser`: `0.3` -> `0.5.0`
* `Koin`: `4.1.0` -> `4.1.1`
## 0.26.4
* `Common`:
* Add expect/actual `MPPFilePathSeparator`
* Fix `FileName` realization to take care about system file path separator
## 0.26.3
* `Versions`:
* `Kotlin`: `2.2.0` -> `2.2.10`
* `KSP`: `2.2.0-2.0.2` -> `2.2.10-2.0.2`
* `Android CoreKTX`: `1.16.0` -> `1.17.0`
* `Android Fragment`: `1.8.8` -> `1.8.9`
## 0.26.2
* `Versions`:
* `Ktor`: `3.2.2` -> `3.2.3`
* `Okio`: `3.15.0` -> `3.16.0`
* `Coroutines`:
* Rename `SpecialMutableStateFlow` to `MutableRedeliverStateFlow`
## 0.26.1
* `Versions`:
* `Compose`: `1.8.1` -> `1.8.2`
* `Ktor`: `3.2.1` -> `3.2.2`
* `Coroutines`:
* Add opportunity to pass logger in subscribe async
## 0.26.0
**WARNING!!! SINCE THIS VERSION IF YOU WANT TO USE SOME OF KSP MODULES, SET `ksp.useKSP2=false` IN YOUR `gradle.properties`** (see [gh issue 2491](https://github.com/google/ksp/issues/2491))
* `Versions`:
* `Kotlin`: `2.1.21` -> `2.2.0`
* `Serialization`: `1.8.1` -> `1.9.0`
* `KSLog`: `1.4.2` -> `1.5.0`
* `Ktor`: `3.1.3` -> `3.2.1`
* `Koin`: `4.0.4` -> `4.1.0`
* `Okio`: `3.12.0` -> `3.15.0`
* `KSP`: `2.1.20-1.0.31` -> `2.2.0-2.0.2`
* `kotlin-poet`: `1.18.1` -> `2.2.0`
## 0.25.8 ## 0.25.8

View File

@@ -13,7 +13,6 @@ kotlin {
androidMain { androidMain {
dependencies { dependencies {
api project(":micro_utils.android.smalltextfield") api project(":micro_utils.android.smalltextfield")
api libs.jb.compose.icons
} }
} }
} }

View File

@@ -28,7 +28,7 @@ if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != n
centralPortal { centralPortal {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
validationTimeout = Duration.ofHours(4) verificationTimeout = Duration.ofHours(4)
publishingType = System.getenv('PUBLISHING_TYPE') != "" ? System.getenv('PUBLISHING_TYPE') : "USER_MANAGED" publishingType = System.getenv('PUBLISHING_TYPE') != "" ? System.getenv('PUBLISHING_TYPE') : "USER_MANAGED"
} }
@@ -44,11 +44,6 @@ allprojects {
maven { url "https://nexus.inmo.dev/repository/maven-releases/" } maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
mavenLocal() mavenLocal()
} }
it.tasks.withType(AbstractTestTask).configureEach {
it.failOnNoDiscoveredTests = false
}
} }
apply from: "./extensions.gradle" apply from: "./extensions.gradle"

View File

@@ -3,8 +3,10 @@ import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.runComposeUiTest import androidx.compose.ui.test.runComposeUiTest
import dev.inmo.micro_utils.common.compose.LoadableComponent import dev.inmo.micro_utils.common.compose.LoadableComponent
import dev.inmo.micro_utils.coroutines.MutableRedeliverStateFlow import dev.inmo.micro_utils.coroutines.MutableRedeliverStateFlow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.TestOnly
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertTrue import kotlin.test.assertTrue

View File

@@ -7,7 +7,7 @@ import kotlin.jvm.JvmInline
@JvmInline @JvmInline
value class FileName(val string: String) { value class FileName(val string: String) {
val name: String val name: String
get() = withoutSlashAtTheEnd.takeLastWhile { it != MPPFilePathSeparator } get() = withoutSlashAtTheEnd.takeLastWhile { it != '/' }
val extension: String val extension: String
get() = name.takeLastWhile { it != '.' } get() = name.takeLastWhile { it != '.' }
val nameWithoutExtension: String val nameWithoutExtension: String
@@ -18,7 +18,7 @@ value class FileName(val string: String) {
} ?: filename } ?: filename
} }
val withoutSlashAtTheEnd: String val withoutSlashAtTheEnd: String
get() = string.dropLastWhile { it == MPPFilePathSeparator } get() = string.dropLastWhile { it == '/' }
override fun toString(): String = string override fun toString(): String = string
} }
@@ -26,7 +26,6 @@ value class FileName(val string: String) {
expect class MPPFile expect class MPPFile
expect val MPPFile.filename: FileName expect val MPPFile.filename: FileName
expect val MPPFilePathSeparator: Char
expect val MPPFile.filesize: Long expect val MPPFile.filesize: Long
expect val MPPFile.bytesAllocatorSync: ByteArrayAllocator expect val MPPFile.bytesAllocatorSync: ByteArrayAllocator
expect val MPPFile.bytesAllocator: SuspendByteArrayAllocator expect val MPPFile.bytesAllocator: SuspendByteArrayAllocator

View File

@@ -35,10 +35,6 @@ private suspend fun MPPFile.dirtyReadBytes(): ByteArray = readBytesPromise().awa
*/ */
actual val MPPFile.filename: FileName actual val MPPFile.filename: FileName
get() = FileName(name) get() = FileName(name)
actual val MPPFilePathSeparator: Char
get() = '/'
/** /**
* @suppress * @suppress
*/ */

View File

@@ -14,10 +14,6 @@ actual typealias MPPFile = File
*/ */
actual val MPPFile.filename: FileName actual val MPPFile.filename: FileName
get() = FileName(name) get() = FileName(name)
actual val MPPFilePathSeparator: Char
get() = File.separatorChar
/** /**
* @suppress * @suppress
*/ */

View File

@@ -11,10 +11,6 @@ actual typealias MPPFile = Path
*/ */
actual val MPPFile.filename: FileName actual val MPPFile.filename: FileName
get() = FileName(toString()) get() = FileName(toString())
actual val MPPFilePathSeparator: Char = Path.DIRECTORY_SEPARATOR.first()
/** /**
* @suppress * @suppress
*/ */

View File

@@ -37,10 +37,6 @@ private suspend fun MPPFile.dirtyReadBytes(): ByteArray = readBytesPromise().awa
*/ */
actual val MPPFile.filename: FileName actual val MPPFile.filename: FileName
get() = FileName(name) get() = FileName(name)
actual val MPPFilePathSeparator: Char
get() = '/'
/** /**
* @suppress * @suppress
*/ */

View File

@@ -1,6 +1,5 @@
package dev.inmo.micro_utils.coroutines package dev.inmo.micro_utils.coroutines
import dev.inmo.kslog.common.KSLog
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.consumeAsFlow
@@ -8,15 +7,13 @@ import kotlinx.coroutines.flow.consumeAsFlow
fun <T> CoroutineScope.actorAsync( fun <T> CoroutineScope.actorAsync(
channelCapacity: Int = Channel.UNLIMITED, channelCapacity: Int = Channel.UNLIMITED,
markerFactory: suspend (T) -> Any? = { null }, markerFactory: suspend (T) -> Any? = { null },
logger: KSLog = KSLog,
block: suspend (T) -> Unit block: suspend (T) -> Unit
): Channel<T> { ): Channel<T> {
val channel = Channel<T>(channelCapacity) val channel = Channel<T>(channelCapacity)
channel.consumeAsFlow().subscribeAsync(this, markerFactory, logger, block) channel.consumeAsFlow().subscribeAsync(this, markerFactory, block)
return channel return channel
} }
@Deprecated("Use standard actosAsync instead", ReplaceWith("actorAsync(channelCapacity, markerFactory, block = block)", "dev.inmo.micro_utils.coroutines.actorAsync"))
inline fun <T> CoroutineScope.safeActorAsync( inline fun <T> CoroutineScope.safeActorAsync(
channelCapacity: Int = Channel.UNLIMITED, channelCapacity: Int = Channel.UNLIMITED,
noinline onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler, noinline onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,

View File

@@ -1,6 +1,5 @@
package dev.inmo.micro_utils.coroutines package dev.inmo.micro_utils.coroutines
import dev.inmo.kslog.common.KSLog
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.* import kotlinx.coroutines.channels.*
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
@@ -9,7 +8,6 @@ import kotlinx.coroutines.sync.withLock
private class SubscribeAsyncReceiver<T>( private class SubscribeAsyncReceiver<T>(
val scope: CoroutineScope, val scope: CoroutineScope,
val logger: KSLog,
output: suspend SubscribeAsyncReceiver<T>.(T) -> Unit output: suspend SubscribeAsyncReceiver<T>.(T) -> Unit
) { ) {
private val dataChannel: Channel<T> = Channel(Channel.UNLIMITED) private val dataChannel: Channel<T> = Channel(Channel.UNLIMITED)
@@ -17,7 +15,7 @@ private class SubscribeAsyncReceiver<T>(
get() = dataChannel get() = dataChannel
init { init {
scope.launchLoggingDropExceptions(logger = logger) { scope.launchLoggingDropExceptions {
for (data in dataChannel) { for (data in dataChannel) {
output(data) output(data)
} }
@@ -35,16 +33,13 @@ private data class AsyncSubscriptionCommandData<T, M>(
val scope: CoroutineScope, val scope: CoroutineScope,
val markerFactory: suspend (T) -> M, val markerFactory: suspend (T) -> M,
val block: suspend (T) -> Unit, val block: suspend (T) -> Unit,
val logger: KSLog,
val onEmpty: suspend (M) -> Unit val onEmpty: suspend (M) -> Unit
) : AsyncSubscriptionCommand<T, M> { ) : AsyncSubscriptionCommand<T, M> {
override suspend fun invoke(markersMap: MutableMap<M, SubscribeAsyncReceiver<T>>) { override suspend fun invoke(markersMap: MutableMap<M, SubscribeAsyncReceiver<T>>) {
val marker = markerFactory(data) val marker = markerFactory(data)
markersMap.getOrPut(marker) { markersMap.getOrPut(marker) {
SubscribeAsyncReceiver(scope.LinkedSupervisorScope(), logger) { SubscribeAsyncReceiver(scope.LinkedSupervisorScope()) {
runCatchingLogging(logger = logger) { safelyWithoutExceptions { block(it) }
block(it)
}
if (isEmpty()) { if (isEmpty()) {
onEmpty(marker) onEmpty(marker)
} }
@@ -68,7 +63,6 @@ private data class AsyncSubscriptionCommandClearReceiver<T, M>(
fun <T, M> Flow<T>.subscribeAsync( fun <T, M> Flow<T>.subscribeAsync(
scope: CoroutineScope, scope: CoroutineScope,
markerFactory: suspend (T) -> M, markerFactory: suspend (T) -> M,
logger: KSLog = KSLog,
block: suspend (T) -> Unit block: suspend (T) -> Unit
): Job { ): Job {
val subscope = scope.LinkedSupervisorScope() val subscope = scope.LinkedSupervisorScope()
@@ -77,14 +71,8 @@ fun <T, M> Flow<T>.subscribeAsync(
it.invoke(markersMap) it.invoke(markersMap)
} }
val job = subscribeLoggingDropExceptions(subscope, logger = logger) { data -> val job = subscribeLoggingDropExceptions(subscope) { data ->
val dataCommand = AsyncSubscriptionCommandData( val dataCommand = AsyncSubscriptionCommandData(data, subscope, markerFactory, block) { marker ->
data = data,
scope = subscope,
markerFactory = markerFactory,
block = block,
logger = logger
) { marker ->
actor.send( actor.send(
AsyncSubscriptionCommandClearReceiver(marker) AsyncSubscriptionCommandClearReceiver(marker)
) )
@@ -97,20 +85,17 @@ fun <T, M> Flow<T>.subscribeAsync(
return job return job
} }
@Deprecated("Renamed", ReplaceWith("subscribeLoggingDropExceptionsAsync(scope, markerFactory, block = block)", "dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptionsAsync"))
fun <T, M> Flow<T>.subscribeSafelyAsync( fun <T, M> Flow<T>.subscribeSafelyAsync(
scope: CoroutineScope, scope: CoroutineScope,
markerFactory: suspend (T) -> M, markerFactory: suspend (T) -> M,
onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler, onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,
logger: KSLog = KSLog,
block: suspend (T) -> Unit block: suspend (T) -> Unit
) = subscribeAsync(scope, markerFactory, logger) { ) = subscribeAsync(scope, markerFactory) {
safely(onException) { safely(onException) {
block(it) block(it)
} }
} }
@Deprecated("Renamed", ReplaceWith("subscribeLoggingDropExceptionsAsync(scope, markerFactory, block = block)", "dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptionsAsync"))
fun <T, M> Flow<T>.subscribeSafelyWithoutExceptionsAsync( fun <T, M> Flow<T>.subscribeSafelyWithoutExceptionsAsync(
scope: CoroutineScope, scope: CoroutineScope,
markerFactory: suspend (T) -> M, markerFactory: suspend (T) -> M,
@@ -122,22 +107,11 @@ fun <T, M> Flow<T>.subscribeSafelyWithoutExceptionsAsync(
} }
} }
fun <T, M> Flow<T>.subscribeLoggingDropExceptionsAsync(
scope: CoroutineScope,
markerFactory: suspend (T) -> M,
logger: KSLog = KSLog,
block: suspend (T) -> Unit
) = subscribeAsync(scope, markerFactory, logger) {
block(it)
}
@Deprecated("Renamed", ReplaceWith("subscribeLoggingDropExceptionsAsync(scope, markerFactory, logger, block = block)", "dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptionsAsync"))
fun <T, M> Flow<T>.subscribeSafelySkippingExceptionsAsync( fun <T, M> Flow<T>.subscribeSafelySkippingExceptionsAsync(
scope: CoroutineScope, scope: CoroutineScope,
markerFactory: suspend (T) -> M, markerFactory: suspend (T) -> M,
logger: KSLog = KSLog,
block: suspend (T) -> Unit block: suspend (T) -> Unit
) = subscribeAsync(scope, markerFactory, logger) { ) = subscribeAsync(scope, markerFactory) {
safelyWithoutExceptions({ /* do nothing */}) { safelyWithoutExceptions({ /* do nothing */}) {
block(it) block(it)
} }

View File

@@ -65,6 +65,3 @@ open class MutableRedeliverStateFlow<T>(
override suspend fun collect(collector: FlowCollector<T>) = sharingFlow.collect(collector) override suspend fun collect(collector: FlowCollector<T>) = sharingFlow.collect(collector)
} }
@Deprecated("Renamed to MutableRedeliverStateFlow", ReplaceWith("MutableRedeliverStateFlow<T>"))
typealias SpecialMutableStateFlow<T> = MutableRedeliverStateFlow<T>

View File

@@ -2,11 +2,27 @@ package dev.inmo.micro_utils.coroutines
import dev.inmo.kslog.common.KSLog import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.e import dev.inmo.kslog.common.e
import kotlin.coroutines.cancellation.CancellationException
/**
* Executes the given [block] within a `runCatching` context and logs any exceptions that occur, excluding
* `CancellationException` which is rethrown. This method simplifies error handling by automatically logging
* the errors using the provided [logger].
*
* @param T The result type of the [block].
* @param R The receiver type on which this function operates.
* @param errorMessageBuilder A lambda to build the error log message. By default, it returns a generic error message.
* @param logger The logging instance used for logging errors. Defaults to [KSLog].
* @param block The code block to execute within the `runCatching` context.
* @return A [Result] representing the outcome of executing the [block].
*/
inline fun <T, R> R.runCatchingLogging( inline fun <T, R> R.runCatchingLogging(
noinline errorMessageBuilder: R.(Throwable) -> Any = { "Something web wrong" }, noinline errorMessageBuilder: R.(Throwable) -> Any = { "Something web wrong" },
logger: KSLog = KSLog, logger: KSLog = KSLog,
block: R.() -> T block: R.() -> T
) = runCatching(block).onFailure { ) = runCatching(block).onFailure {
logger.e(it) { errorMessageBuilder(it) } when (it) {
is CancellationException -> throw it
else -> logger.e(it) { errorMessageBuilder(it) }
}
} }

View File

@@ -12,4 +12,4 @@ import kotlinx.coroutines.ensureActive
* *
* @throws kotlinx.coroutines.CancellationException if the coroutine context is no longer active. * @throws kotlinx.coroutines.CancellationException if the coroutine context is no longer active.
*/ */
suspend fun suspendPoint() = currentCoroutineContext().ensureActive() suspend fun suspendPoint() = currentCoroutineContext().ensureActive()

View File

@@ -1,31 +1,33 @@
import dev.inmo.micro_utils.coroutines.MutableRedeliverStateFlow import dev.inmo.micro_utils.coroutines.MutableRedeliverStateFlow
import dev.inmo.micro_utils.coroutines.asDeferred
import dev.inmo.micro_utils.coroutines.subscribe import dev.inmo.micro_utils.coroutines.subscribe
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue
class SpecialMutableStateFlowTests { class MutableRedeliverStateFlowTests {
@Test @Test
fun simpleTest() = runTest { fun simpleTest() = runTest {
val mutableRedeliverStateFlow = MutableRedeliverStateFlow(0) val specialMutableStateFlow = MutableRedeliverStateFlow(0)
mutableRedeliverStateFlow.value = 1 specialMutableStateFlow.value = 1
mutableRedeliverStateFlow.first { it == 1 } specialMutableStateFlow.first { it == 1 }
assertEquals(1, mutableRedeliverStateFlow.value) assertEquals(1, specialMutableStateFlow.value)
} }
@Test @Test
fun specialTest() = runTest { fun specialTest() = runTest {
val mutableRedeliverStateFlow = MutableRedeliverStateFlow(0) val specialMutableStateFlow = MutableRedeliverStateFlow(0)
lateinit var subscriberJob: Job lateinit var subscriberJob: Job
subscriberJob = mutableRedeliverStateFlow.subscribe(this) { subscriberJob = specialMutableStateFlow.subscribe(this) {
when (it) { when (it) {
1 -> mutableRedeliverStateFlow.value = 2 1 -> specialMutableStateFlow.value = 2
2 -> subscriberJob.cancel() 2 -> subscriberJob.cancel()
} }
} }
mutableRedeliverStateFlow.value = 1 specialMutableStateFlow.value = 1
subscriberJob.join() subscriberJob.join()
assertEquals(2, mutableRedeliverStateFlow.value) assertEquals(2, specialMutableStateFlow.value)
} }
} }

View File

@@ -26,7 +26,7 @@ kotlin {
project.parent.subprojects.forEach { project.parent.subprojects.forEach {
if ( if (
it.name != project.name it != project
&& it.hasProperty("kotlin") && it.hasProperty("kotlin")
&& it.kotlin.sourceSets.any { it.name.contains("commonMain") } && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
&& it.kotlin.sourceSets.any { it.name.contains("jsMain") } && it.kotlin.sourceSets.any { it.name.contains("jsMain") }
@@ -44,7 +44,7 @@ kotlin {
project.parent.subprojects.forEach { project.parent.subprojects.forEach {
if ( if (
it.name != project.name it != project
&& it.hasProperty("kotlin") && it.hasProperty("kotlin")
&& it.kotlin.sourceSets.any { it.name.contains("commonMain") } && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
&& it.kotlin.sourceSets.any { it.name.contains("jsMain") } && it.kotlin.sourceSets.any { it.name.contains("jsMain") }
@@ -60,7 +60,7 @@ kotlin {
project.parent.subprojects.forEach { project.parent.subprojects.forEach {
if ( if (
it.name != project.name it != project
&& it.hasProperty("kotlin") && it.hasProperty("kotlin")
&& it.kotlin.sourceSets.any { it.name.contains("commonMain") } && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
&& it.kotlin.sourceSets.any { it.name.contains("jvmMain") } && it.kotlin.sourceSets.any { it.name.contains("jvmMain") }
@@ -76,7 +76,7 @@ kotlin {
project.parent.subprojects.forEach { project.parent.subprojects.forEach {
if ( if (
it.name != project.name it != project
&& it.hasProperty("kotlin") && it.hasProperty("kotlin")
&& it.kotlin.sourceSets.any { it.name.contains("commonMain") } && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
&& it.kotlin.sourceSets.any { it.name.contains("androidMain") } && it.kotlin.sourceSets.any { it.name.contains("androidMain") }

View File

@@ -1,15 +1,9 @@
interface InjectedExecOps {
@Inject //@javax.inject.Inject
ExecOperations getExecOps()
}
private String getCurrentVersionChangelog() { private String getCurrentVersionChangelog() {
OutputStream changelogDataOS = new ByteArrayOutputStream() OutputStream changelogDataOS = new ByteArrayOutputStream()
exec {
def injected = project.objects.newInstance(InjectedExecOps)
injected.execOps.exec {
commandLine 'chmod', "+x", './changelog_parser.sh' commandLine 'chmod', "+x", './changelog_parser.sh'
} }
injected.execOps.exec { exec {
standardOutput = changelogDataOS standardOutput = changelogDataOS
commandLine './changelog_parser.sh', "${project.version}", 'CHANGELOG.md' commandLine './changelog_parser.sh', "${project.version}", 'CHANGELOG.md'
} }

View File

@@ -8,9 +8,6 @@ android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=2g org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=2g
## https://github.com/google/ksp/issues/2491
#ksp.useKSP2=false
# JS NPM # JS NPM
crypto_js_version=4.1.1 crypto_js_version=4.1.1
@@ -18,5 +15,5 @@ crypto_js_version=4.1.1
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.28.0 version=0.25.8.2
android_code_version=309 android_code_version=298

View File

@@ -1,48 +1,45 @@
[versions] [versions]
kt = "2.3.0" kt = "2.1.21"
kt-serialization = "1.10.0" kt-serialization = "1.8.1"
kt-coroutines = "1.10.2" kt-coroutines = "1.10.2"
kotlinx-browser = "0.5.0" kotlinx-browser = "0.3"
kslog = "1.5.2" kslog = "1.4.2"
jb-compose = "1.10.0" jb-compose = "1.8.1"
jb-compose-material3 = "1.10.0-alpha05" jb-exposed = "0.61.0"
jb-compose-icons = "1.7.8" jb-dokka = "2.0.0"
jb-exposed = "1.0.0"
jb-dokka = "2.1.0"
# 3.51.0.0 contains bug, checking with ./gradlew :micro_utils.repos.exposed:jvmTest
sqlite = "3.50.1.0" sqlite = "3.50.1.0"
korlibs = "5.4.0" korlibs = "5.4.0"
uuid = "0.8.4" uuid = "0.8.4"
ktor = "3.4.0" ktor = "3.1.3"
gh-release = "2.5.2" gh-release = "2.5.2"
koin = "4.1.1" koin = "4.0.4"
okio = "3.16.4" okio = "3.12.0"
ksp = "2.3.4" ksp = "2.1.20-1.0.31"
kotlin-poet = "2.2.0" kotlin-poet = "1.18.1"
versions = "0.53.0" versions = "0.51.0"
nmcp = "1.2.1" nmcp = "0.1.5"
android-gradle = "8.12.+" android-gradle = "8.9.+"
dexcount = "4.0.0" dexcount = "4.0.0"
android-coreKtx = "1.17.0" android-coreKtx = "1.16.0"
android-recyclerView = "1.4.0" android-recyclerView = "1.4.0"
android-appCompat = "1.7.1" android-appCompat = "1.7.1"
android-fragment = "1.8.9" android-fragment = "1.8.8"
android-espresso = "3.7.0" android-espresso = "3.6.1"
android-test = "1.3.0" android-test = "1.2.1"
android-props-minSdk = "21" android-props-minSdk = "21"
android-props-compileSdk = "36" android-props-compileSdk = "36"
@@ -91,8 +88,7 @@ jb-exposed = { module = "org.jetbrains.exposed:exposed-core", version.ref = "jb-
jb-exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", 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" } sqlite = { module = "org.xerial:sqlite-jdbc", version.ref = "sqlite" }
jb-compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "jb-compose-material3" } jb-compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "jb-compose" }
jb-compose-icons = { module = "androidx.compose.material:material-icons-extended", version.ref = "jb-compose-icons" }
android-coreKtx = { module = "androidx.core:core-ktx", version.ref = "android-coreKtx" } android-coreKtx = { module = "androidx.core:core-ktx", version.ref = "android-coreKtx" }
android-recyclerView = { module = "androidx.recyclerview:recyclerview", version.ref = "android-recyclerView" } android-recyclerView = { module = "androidx.recyclerview:recyclerview", version.ref = "android-recyclerView" }

View File

@@ -1,9 +1,6 @@
kotlin { kotlin {
androidTarget { androidTarget {
publishLibraryVariants( publishAllLibraryVariants()
"release",
"debug",
)
compilations.all { compilations.all {
kotlinOptions { kotlinOptions {
jvmTarget = "17" jvmTarget = "17"

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -10,7 +10,6 @@ repositories {
dependencies { dependencies {
api project(":micro_utils.koin") api project(":micro_utils.koin")
api project(":micro_utils.ksp.generator")
api libs.kotlin.poet api libs.kotlin.poet
api libs.ksp api libs.ksp
} }

View File

@@ -26,8 +26,6 @@ import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.ksp.toClassName import com.squareup.kotlinpoet.ksp.toClassName
import com.squareup.kotlinpoet.ksp.toTypeName import com.squareup.kotlinpoet.ksp.toTypeName
import com.squareup.kotlinpoet.ksp.writeTo import com.squareup.kotlinpoet.ksp.writeTo
import dev.inmo.micro_ksp.generator.safeClassName
import dev.inmo.micro_ksp.generator.withNoSuchElementWorkaround
import dev.inmo.micro_utils.koin.annotations.GenerateGenericKoinDefinition import dev.inmo.micro_utils.koin.annotations.GenerateGenericKoinDefinition
import dev.inmo.micro_utils.koin.annotations.GenerateKoinDefinition import dev.inmo.micro_utils.koin.annotations.GenerateKoinDefinition
import org.koin.core.Koin import org.koin.core.Koin
@@ -239,16 +237,19 @@ class Processor(
""".trimIndent() """.trimIndent()
) )
ksFile.getAnnotationsByType(GenerateKoinDefinition::class).forEach { ksFile.getAnnotationsByType(GenerateKoinDefinition::class).forEach {
val type = safeClassName { it.type } val type = runCatching {
it.type.asTypeName()
}.getOrElse { e ->
if (e is KSTypeNotPresentException) {
e.ksType.toClassName()
} else {
throw e
}
}
val targetType = runCatching { val targetType = runCatching {
type.parameterizedBy( type.parameterizedBy(*(it.typeArgs.takeIf { it.isNotEmpty() } ?.map { it.asTypeName() } ?.toTypedArray() ?: return@runCatching type))
*withNoSuchElementWorkaround(emptyArray()) {
it.typeArgs.takeIf { it.isNotEmpty() } ?.map { it.asTypeName() } ?.toTypedArray() ?: return@runCatching type
}
)
}.getOrElse { e -> }.getOrElse { e ->
when (e) { when (e) {
is IllegalArgumentException if (e.message ?.contains("no type argument") == true) -> return@getOrElse type
is KSTypeNotPresentException -> e.ksType.toClassName() is KSTypeNotPresentException -> e.ksType.toClassName()
} }
if (e is KSTypesNotPresentException) { if (e is KSTypesNotPresentException) {
@@ -257,32 +258,14 @@ class Processor(
throw e throw e
} }
}.copy( }.copy(
nullable = withNoSuchElementWorkaround(true) { it.nullable } nullable = it.nullable
) )
addCodeForType( addCodeForType(targetType, it.name, it.nullable, it.generateSingle, it.generateFactory)
targetType,
it.name,
withNoSuchElementWorkaround(true) {
it.nullable
},
withNoSuchElementWorkaround(true) {
it.generateSingle
},
withNoSuchElementWorkaround(true) {
it.generateFactory
}
)
} }
ksFile.getAnnotationsByType(GenerateGenericKoinDefinition::class).forEach { ksFile.getAnnotationsByType(GenerateGenericKoinDefinition::class).forEach {
val targetType = TypeVariableName("T", Any::class) val targetType = TypeVariableName("T", Any::class)
addCodeForType( addCodeForType(targetType, it.name, it.nullable, it.generateSingle, it.generateFactory)
targetType = targetType,
name = it.name,
nullable = withNoSuchElementWorkaround(true) { it.nullable },
generateSingle = withNoSuchElementWorkaround(true) { it.generateSingle },
generateFactory = withNoSuchElementWorkaround(true) { it.generateFactory }
)
} }
}.build().let { }.build().let {
File( File(

View File

@@ -5,6 +5,7 @@ package dev.inmo.micro_utils.koin.generator.test
import kotlin.Any import kotlin.Any
import kotlin.Boolean import kotlin.Boolean
import kotlin.Deprecated
import kotlin.String import kotlin.String
import org.koin.core.Koin import org.koin.core.Koin
import org.koin.core.definition.Definition import org.koin.core.definition.Definition
@@ -29,59 +30,95 @@ public val Koin.sampleInfo: Test<String>
/** /**
* @return Definition by key "sampleInfo" with [parameters] * @return Definition by key "sampleInfo" with [parameters]
*/ */
public inline fun Scope.sampleInfo(noinline parameters: ParametersDefinition): Test<String> = get(named("sampleInfo"), parameters) public inline fun Scope.sampleInfo(noinline parameters: ParametersDefinition): Test<String> =
get(named("sampleInfo"), parameters)
/** /**
* @return Definition by key "sampleInfo" with [parameters] * @return Definition by key "sampleInfo" with [parameters]
*/ */
public inline fun Koin.sampleInfo(noinline parameters: ParametersDefinition): Test<String> = get(named("sampleInfo"), parameters) public inline fun Koin.sampleInfo(noinline parameters: ParametersDefinition): Test<String> =
get(named("sampleInfo"), parameters)
/** /**
* Will register [definition] with [org.koin.core.module.Module.single] and key "sampleInfo" * Will register [definition] with [org.koin.core.module.Module.single] and key "sampleInfo"
*/ */
public fun Module.singleSampleInfo(createdAtStart: Boolean = false, definition: Definition<Test<String>>): KoinDefinition<Test<String>> = single(named("sampleInfo"), createdAtStart = createdAtStart, definition = definition) @Deprecated(
"This definition is old style and should not be used anymore. Use singleSampleInfo instead",
ReplaceWith("singleSampleInfo"),
)
public fun Module.sampleInfoSingle(createdAtStart: Boolean = false,
definition: Definition<Test<String>>): KoinDefinition<Test<String>> =
single(named("sampleInfo"), createdAtStart = createdAtStart, definition = definition)
/**
* Will register [definition] with [org.koin.core.module.Module.single] and key "sampleInfo"
*/
public fun Module.singleSampleInfo(createdAtStart: Boolean = false,
definition: Definition<Test<String>>): KoinDefinition<Test<String>> =
single(named("sampleInfo"), createdAtStart = createdAtStart, definition = definition)
/** /**
* Will register [definition] with [org.koin.core.module.Module.factory] and key "sampleInfo" * Will register [definition] with [org.koin.core.module.Module.factory] and key "sampleInfo"
*/ */
public fun Module.factorySampleInfo(definition: Definition<Test<String>>): KoinDefinition<Test<String>> = factory(named("sampleInfo"), definition = definition) @Deprecated(
"This definition is old style and should not be used anymore. Use factorySampleInfo instead",
ReplaceWith("factorySampleInfo"),
)
public fun Module.sampleInfoFactory(definition: Definition<Test<String>>):
KoinDefinition<Test<String>> = factory(named("sampleInfo"), definition = definition)
/**
* Will register [definition] with [org.koin.core.module.Module.factory] and key "sampleInfo"
*/
public fun Module.factorySampleInfo(definition: Definition<Test<String>>):
KoinDefinition<Test<String>> = factory(named("sampleInfo"), definition = definition)
/** /**
* @return Definition by key "test" with [parameters] * @return Definition by key "test" with [parameters]
*/ */
public inline fun <reified T : Any> Scope.test(noinline parameters: ParametersDefinition? = null): T = get(named("test"), parameters) public inline fun <reified T : Any> Scope.test(noinline parameters: ParametersDefinition? = null): T
= get(named("test"), parameters)
/** /**
* @return Definition by key "test" with [parameters] * @return Definition by key "test" with [parameters]
*/ */
public inline fun <reified T : Any> Koin.test(noinline parameters: ParametersDefinition? = null): T = get(named("test"), parameters) public inline fun <reified T : Any> Koin.test(noinline parameters: ParametersDefinition? = null): T
= get(named("test"), parameters)
/** /**
* Will register [definition] with [org.koin.core.module.Module.single] and key "test" * Will register [definition] with [org.koin.core.module.Module.single] and key "test"
*/ */
public inline fun <reified T : Any> Module.singleTest(createdAtStart: Boolean = false, noinline definition: Definition<T>): KoinDefinition<T> = single(named("test"), createdAtStart = createdAtStart, definition = definition) public inline fun <reified T : Any> Module.singleTest(createdAtStart: Boolean = false, noinline
definition: Definition<T>): KoinDefinition<T> = single(named("test"), createdAtStart =
createdAtStart, definition = definition)
/** /**
* Will register [definition] with [org.koin.core.module.Module.factory] and key "test" * Will register [definition] with [org.koin.core.module.Module.factory] and key "test"
*/ */
public inline fun <reified T : Any> Module.factoryTest(noinline definition: Definition<T>): KoinDefinition<T> = factory(named("test"), definition = definition) public inline fun <reified T : Any> Module.factoryTest(noinline definition: Definition<T>):
KoinDefinition<T> = factory(named("test"), definition = definition)
/** /**
* @return Definition by key "testNullable" with [parameters] * @return Definition by key "testNullable" with [parameters]
*/ */
public inline fun <reified T : Any> Scope.testNullable(noinline parameters: ParametersDefinition? = null): T? = getOrNull(named("testNullable"), parameters) public inline fun <reified T : Any> Scope.testNullable(noinline parameters: ParametersDefinition? =
null): T? = getOrNull(named("testNullable"), parameters)
/** /**
* @return Definition by key "testNullable" with [parameters] * @return Definition by key "testNullable" with [parameters]
*/ */
public inline fun <reified T : Any> Koin.testNullable(noinline parameters: ParametersDefinition? = null): T? = getOrNull(named("testNullable"), parameters) public inline fun <reified T : Any> Koin.testNullable(noinline parameters: ParametersDefinition? =
null): T? = getOrNull(named("testNullable"), parameters)
/** /**
* Will register [definition] with [org.koin.core.module.Module.single] and key "testNullable" * Will register [definition] with [org.koin.core.module.Module.single] and key "testNullable"
*/ */
public inline fun <reified T : Any> Module.singleTestNullable(createdAtStart: Boolean = false, noinline definition: Definition<T>): KoinDefinition<T> = single(named("testNullable"), createdAtStart = createdAtStart, definition = definition) public inline fun <reified T : Any> Module.singleTestNullable(createdAtStart: Boolean = false,
noinline definition: Definition<T>): KoinDefinition<T> = single(named("testNullable"),
createdAtStart = createdAtStart, definition = definition)
/** /**
* Will register [definition] with [org.koin.core.module.Module.factory] and key "testNullable" * Will register [definition] with [org.koin.core.module.Module.factory] and key "testNullable"
*/ */
public inline fun <reified T : Any> Module.factoryTestNullable(noinline definition: Definition<T>): KoinDefinition<T> = factory(named("testNullable"), definition = definition) public inline fun <reified T : Any> Module.factoryTestNullable(noinline definition: Definition<T>):
KoinDefinition<T> = factory(named("testNullable"), definition = definition)

View File

@@ -10,7 +10,6 @@ import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.* import com.google.devtools.ksp.symbol.*
import com.squareup.kotlinpoet.* import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ksp.toClassName import com.squareup.kotlinpoet.ksp.toClassName
import dev.inmo.micro_ksp.generator.withNoSuchElementWorkaround
import dev.inmo.micro_ksp.generator.writeFile import dev.inmo.micro_ksp.generator.writeFile
import dev.inmo.micro_utils.ksp.classcasts.ClassCastsExcluded import dev.inmo.micro_utils.ksp.classcasts.ClassCastsExcluded
import dev.inmo.micro_utils.ksp.classcasts.ClassCastsIncluded import dev.inmo.micro_utils.ksp.classcasts.ClassCastsIncluded
@@ -26,11 +25,7 @@ class Processor(
) { ) {
val rootAnnotation = ksClassDeclaration.getAnnotationsByType(ClassCastsIncluded::class).first() val rootAnnotation = ksClassDeclaration.getAnnotationsByType(ClassCastsIncluded::class).first()
val (includeRegex: Regex?, excludeRegex: Regex?) = rootAnnotation.let { val (includeRegex: Regex?, excludeRegex: Regex?) = rootAnnotation.let {
withNoSuchElementWorkaround("") { it.typesRegex.takeIf { it.isNotEmpty() } ?.let(::Regex) to it.excludeRegex.takeIf { it.isNotEmpty() } ?.let(::Regex)
it.typesRegex
}.takeIf { it.isNotEmpty() } ?.let(::Regex) to withNoSuchElementWorkaround("") {
it.excludeRegex
}.takeIf { it.isNotEmpty() } ?.let(::Regex)
} }
val classesSubtypes = mutableMapOf<KSClassDeclaration, MutableSet<KSClassDeclaration>>() val classesSubtypes = mutableMapOf<KSClassDeclaration, MutableSet<KSClassDeclaration>>()
@@ -54,9 +49,7 @@ class Processor(
when { when {
potentialSubtype === ksClassDeclaration -> {} potentialSubtype === ksClassDeclaration -> {}
potentialSubtype.isAnnotationPresent(ClassCastsExcluded::class) -> return@forEach potentialSubtype.isAnnotationPresent(ClassCastsExcluded::class) -> return@forEach
potentialSubtype !is KSClassDeclaration || !potentialSubtype.checkSupertypeLevel( potentialSubtype !is KSClassDeclaration || !potentialSubtype.checkSupertypeLevel(rootAnnotation.levelsToInclude.takeIf { it >= 0 }) -> return@forEach
withNoSuchElementWorkaround(-1) { rootAnnotation.levelsToInclude }.takeIf { it >= 0 }
) -> return@forEach
excludeRegex ?.matches(simpleName) == true -> return@forEach excludeRegex ?.matches(simpleName) == true -> return@forEach
includeRegex ?.matches(simpleName) == false -> {} includeRegex ?.matches(simpleName) == false -> {}
else -> classesSubtypes.getOrPut(ksClassDeclaration) { mutableSetOf() }.add(potentialSubtype) else -> classesSubtypes.getOrPut(ksClassDeclaration) { mutableSetOf() }.add(potentialSubtype)
@@ -103,9 +96,7 @@ class Processor(
@OptIn(KspExperimental::class) @OptIn(KspExperimental::class)
override fun process(resolver: Resolver): List<KSAnnotated> { override fun process(resolver: Resolver): List<KSAnnotated> {
(resolver.getSymbolsWithAnnotation(ClassCastsIncluded::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach { (resolver.getSymbolsWithAnnotation(ClassCastsIncluded::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach {
val prefix = withNoSuchElementWorkaround("") { val prefix = it.getAnnotationsByType(ClassCastsIncluded::class).first().outputFilePrefix
it.getAnnotationsByType(ClassCastsIncluded::class).first().outputFilePrefix
}
it.writeFile(prefix = prefix, suffix = "ClassCasts") { it.writeFile(prefix = prefix, suffix = "ClassCasts") {
FileSpec.builder( FileSpec.builder(
it.packageName.asString(), it.packageName.asString(),

View File

@@ -1,23 +0,0 @@
package dev.inmo.micro_ksp.generator
import com.google.devtools.ksp.KSTypeNotPresentException
import com.google.devtools.ksp.KspExperimental
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.asTypeName
import kotlin.reflect.KClass
@Suppress("NOTHING_TO_INLINE")
@OptIn(KspExperimental::class)
inline fun safeClassName(classnameGetter: () -> KClass<*>) = runCatching {
classnameGetter().asTypeName()
}.getOrElse { e ->
if (e is KSTypeNotPresentException) {
ClassName(
e.ksType.declaration.packageName.asString(),
e.ksType.declaration.qualifiedName ?.asString() ?.replaceFirst(e.ksType.declaration.packageName.asString(), "")
?: e.ksType.declaration.simpleName.asString()
)
} else {
throw e
}
}

View File

@@ -1,12 +0,0 @@
package dev.inmo.micro_ksp.generator
inline fun <T> withNoSuchElementWorkaround(
default: T,
block: () -> T
): T = runCatching(block).getOrElse {
if (it is NoSuchElementException) {
default
} else {
throw it
}
}

View File

@@ -12,7 +12,6 @@ import com.squareup.kotlinpoet.ksp.toClassName
import dev.inmo.micro_ksp.generator.buildSubFileName import dev.inmo.micro_ksp.generator.buildSubFileName
import dev.inmo.micro_ksp.generator.companion import dev.inmo.micro_ksp.generator.companion
import dev.inmo.micro_ksp.generator.findSubClasses import dev.inmo.micro_ksp.generator.findSubClasses
import dev.inmo.micro_ksp.generator.withNoSuchElementWorkaround
import dev.inmo.micro_ksp.generator.writeFile import dev.inmo.micro_ksp.generator.writeFile
import dev.inmo.micro_utils.ksp.sealed.GenerateSealedTypesWorkaround import dev.inmo.micro_utils.ksp.sealed.GenerateSealedTypesWorkaround
import dev.inmo.micro_utils.ksp.sealed.GenerateSealedWorkaround import dev.inmo.micro_utils.ksp.sealed.GenerateSealedWorkaround
@@ -114,7 +113,7 @@ class Processor(
val annotation = ksClassDeclaration.getGenerateSealedTypesWorkaroundAnnotation val annotation = ksClassDeclaration.getGenerateSealedTypesWorkaroundAnnotation
val subClasses = ksClassDeclaration.resolveSubclasses( val subClasses = ksClassDeclaration.resolveSubclasses(
searchIn = resolver.getAllFiles(), searchIn = resolver.getAllFiles(),
allowNonSealed = withNoSuchElementWorkaround(null) { annotation ?.includeNonSealedSubTypes } ?: false allowNonSealed = annotation ?.includeNonSealedSubTypes ?: false
).distinct() ).distinct()
val subClassesNames = subClasses.filter { val subClassesNames = subClasses.filter {
it.getAnnotationsByType(GenerateSealedTypesWorkaround.Exclude::class).count() == 0 it.getAnnotationsByType(GenerateSealedTypesWorkaround.Exclude::class).count() == 0
@@ -165,15 +164,7 @@ class Processor(
@OptIn(KspExperimental::class) @OptIn(KspExperimental::class)
override fun process(resolver: Resolver): List<KSAnnotated> { override fun process(resolver: Resolver): List<KSAnnotated> {
(resolver.getSymbolsWithAnnotation(GenerateSealedWorkaround::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach { (resolver.getSymbolsWithAnnotation(GenerateSealedWorkaround::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach {
val prefix = runCatching { val prefix = (it.getGenerateSealedWorkaroundAnnotation) ?.prefix ?.takeIf {
(it.getGenerateSealedWorkaroundAnnotation) ?.prefix
}.getOrElse {
if (it is NoSuchElementException) {
""
} else {
throw it
}
} ?.takeIf {
it.isNotEmpty() it.isNotEmpty()
} ?: it.buildSubFileName.replaceFirst(it.simpleName.asString(), "") } ?: it.buildSubFileName.replaceFirst(it.simpleName.asString(), "")
it.writeFile(prefix = prefix, suffix = "SealedWorkaround") { it.writeFile(prefix = prefix, suffix = "SealedWorkaround") {
@@ -193,9 +184,7 @@ class Processor(
} }
} }
(resolver.getSymbolsWithAnnotation(GenerateSealedTypesWorkaround::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach { (resolver.getSymbolsWithAnnotation(GenerateSealedTypesWorkaround::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach {
val prefix = withNoSuchElementWorkaround("") { val prefix = (it.getGenerateSealedTypesWorkaroundAnnotation) ?.prefix ?.takeIf {
(it.getGenerateSealedTypesWorkaroundAnnotation)?.prefix
} ?.takeIf {
it.isNotEmpty() it.isNotEmpty()
} ?: it.buildSubFileName.replaceFirst(it.simpleName.asString(), "") } ?: it.buildSubFileName.replaceFirst(it.simpleName.asString(), "")
it.writeFile(prefix = prefix, suffix = "SealedTypesWorkaround") { it.writeFile(prefix = prefix, suffix = "SealedTypesWorkaround") {

View File

@@ -16,7 +16,6 @@ import com.squareup.kotlinpoet.ksp.toTypeName
import dev.inmo.micro_ksp.generator.convertToClassName import dev.inmo.micro_ksp.generator.convertToClassName
import dev.inmo.micro_ksp.generator.convertToClassNames import dev.inmo.micro_ksp.generator.convertToClassNames
import dev.inmo.micro_ksp.generator.findSubClasses import dev.inmo.micro_ksp.generator.findSubClasses
import dev.inmo.micro_ksp.generator.withNoSuchElementWorkaround
import dev.inmo.micro_ksp.generator.writeFile import dev.inmo.micro_ksp.generator.writeFile
import dev.inmo.micro_utils.ksp.variations.GenerateVariations import dev.inmo.micro_utils.ksp.variations.GenerateVariations
import dev.inmo.micro_utils.ksp.variations.GenerationVariant import dev.inmo.micro_utils.ksp.variations.GenerationVariant
@@ -219,9 +218,7 @@ class Processor(
@OptIn(KspExperimental::class) @OptIn(KspExperimental::class)
override fun process(resolver: Resolver): List<KSAnnotated> { override fun process(resolver: Resolver): List<KSAnnotated> {
(resolver.getSymbolsWithAnnotation(GenerateVariations::class.qualifiedName!!)).filterIsInstance<KSFunctionDeclaration>().forEach { (resolver.getSymbolsWithAnnotation(GenerateVariations::class.qualifiedName!!)).filterIsInstance<KSFunctionDeclaration>().forEach {
val prefix = withNoSuchElementWorkaround("") { val prefix = (it.getAnnotationsByType(GenerateVariations::class)).firstOrNull() ?.prefix ?.takeIf {
(it.getAnnotationsByType(GenerateVariations::class)).firstOrNull() ?.prefix
} ?.takeIf {
it.isNotEmpty() it.isNotEmpty()
} ?: it.simpleName.asString().replaceFirst(it.simpleName.asString(), "") } ?: it.simpleName.asString().replaceFirst(it.simpleName.asString(), "")
it.writeFile(prefix = prefix, suffix = "GeneratedVariation") { it.writeFile(prefix = prefix, suffix = "GeneratedVariation") {

View File

@@ -23,9 +23,7 @@ dependencies {
implementation libs.ktor.client.java implementation libs.ktor.client.java
} }
application { mainClassName="MainKt"
mainClass = "MainKt"
}
java { java {
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_17

View File

@@ -3,6 +3,7 @@ package dev.inmo.micro_utils.pagination.compose
import androidx.compose.runtime.* import androidx.compose.runtime.*
import dev.inmo.micro_utils.coroutines.MutableRedeliverStateFlow import dev.inmo.micro_utils.coroutines.MutableRedeliverStateFlow
import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions
import dev.inmo.micro_utils.coroutines.runCatchingLogging
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job

View File

@@ -14,7 +14,7 @@ kotlin {
} }
jvmMain { jvmMain {
dependencies { dependencies {
api libs.jb.exposed.jdbc api libs.jb.exposed
} }
} }
} }

View File

@@ -1,8 +1,6 @@
package dev.inmo.micro_utils.pagination package dev.inmo.micro_utils.pagination
import org.jetbrains.exposed.v1.core.Expression import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.jdbc.Query
fun Query.paginate(with: Pagination, orderBy: Pair<Expression<*>, SortOrder>? = null) = fun Query.paginate(with: Pagination, orderBy: Pair<Expression<*>, SortOrder>? = null) =
limit(with.size) limit(with.size)

View File

@@ -2,8 +2,8 @@ package dev.inmo.micro_utils.repos.exposed
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadCRUDRepo import dev.inmo.micro_utils.repos.ReadCRUDRepo
import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.jdbc.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedReadCRUDRepo<ObjectType, IdType>( abstract class AbstractExposedReadCRUDRepo<ObjectType, IdType>(
tableName: String tableName: String

View File

@@ -4,12 +4,9 @@ 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.channels.BufferOverflow
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.v1.core.statements.InsertStatement import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.statements.UpdateBuilder import org.jetbrains.exposed.sql.statements.*
import org.jetbrains.exposed.v1.jdbc.deleteWhere import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.update
import java.util.Objects import java.util.Objects
abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>( abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
@@ -96,7 +93,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
return transaction(db = database) { return transaction(db = database) {
update( update(
{ {
selectById( id) selectById(this, id)
} }
) { ) {
update(id, value, it as UpdateBuilder<Int>) update(id, value, it as UpdateBuilder<Int>)
@@ -105,7 +102,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
if (it > 0) { if (it > 0) {
transaction(db = database) { transaction(db = database) {
selectAll().where { selectAll().where {
selectById(id) selectById(this, id)
}.limit(1).firstOrNull() ?.asObject }.limit(1).firstOrNull() ?.asObject
} }
} else { } else {
@@ -140,7 +137,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
override suspend fun deleteById(ids: List<IdType>) { override suspend fun deleteById(ids: List<IdType>) {
onBeforeDelete(ids) onBeforeDelete(ids)
transaction(db = database) { transaction(db = database) {
val deleted = deleteWhere { selectByIds(ids) } val deleted = deleteWhere { selectByIds(it, ids) }
if (deleted == ids.size) { if (deleted == ids.size) {
ids ids
} else { } else {

View File

@@ -1,6 +1,6 @@
package dev.inmo.micro_utils.repos.exposed package dev.inmo.micro_utils.repos.exposed
import org.jetbrains.exposed.v1.core.Column import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.sql.Table
typealias ColumnAllocator<T> = Table.() -> Column<T> typealias ColumnAllocator<T> = Table.() -> Column<T>

View File

@@ -1,10 +1,10 @@
package dev.inmo.micro_utils.repos.exposed package dev.inmo.micro_utils.repos.exposed
import org.jetbrains.exposed.v1.core.Column import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.v1.core.isNotNull import org.jetbrains.exposed.sql.SqlExpressionBuilder.isNotNull
import org.jetbrains.exposed.v1.core.isNull import org.jetbrains.exposed.sql.SqlExpressionBuilder.isNull
import org.jetbrains.exposed.v1.core.neq import org.jetbrains.exposed.sql.SqlExpressionBuilder.neq
fun <T> Column<T?>.eqOrIsNull( fun <T> Column<T?>.eqOrIsNull(
value: T? value: T?

View File

@@ -1,14 +1,12 @@
package dev.inmo.micro_utils.repos.exposed package dev.inmo.micro_utils.repos.exposed
import org.jetbrains.exposed.v1.core.Op import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.or
interface CommonExposedRepo<IdType, ObjectType> : ExposedRepo { interface CommonExposedRepo<IdType, ObjectType> : ExposedRepo {
val ResultRow.asObject: ObjectType val ResultRow.asObject: ObjectType
val ResultRow.asId: IdType val ResultRow.asId: IdType
val selectById: (IdType) -> Op<Boolean> val selectById: ISqlExpressionBuilder.(IdType) -> Op<Boolean>
val selectByIds: (List<IdType>) -> Op<Boolean> val selectByIds: ISqlExpressionBuilder.(List<IdType>) -> Op<Boolean>
get() = { get() = {
if (it.isEmpty()) { if (it.isEmpty()) {
Op.FALSE Op.FALSE

View File

@@ -1,3 +1,5 @@
package dev.inmo.micro_utils.repos.exposed package dev.inmo.micro_utils.repos.exposed
import org.jetbrains.exposed.sql.*
interface ExposedCRUDRepo<ObjectType, IdType> : CommonExposedRepo<IdType, ObjectType> interface ExposedCRUDRepo<ObjectType, IdType> : CommonExposedRepo<IdType, ObjectType>

View File

@@ -1,11 +1,7 @@
package dev.inmo.micro_utils.repos.exposed package dev.inmo.micro_utils.repos.exposed
import dev.inmo.micro_utils.repos.Repo import dev.inmo.micro_utils.repos.Repo
import org.jetbrains.exposed.v1.core.FieldSet import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.Transaction
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.Query
import org.jetbrains.exposed.v1.jdbc.selectAll
interface ExposedRepo : Repo, FieldSet { interface ExposedRepo : Repo, FieldSet {
val database: Database val database: Database

View File

@@ -1,85 +1,10 @@
package dev.inmo.micro_utils.repos.exposed package dev.inmo.micro_utils.repos.exposed
import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.Transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.v1.core.exposedLogger
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
import org.jetbrains.exposed.v1.jdbc.SchemaUtils.addMissingColumnsStatements
import org.jetbrains.exposed.v1.jdbc.SchemaUtils.checkMappingConsistence
import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
/**
* Code in this function mostly duplicates Exposed [SchemaUtils.createMissingTablesAndColumns]. It made due to deprecation
* status of the last one and potential lost of it in future updates.
*
* Code doing:
*
* * Creating missed tables
* * Altering missed tables (where possible)
* * Calculate problems with [checkMappingConsistence] and add them in execution too
*
* All changes made in [transaction] with [database] as its `db` argument
*/
fun initTablesInTransaction(vararg tables: Table, database: Database, inBatch: Boolean = false, withLogs: Boolean = true) {
transaction(database) {
fun <R> logTimeSpent(message: String, withLogs: Boolean, block: () -> R): R {
return if (withLogs) {
val start = System.currentTimeMillis()
val answer = block()
exposedLogger.info(message + " took " + (System.currentTimeMillis() - start) + "ms")
answer
} else {
block()
}
}
fun Transaction.execStatements(inBatch: Boolean, statements: List<String>) {
if (inBatch) {
execInBatch(statements)
} else {
for (statement in statements) {
exec(statement)
}
}
}
with(TransactionManager.current()) {
db.dialectMetadata.resetCaches()
val createStatements = logTimeSpent("Preparing create tables statements", withLogs) {
SchemaUtils.createStatements(*tables)
}
logTimeSpent("Executing create tables statements", withLogs) {
execStatements(inBatch, createStatements)
commit()
}
val alterStatements = logTimeSpent("Preparing alter table statements", withLogs) {
addMissingColumnsStatements(tables = tables, withLogs)
}
logTimeSpent("Executing alter table statements", withLogs) {
execStatements(inBatch, alterStatements)
commit()
}
val executedStatements = createStatements + alterStatements
logTimeSpent("Checking mapping consistence", withLogs) {
val modifyTablesStatements = checkMappingConsistence(
tables = tables,
withLogs
).filter { it !in executedStatements }
execStatements(inBatch, modifyTablesStatements)
commit()
}
db.dialectMetadata.resetCaches()
}
}
}
fun Table.initTable(database: Database, inBatch: Boolean, withLogs: Boolean) {
initTablesInTransaction(this, database = database, inBatch = inBatch, withLogs = withLogs)
}
fun Table.initTable(database: Database) { fun Table.initTable(database: Database) {
initTable(database = database, inBatch = false, withLogs = true) transaction(database) { SchemaUtils.createMissingTablesAndColumns(this@initTable) }
} }
fun <T> T.initTable() where T: ExposedRepo, T: Table = initTable(database) fun <T> T.initTable() where T: ExposedRepo, T: Table = initTable(database)

View File

@@ -3,14 +3,9 @@ package dev.inmo.micro_utils.repos.exposed.keyvalue
import dev.inmo.micro_utils.repos.KeyValueRepo import dev.inmo.micro_utils.repos.KeyValueRepo
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.update import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.statements.UpdateBuilder import org.jetbrains.exposed.sql.statements.*
import org.jetbrains.exposed.v1.jdbc.Database import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.deleteAll
import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.update
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
abstract class AbstractExposedKeyValueRepo<Key, Value>( abstract class AbstractExposedKeyValueRepo<Key, Value>(
override val database: Database, override val database: Database,
@@ -59,7 +54,7 @@ abstract class AbstractExposedKeyValueRepo<Key, Value>(
override suspend fun unset(toUnset: List<Key>) { override suspend fun unset(toUnset: List<Key>) {
transaction(database) { transaction(database) {
toUnset.mapNotNull { item -> toUnset.mapNotNull { item ->
if (deleteWhere { selectById(item) } > 0) { if (deleteWhere { selectById(it, item) } > 0) {
item item
} else { } else {
null null
@@ -74,7 +69,7 @@ abstract class AbstractExposedKeyValueRepo<Key, Value>(
transaction(database) { transaction(database) {
toUnset.flatMap { toUnset.flatMap {
val keys = selectAll().where { selectByValue(it) }.mapNotNull { it.asKey } val keys = selectAll().where { selectByValue(it) }.mapNotNull { it.asKey }
deleteWhere { selectByIds(keys) } deleteWhere { selectByIds(it, keys) }
keys keys
} }
}.distinct().forEach { }.distinct().forEach {

View File

@@ -4,12 +4,8 @@ import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadKeyValueRepo import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.* import dev.inmo.micro_utils.repos.exposed.*
import dev.inmo.micro_utils.repos.exposed.utils.selectPaginated import dev.inmo.micro_utils.repos.exposed.utils.selectPaginated
import org.jetbrains.exposed.v1.core.Column import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.Op import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
abstract class AbstractExposedReadKeyValueRepo<Key, Value>( abstract class AbstractExposedReadKeyValueRepo<Key, Value>(
override val database: Database, override val database: Database,
@@ -25,7 +21,7 @@ abstract class AbstractExposedReadKeyValueRepo<Key, Value>(
abstract val ResultRow.asKey: Key abstract val ResultRow.asKey: Key
override val ResultRow.asId: Key override val ResultRow.asId: Key
get() = asKey get() = asKey
abstract val selectByValue: (Value) -> Op<Boolean> abstract val selectByValue: ISqlExpressionBuilder.(Value) -> Op<Boolean>
override suspend fun get(k: Key): Value? = transaction(database) { override suspend fun get(k: Key): Value? = transaction(database) {
selectAll().where { selectById(k) }.limit(1).firstOrNull() ?.asObject selectAll().where { selectById(k) }.limit(1).firstOrNull() ?.asObject

View File

@@ -4,14 +4,11 @@ import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.inList import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.v1.jdbc.Database import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
import org.jetbrains.exposed.v1.jdbc.deleteAll import org.jetbrains.exposed.sql.SqlExpressionBuilder.inSubQuery
import org.jetbrains.exposed.v1.jdbc.deleteWhere import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.update
open class ExposedKeyValueRepo<Key, Value>( open class ExposedKeyValueRepo<Key, Value>(
database: Database, database: Database,

View File

@@ -1,13 +1,13 @@
package dev.inmo.micro_utils.repos.exposed.keyvalue package dev.inmo.micro_utils.repos.exposed.keyvalue
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadKeyValueRepo import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.* import dev.inmo.micro_utils.repos.exposed.*
import org.jetbrains.exposed.v1.core.Column import dev.inmo.micro_utils.repos.exposed.utils.selectPaginated
import org.jetbrains.exposed.v1.core.Op import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.sql.statements.InsertStatement
import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.sql.statements.UpdateBuilder
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.Database
open class ExposedReadKeyValueRepo<Key, Value>( open class ExposedReadKeyValueRepo<Key, Value>(
database: Database, database: Database,
@@ -20,10 +20,10 @@ open class ExposedReadKeyValueRepo<Key, Value>(
val valueColumn: Column<Value> = valueColumnAllocator() val valueColumn: Column<Value> = valueColumnAllocator()
override val ResultRow.asKey: Key override val ResultRow.asKey: Key
get() = get(keyColumn) get() = get(keyColumn)
override val selectByValue: (Value) -> Op<Boolean> = { valueColumn.eq(it) } override val selectByValue: ISqlExpressionBuilder.(Value) -> Op<Boolean> = { valueColumn.eq(it) }
override val ResultRow.asObject: Value override val ResultRow.asObject: Value
get() = get(valueColumn) get() = get(valueColumn)
override val selectById: (Key) -> Op<Boolean> = { keyColumn.eq(it) } override val selectById: ISqlExpressionBuilder.(Key) -> Op<Boolean> = { keyColumn.eq(it) }
override val primaryKey: Table.PrimaryKey override val primaryKey: Table.PrimaryKey
get() = PrimaryKey(keyColumn, valueColumn) get() = PrimaryKey(keyColumn, valueColumn)

View File

@@ -3,13 +3,9 @@ package dev.inmo.micro_utils.repos.exposed.onetomany
import dev.inmo.micro_utils.repos.KeyValuesRepo import dev.inmo.micro_utils.repos.KeyValuesRepo
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.v1.core.and import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.statements.UpdateBuilder import org.jetbrains.exposed.sql.statements.UpdateBuilder
import org.jetbrains.exposed.v1.jdbc.Database import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.batchInsert
import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
abstract class AbstractExposedKeyValuesRepo<Key, Value>( abstract class AbstractExposedKeyValuesRepo<Key, Value>(
override val database: Database, override val database: Database,
@@ -64,7 +60,7 @@ abstract class AbstractExposedKeyValuesRepo<Key, Value>(
val oldObjects = selectAll().where { selectByIds(toSet.keys.toList()) }.map { it.asKey to it.asObject } val oldObjects = selectAll().where { selectByIds(toSet.keys.toList()) }.map { it.asKey to it.asObject }
deleteWhere { deleteWhere {
selectByIds(toSet.keys.toList()) selectByIds(it, toSet.keys.toList())
} }
val inserted = batchInsert( val inserted = batchInsert(
prepreparedData, prepreparedData,
@@ -108,7 +104,7 @@ abstract class AbstractExposedKeyValuesRepo<Key, Value>(
transaction(database) { transaction(database) {
toRemove.keys.flatMap { k -> toRemove.keys.flatMap { k ->
toRemove[k] ?.mapNotNull { v -> toRemove[k] ?.mapNotNull { v ->
if (deleteWhere { selectById(k).and(selectByValue(v)) } > 0 ) { if (deleteWhere { selectById(it, k).and(SqlExpressionBuilder.selectByValue(v)) } > 0 ) {
k to v k to v
} else { } else {
null null
@@ -122,7 +118,7 @@ abstract class AbstractExposedKeyValuesRepo<Key, Value>(
override suspend fun clear(k: Key) { override suspend fun clear(k: Key) {
transaction(database) { transaction(database) {
deleteWhere { selectById(k) } deleteWhere { selectById(it, k) }
}.also { _onDataCleared.emit(k) } }.also { _onDataCleared.emit(k) }
} }
} }

View File

@@ -4,14 +4,8 @@ import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import dev.inmo.micro_utils.repos.exposed.* import dev.inmo.micro_utils.repos.exposed.*
import dev.inmo.micro_utils.repos.exposed.utils.selectPaginated import dev.inmo.micro_utils.repos.exposed.utils.selectPaginated
import org.jetbrains.exposed.v1.core.Column import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.Op import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
abstract class AbstractExposedReadKeyValuesRepo<Key, Value>( abstract class AbstractExposedReadKeyValuesRepo<Key, Value>(
override val database: Database, override val database: Database,
@@ -23,7 +17,7 @@ abstract class AbstractExposedReadKeyValuesRepo<Key, Value>(
abstract val ResultRow.asKey: Key abstract val ResultRow.asKey: Key
override val ResultRow.asId: Key override val ResultRow.asId: Key
get() = asKey get() = asKey
abstract val selectByValue: (Value) -> Op<Boolean> abstract val selectByValue: ISqlExpressionBuilder.(Value) -> Op<Boolean>
override suspend fun count(k: Key): Long = transaction(database) { selectAll().where { selectById(k) }.count() } override suspend fun count(k: Key): Long = transaction(database) { selectAll().where { selectById(k) }.count() }

View File

@@ -4,13 +4,10 @@ import dev.inmo.micro_utils.repos.KeyValuesRepo
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.v1.core.and import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.v1.core.inList import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
import org.jetbrains.exposed.v1.jdbc.Database import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
typealias ExposedOneToManyKeyValueRepo1<Key, Value> = ExposedKeyValuesRepo<Key, Value> typealias ExposedOneToManyKeyValueRepo1<Key, Value> = ExposedKeyValuesRepo<Key, Value>
open class ExposedKeyValuesRepo<Key, Value>( open class ExposedKeyValuesRepo<Key, Value>(
@@ -37,7 +34,7 @@ open class ExposedKeyValuesRepo<Key, Value>(
get() = _onDataCleared get() = _onDataCleared
override suspend fun add(toAdd: Map<Key, List<Value>>) { override suspend fun add(toAdd: Map<Key, List<Value>>) {
transaction (database) { transaction(database) {
toAdd.keys.flatMap { k -> toAdd.keys.flatMap { k ->
toAdd[k] ?.mapNotNull { v -> toAdd[k] ?.mapNotNull { v ->
if (selectAll().where { keyColumn.eq(k).and(valueColumn.eq(v)) }.limit(1).count() > 0) { if (selectAll().where { keyColumn.eq(k).and(valueColumn.eq(v)) }.limit(1).count() > 0) {
@@ -76,7 +73,7 @@ open class ExposedKeyValuesRepo<Key, Value>(
override suspend fun removeWithValue(v: Value) { override suspend fun removeWithValue(v: Value) {
transaction(database) { transaction(database) {
val keys = selectAll().where { selectByValue(v) }.map { it.asKey } val keys = selectAll().where { selectByValue(v) }.map { it.asKey }
deleteWhere { selectByValue(v) } deleteWhere { SqlExpressionBuilder.selectByValue(v) }
keys keys
}.forEach { }.forEach {
_onValueRemoved.emit(it to v) _onValueRemoved.emit(it to v)

View File

@@ -2,11 +2,7 @@ package dev.inmo.micro_utils.repos.exposed.onetomany
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import dev.inmo.micro_utils.repos.exposed.* import dev.inmo.micro_utils.repos.exposed.*
import org.jetbrains.exposed.v1.core.Column import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.Op
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.Database
typealias ExposedReadOneToManyKeyValueRepo<Key, Value> = ExposedReadKeyValuesRepo<Key, Value> typealias ExposedReadOneToManyKeyValueRepo<Key, Value> = ExposedReadKeyValuesRepo<Key, Value>
@@ -19,10 +15,10 @@ open class ExposedReadKeyValuesRepo<Key, Value>(
override val keyColumn: Column<Key> = keyColumnAllocator() override val keyColumn: Column<Key> = keyColumnAllocator()
override val ResultRow.asKey: Key override val ResultRow.asKey: Key
get() = get(keyColumn) get() = get(keyColumn)
override val selectByValue: (Value) -> Op<Boolean> = { valueColumn.eq(it) } override val selectByValue: ISqlExpressionBuilder.(Value) -> Op<Boolean> = { valueColumn.eq(it) }
override val ResultRow.asObject: Value override val ResultRow.asObject: Value
get() = get(valueColumn) get() = get(valueColumn)
override val selectById: (Key) -> Op<Boolean> = { keyColumn.eq(it) } override val selectById: ISqlExpressionBuilder.(Key) -> Op<Boolean> = { keyColumn.eq(it) }
val valueColumn: Column<Value> = valueColumnAllocator() val valueColumn: Column<Value> = valueColumnAllocator()
init { initTable() } init { initTable() }

View File

@@ -1,10 +1,7 @@
package dev.inmo.micro_utils.repos.exposed.utils package dev.inmo.micro_utils.repos.exposed.utils
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import org.jetbrains.exposed.v1.core.Expression import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.jdbc.Query
fun <T> Query.selectPaginated( fun <T> Query.selectPaginated(
pagination: Pagination, pagination: Pagination,

View File

@@ -3,12 +3,8 @@ package dev.inmo.micro_utils.repos.exposed.versions
import dev.inmo.micro_utils.repos.exposed.ExposedRepo import dev.inmo.micro_utils.repos.exposed.ExposedRepo
import dev.inmo.micro_utils.repos.exposed.initTable import dev.inmo.micro_utils.repos.exposed.initTable
import dev.inmo.micro_utils.repos.versions.* import dev.inmo.micro_utils.repos.versions.*
import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.update
/** /**
* Use this method to create [StandardVersionsRepo] based on [Database] with [ExposedStandardVersionsRepoProxy] as * Use this method to create [StandardVersionsRepo] based on [Database] with [ExposedStandardVersionsRepoProxy] as

View File

@@ -1,8 +1,8 @@
package full package full
import com.benasher44.uuid.uuid4 import com.benasher44.uuid.uuid4
import org.jetbrains.exposed.v1.jdbc.Database import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.v1.jdbc.transactions.transactionManager import org.jetbrains.exposed.sql.transactions.transactionManager
import org.sqlite.JDBC import org.sqlite.JDBC
import java.io.File import java.io.File
import java.sql.Connection import java.sql.Connection

View File

@@ -5,12 +5,12 @@ import dev.inmo.micro_utils.repos.CRUDRepo
import dev.inmo.micro_utils.repos.common.tests.CommonCRUDRepoTests import dev.inmo.micro_utils.repos.common.tests.CommonCRUDRepoTests
import dev.inmo.micro_utils.repos.exposed.AbstractExposedCRUDRepo import dev.inmo.micro_utils.repos.exposed.AbstractExposedCRUDRepo
import dev.inmo.micro_utils.repos.exposed.initTable import dev.inmo.micro_utils.repos.exposed.initTable
import org.jetbrains.exposed.v1.core.Op import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.sql.ISqlExpressionBuilder
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.v1.core.statements.InsertStatement import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.v1.core.statements.UpdateBuilder import org.jetbrains.exposed.sql.statements.InsertStatement
import org.jetbrains.exposed.v1.jdbc.Database import org.jetbrains.exposed.sql.statements.UpdateBuilder
import java.io.File import java.io.File
import kotlin.test.AfterTest import kotlin.test.AfterTest
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
@@ -29,7 +29,7 @@ class ExposedCRUDRepoTests : CommonCRUDRepoTests() {
asId, asId,
get(dataColumn) get(dataColumn)
) )
override val selectById: (String) -> Op<Boolean> = { idColumn.eq(it) } override val selectById: ISqlExpressionBuilder.(String) -> Op<Boolean> = { idColumn.eq(it) }
init { init {
initTable() initTable()

View File

@@ -4,11 +4,11 @@ import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.common.tests.CommonKeyValueRepoTests import dev.inmo.micro_utils.repos.common.tests.CommonKeyValueRepoTests
import dev.inmo.micro_utils.repos.exposed.initTable import dev.inmo.micro_utils.repos.exposed.initTable
import dev.inmo.micro_utils.repos.exposed.keyvalue.AbstractExposedKeyValueRepo import dev.inmo.micro_utils.repos.exposed.keyvalue.AbstractExposedKeyValueRepo
import org.jetbrains.exposed.v1.core.Op import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.sql.ISqlExpressionBuilder
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.v1.core.statements.UpdateBuilder import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.v1.jdbc.Database import org.jetbrains.exposed.sql.statements.UpdateBuilder
import java.io.File import java.io.File
import kotlin.test.AfterTest import kotlin.test.AfterTest
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
@@ -22,10 +22,10 @@ class ExposedKeyValueRepoTests : CommonKeyValueRepoTests() {
override val ResultRow.asKey: String override val ResultRow.asKey: String
get() = get(keyColumn) get() = get(keyColumn)
override val selectByValue: (String) -> Op<Boolean> = { dataColumn.eq(it) } override val selectByValue: ISqlExpressionBuilder.(String) -> Op<Boolean> = { dataColumn.eq(it) }
override val ResultRow.asObject: String override val ResultRow.asObject: String
get() = get(dataColumn) get() = get(dataColumn)
override val selectById: (String) -> Op<Boolean> = { keyColumn.eq(it) } override val selectById: ISqlExpressionBuilder.(String) -> Op<Boolean> = { keyColumn.eq(it) }
init { init {
initTable() initTable()

View File

@@ -4,11 +4,11 @@ import dev.inmo.micro_utils.repos.KeyValuesRepo
import dev.inmo.micro_utils.repos.common.tests.CommonKeyValuesRepoTests import dev.inmo.micro_utils.repos.common.tests.CommonKeyValuesRepoTests
import dev.inmo.micro_utils.repos.exposed.initTable import dev.inmo.micro_utils.repos.exposed.initTable
import dev.inmo.micro_utils.repos.exposed.onetomany.AbstractExposedKeyValuesRepo import dev.inmo.micro_utils.repos.exposed.onetomany.AbstractExposedKeyValuesRepo
import org.jetbrains.exposed.v1.core.Op import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.v1.core.ResultRow import org.jetbrains.exposed.sql.ISqlExpressionBuilder
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.v1.core.statements.UpdateBuilder import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.v1.jdbc.Database import org.jetbrains.exposed.sql.statements.UpdateBuilder
import java.io.File import java.io.File
import kotlin.test.AfterTest import kotlin.test.AfterTest
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
@@ -22,10 +22,10 @@ class ExposedKeyValuesRepoTests : CommonKeyValuesRepoTests() {
override val ResultRow.asKey: String override val ResultRow.asKey: String
get() = get(keyColumn) get() = get(keyColumn)
override val selectByValue: (String) -> Op<Boolean> = { dataColumn.eq(it) } override val selectByValue: ISqlExpressionBuilder.(String) -> Op<Boolean> = { dataColumn.eq(it) }
override val ResultRow.asObject: String override val ResultRow.asObject: String
get() = get(dataColumn) get() = get(dataColumn)
override val selectById: (String) -> Op<Boolean> = { keyColumn.eq(it) } override val selectById: ISqlExpressionBuilder.(String) -> Op<Boolean> = { keyColumn.eq(it) }
init { init {
initTable() initTable()