diff --git a/CHANGELOG.md b/CHANGELOG.md index 9174de96bf5..ee6c5f60e26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 0.24.6 + +* `Versions`: + * `KSLog`: `1.4.0` -> `1.4.1` + * `Exposed`: `0.58.0` -> `0.59.0` + * `SQLite`: `3.48.0.0` -> `3.49.0.0` + * `AndroidFragment`: `1.8.5` -> `1.8.6` +* `Coroutines`: + * Safely functions has been replaced with `Logging` variations (resolve of [#541](https://github.com/InsanusMokrassar/MicroUtils/issues/541)) +* `KSP`: + * `Variations`: + * Module has been created + ## 0.24.5 * `Versions`: diff --git a/coroutines/build.gradle b/coroutines/build.gradle index 42c09d974c4..5b5d02e9f95 100644 --- a/coroutines/build.gradle +++ b/coroutines/build.gradle @@ -11,6 +11,7 @@ kotlin { commonMain { dependencies { api libs.kt.coroutines + api libs.kslog } } jsMain { diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ContextSafelyExceptionHandler.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ContextSafelyExceptionHandler.kt index 140e59ae9e1..8e62a124087 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ContextSafelyExceptionHandler.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ContextSafelyExceptionHandler.kt @@ -41,4 +41,4 @@ class ContextSafelyExceptionHandler( ) : CoroutineContext.Element { override val key: CoroutineContext.Key<*> get() = ContextSafelyExceptionHandlerKey -} \ No newline at end of file +} diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscription.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscription.kt index fddcc64ac4d..47bd4e1ceaa 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscription.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscription.kt @@ -2,6 +2,7 @@ package dev.inmo.micro_utils.coroutines +import dev.inmo.kslog.common.KSLog import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* import kotlinx.coroutines.sync.Mutex @@ -16,6 +17,45 @@ inline fun Flow.subscribe(scope: CoroutineScope, noinline block: suspend * Use [subscribe], but all [block]s will be called inside of [safely] function. * Use [onException] to set up your reaction for [Throwable]s */ +inline fun Flow.subscribeLogging( + scope: CoroutineScope, + noinline errorMessageBuilder: T.(Throwable) -> Any = { "Something web wrong" }, + logger: KSLog = KSLog, + noinline block: suspend (T) -> Unit +) = subscribe(scope) { + it.runCatchingLogging( + errorMessageBuilder, + logger + ) { + block(it) + }.getOrThrow() +} + +/** + * Use [subscribeSafelyWithoutExceptions], but all exceptions will be passed to [defaultSafelyExceptionHandler] + */ +inline fun Flow.subscribeLoggingDropExceptions( + scope: CoroutineScope, + noinline errorMessageBuilder: T.(Throwable) -> Any = { "Something web wrong" }, + logger: KSLog = KSLog, + noinline block: suspend (T) -> Unit +) = subscribe(scope) { + it.runCatchingLogging( + errorMessageBuilder, + logger + ) { + block(it) + } +} + +/** + * Use [subscribe], but all [block]s will be called inside of [safely] function. + * Use [onException] to set up your reaction for [Throwable]s + */ +@Deprecated( + "Will be removed soon due to replacement by subscribeLogging", + ReplaceWith("this.subscribeLogging(scope = scope, block = block)") +) inline fun Flow.subscribeSafely( scope: CoroutineScope, noinline onException: ExceptionHandler = defaultSafelyExceptionHandler, @@ -29,6 +69,10 @@ inline fun Flow.subscribeSafely( /** * Use [subscribeSafelyWithoutExceptions], but all exceptions will be passed to [defaultSafelyExceptionHandler] */ +@Deprecated( + "Will be removed soon due to replacement by subscribeLoggingDropExceptions", + ReplaceWith("this.subscribeLoggingDropExceptions(scope = scope, block = block)") +) inline fun Flow.subscribeSafelyWithoutExceptions( scope: CoroutineScope, noinline onException: ExceptionHandler = defaultSafelyWithoutExceptionHandlerWithNull, @@ -42,6 +86,10 @@ inline fun Flow.subscribeSafelyWithoutExceptions( /** * Use [subscribeSafelyWithoutExceptions], but all exceptions inside of [safely] will be skipped */ +@Deprecated( + "Will be removed soon due to replacement by subscribeLoggingDropExceptions", + ReplaceWith("this.subscribeLoggingDropExceptions(scope = scope, block = block)") +) inline fun Flow.subscribeSafelySkippingExceptions( scope: CoroutineScope, noinline block: suspend (T) -> Unit diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscriptionAsync.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscriptionAsync.kt index 5a8bea14a88..667bba39c3f 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscriptionAsync.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscriptionAsync.kt @@ -15,7 +15,7 @@ private class SubscribeAsyncReceiver( get() = dataChannel init { - scope.launchSafelyWithoutExceptions { + scope.launchLoggingDropExceptions { for (data in dataChannel) { output(data) } diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt index ce84e2c71fe..bd29880e3c7 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/HandleSafely.kt @@ -15,6 +15,10 @@ import kotlin.coroutines.coroutineContext * * @return [Result] with result of [block] if no exceptions or [Result] from [onException] execution */ +@Deprecated( + "This function become redundant since coroutines correctly handling throwing exceptions", + replaceWith = ReplaceWith("runCatching(block).replaceIfFailure { onException(it) }") +) suspend inline fun runCatchingSafely( onException: ExceptionHandler, block: suspend () -> T @@ -29,6 +33,10 @@ suspend inline fun runCatchingSafely( } } +@Deprecated( + "This function become redundant since coroutines correctly handling throwing exceptions", + replaceWith = ReplaceWith("runCatching(block).replaceIfFailure { onException(it) }") +) suspend inline fun R.runCatchingSafely( onException: ExceptionHandler, block: suspend R.() -> T @@ -39,10 +47,18 @@ suspend inline fun R.runCatchingSafely( /** * Launching [runCatchingSafely] with [defaultSafelyExceptionHandler] as `onException` parameter */ +@Deprecated( + "This function become redundant since coroutines correctly handling throwing exceptions", + replaceWith = ReplaceWith("runCatching(block).replaceIfFailure { defaultSafelyExceptionHandler(it) }") +) suspend inline fun runCatchingSafely( block: suspend () -> T ): Result = runCatchingSafely(defaultSafelyExceptionHandler, block) +@Deprecated( + "This function become redundant since coroutines correctly handling throwing exceptions", + replaceWith = ReplaceWith("runCatching(block).replaceIfFailure { defaultSafelyExceptionHandler(it) }") +) suspend inline fun R.runCatchingSafely( block: suspend R.() -> T ): Result = runCatchingSafely { @@ -73,6 +89,9 @@ suspend fun contextSafelyExceptionHandler() = coroutineContext[ContextSafelyExce * After all, will be called [withContext] method with created [ContextSafelyExceptionHandler] and block which will call * [safely] method with [safelyExceptionHandler] as onException parameter and [block] as execution block */ +@Deprecated( + "This function become redundant since coroutines correctly handling throwing exceptions", +) suspend fun safelyWithContextExceptionHandler( contextExceptionHandler: ExceptionHandler, safelyExceptionHandler: ExceptionHandler = defaultSafelyExceptionHandler, @@ -94,6 +113,10 @@ suspend fun safelyWithContextExceptionHandler( * * @see runCatchingSafely */ +@Deprecated( + "This function become redundant since coroutines correctly handling throwing exceptions", + replaceWith = ReplaceWith("runCatching(block).replaceIfFailure { onException(it) }.getOrThrow()") +) suspend inline fun safely( onException: ExceptionHandler, block: suspend () -> T @@ -104,9 +127,17 @@ suspend inline fun safely( * * @see runCatchingSafely */ +@Deprecated( + "This function become redundant since coroutines correctly handling throwing exceptions", + replaceWith = ReplaceWith("runCatching(block).replaceIfFailure { defaultSafelyExceptionHandler(it) }.getOrThrow()") +) suspend inline fun safely( block: suspend () -> T ): T = safely(defaultSafelyExceptionHandler, block) +@Deprecated( + "This function become redundant since coroutines correctly handling throwing exceptions", + replaceWith = ReplaceWith("runCatching(block).replaceIfFailure { defaultSafelyExceptionHandler(it) }.getOrThrow()") +) suspend inline fun R.safely( block: suspend R.() -> T ): T = safely { block() } @@ -137,11 +168,19 @@ val defaultSafelyWithoutExceptionHandlerWithNull: ExceptionHandler = { * Shortcut for [safely] with exception handler, that as expected must return null in case of impossible creating of * result from exception (instead of throwing it, by default always returns null) */ +@Deprecated( + "This function become redundant since coroutines correctly handling throwing exceptions", + replaceWith = ReplaceWith("runCatching(block).replaceIfFailure { onException(it) }.getOrNull()") +) suspend fun safelyWithoutExceptions( onException: ExceptionHandler = defaultSafelyExceptionHandler, block: suspend () -> T ): T? = runCatchingSafely(onException, block).getOrNull() +@Deprecated( + "This function become redundant since coroutines correctly handling throwing exceptions", + replaceWith = ReplaceWith("runCatching(block).replaceIfFailure { onException(it) }.getOrNull()") +) suspend fun runCatchingSafelyWithoutExceptions( onException: ExceptionHandler = defaultSafelyExceptionHandler, block: suspend () -> T diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchLogging.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchLogging.kt new file mode 100644 index 00000000000..4409e84f620 --- /dev/null +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchLogging.kt @@ -0,0 +1,55 @@ +package dev.inmo.micro_utils.coroutines + +import dev.inmo.kslog.common.KSLog +import dev.inmo.kslog.common.e +import kotlinx.coroutines.* +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +fun CoroutineScope.launchLogging( + errorMessageBuilder: CoroutineScope.(Throwable) -> Any = { "Something web wrong" }, + logger: KSLog = KSLog, + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit +) = launch(context, start) { + runCatching { block() }.onFailure { + logger.e(it) { errorMessageBuilder(it) } + }.getOrThrow() +} + +fun CoroutineScope.launchLoggingDropExceptions( + errorMessageBuilder: CoroutineScope.(Throwable) -> Any = { "Something web wrong" }, + logger: KSLog = KSLog, + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit +) = launch(context, start) { + runCatching { block() }.onFailure { + logger.e(it) { errorMessageBuilder(it) } + } // just dropping exception +} + +fun CoroutineScope.asyncLogging( + errorMessageBuilder: CoroutineScope.(Throwable) -> Any = { "Something web wrong" }, + logger: KSLog = KSLog, + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> T +) = async(context, start) { + runCatching { block() }.onFailure { + logger.e(it) { errorMessageBuilder(it) } + }.getOrThrow() +} + +fun CoroutineScope.asyncLoggingDropExceptions( + errorMessageBuilder: CoroutineScope.(Throwable) -> Any = { "Something web wrong" }, + logger: KSLog = KSLog, + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> T +) = async(context, start) { + runCatching { block() }.onFailure { + logger.e(it) { errorMessageBuilder(it) } + } +} diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchSafely.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchSafely.kt index 13583d2c622..155e2df47ac 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchSafely.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchSafely.kt @@ -4,6 +4,10 @@ import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +@Deprecated( + "This method will be removed soon. Use launchLogging instead", + ReplaceWith("this.launchLogging(context = context, start = start, block = block)") +) fun CoroutineScope.launchSafely( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, @@ -15,6 +19,10 @@ fun CoroutineScope.launchSafely( } } +@Deprecated( + "This method will be removed soon. Use launchLoggingDropExceptions instead", + ReplaceWith("this.launchLoggingDropExceptions(context = context, start = start, block = block)") +) fun CoroutineScope.launchSafelyWithoutExceptions( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, @@ -26,6 +34,10 @@ fun CoroutineScope.launchSafelyWithoutExceptions( } } +@Deprecated( + "This method will be removed soon. Use asyncLogging instead", + ReplaceWith("this.asyncLogging(context = context, start = start, block = block)") +) fun CoroutineScope.asyncSafely( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, @@ -37,6 +49,10 @@ fun CoroutineScope.asyncSafely( } } +@Deprecated( + "This method will be removed soon. Use asyncLoggingDropExceptions instead", + ReplaceWith("this.asyncLoggingDropExceptions(context = context, start = start, block = block)") +) fun CoroutineScope.asyncSafelyWithoutExceptions( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ReplaceResult.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ReplaceResult.kt new file mode 100644 index 00000000000..a9629015e91 --- /dev/null +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ReplaceResult.kt @@ -0,0 +1,3 @@ +package dev.inmo.micro_utils.coroutines + +inline fun Result.replaceIfFailure(onException: (Throwable) -> T) = if (isSuccess) { this } else { runCatching { onException(exceptionOrNull()!!) } } diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/RunCatchingLogging.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/RunCatchingLogging.kt new file mode 100644 index 00000000000..7dc9239cd03 --- /dev/null +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/RunCatchingLogging.kt @@ -0,0 +1,12 @@ +package dev.inmo.micro_utils.coroutines + +import dev.inmo.kslog.common.KSLog +import dev.inmo.kslog.common.e + +inline fun R.runCatchingLogging( + noinline errorMessageBuilder: R.(Throwable) -> Any = { "Something web wrong" }, + logger: KSLog = KSLog, + block: R.() -> T +) = runCatching(block).onFailure { + logger.e(it) { errorMessageBuilder(it) } +} diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesMachine.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesMachine.kt index a395137f40e..a6b7eaf8518 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesMachine.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/StatesMachine.kt @@ -128,8 +128,10 @@ open class DefaultStatesMachine ( */ override fun start(scope: CoroutineScope): Job { val supervisorScope = scope.LinkedSupervisorScope() - supervisorScope.launchSafelyWithoutExceptions { - (statesManager.getActiveStates().asFlow() + statesManager.onStartChain).subscribeSafelyWithoutExceptions(supervisorScope) { + supervisorScope.launchLoggingDropExceptions { + (statesManager.getActiveStates().asFlow() + statesManager.onStartChain).subscribeSafelyWithoutExceptions( + supervisorScope + ) { supervisorScope.launch { performStateUpdate(Optional.absent(), it, supervisorScope) } } statesManager.onChainStateUpdated.subscribeSafelyWithoutExceptions(supervisorScope) { @@ -140,7 +142,7 @@ open class DefaultStatesMachine ( statesJobsMutex.withLock { val stateInMap = statesJobs.keys.firstOrNull { stateInMap -> stateInMap == removedState } if (stateInMap === removedState) { - statesJobs[stateInMap] ?.cancel() + statesJobs[stateInMap]?.cancel() } } } diff --git a/gradle.properties b/gradle.properties index 3f6658a13df..ce1fb86fb78 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,5 +15,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.24.5 -android_code_version=285 +version=0.24.6 +android_code_version=286 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 02579471e68..acaede35437 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,18 +4,18 @@ kt = "2.1.10" kt-serialization = "1.8.0" kt-coroutines = "1.10.1" -kslog = "1.4.0" +kslog = "1.4.1" jb-compose = "1.7.3" -jb-exposed = "0.58.0" +jb-exposed = "0.59.0" jb-dokka = "2.0.0" -sqlite = "3.48.0.0" +sqlite = "3.49.0.0" korlibs = "5.4.0" uuid = "0.8.4" -ktor = "3.0.3" +ktor = "3.1.0" gh-release = "2.5.2" @@ -28,16 +28,15 @@ kotlin-poet = "1.18.1" versions = "0.51.0" -android-gradle = "8.2.2" +android-gradle = "8.7.+" dexcount = "4.0.0" android-coreKtx = "1.15.0" android-recyclerView = "1.4.0" android-appCompat = "1.7.0" -android-fragment = "1.8.5" +android-fragment = "1.8.6" android-espresso = "3.6.1" android-test = "1.2.1" -android-compose-material3 = "1.3.0" android-props-minSdk = "21" android-props-compileSdk = "35" diff --git a/gradle/templates/publish.gradle b/gradle/templates/publish.gradle index 740b6a4542f..e71a7ba4309 100644 --- a/gradle/templates/publish.gradle +++ b/gradle/templates/publish.gradle @@ -1,3 +1,7 @@ +if (ext.getProperties()["do_publish"] == false) { + return +} + apply plugin: 'maven-publish' task javadocsJar(type: Jar) { @@ -19,29 +23,29 @@ publishing { } developers { - + developer { id = "InsanusMokrassar" name = "Aleksei Ovsiannikov" email = "ovsyannikov.alexey95@gmail.com" } - + developer { id = "000Sanya" name = "Syrov Aleksandr" email = "000sanya.000sanya@gmail.com" } - + } licenses { - + license { name = "Apache Software License 2.0" url = "https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE" } - + } } repositories { @@ -49,51 +53,51 @@ publishing { maven { name = "GithubPackages" url = uri("https://maven.pkg.github.com/InsanusMokrassar/MicroUtils") - + credentials { username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER') password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD') } - + } } if ((project.hasProperty('INMONEXUS_USER') || System.getenv('INMONEXUS_USER') != null) && (project.hasProperty('INMONEXUS_PASSWORD') || System.getenv('INMONEXUS_PASSWORD') != null)) { maven { name = "InmoNexus" url = uri("https://nexus.inmo.dev/repository/maven-releases/") - + credentials { username = project.hasProperty('INMONEXUS_USER') ? project.property('INMONEXUS_USER') : System.getenv('INMONEXUS_USER') password = project.hasProperty('INMONEXUS_PASSWORD') ? project.property('INMONEXUS_PASSWORD') : System.getenv('INMONEXUS_PASSWORD') } - + } } if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { maven { name = "sonatype" url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") - + credentials { 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') } - + } } } } } - + if (project.hasProperty("signing.gnupg.keyName")) { apply plugin: 'signing' - + signing { useGpgCmd() - + sign publishing.publications } - + task signAll { tasks.withType(Sign).forEach { dependsOn(it) diff --git a/ksp/classcasts/generator/test/build.gradle b/ksp/classcasts/generator/test/build.gradle index fb0690103e3..4f6203b4006 100644 --- a/ksp/classcasts/generator/test/build.gradle +++ b/ksp/classcasts/generator/test/build.gradle @@ -5,6 +5,8 @@ plugins { id "com.google.devtools.ksp" } +ext.do_publish = false + apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/ksp/generator/src/main/kotlin/FilesWorkaround.kt b/ksp/generator/src/main/kotlin/FilesWorkaround.kt index 17bbabc22b4..31d8d180e76 100644 --- a/ksp/generator/src/main/kotlin/FilesWorkaround.kt +++ b/ksp/generator/src/main/kotlin/FilesWorkaround.kt @@ -1,28 +1,38 @@ package dev.inmo.micro_ksp.generator import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSDeclaration import com.google.devtools.ksp.symbol.KSFile +import com.google.devtools.ksp.symbol.KSFunctionDeclaration import com.squareup.kotlinpoet.FileSpec import java.io.File -fun KSClassDeclaration.writeFile( +fun KSDeclaration.writeFile( prefix: String = "", suffix: String = "", relatedPath: String = "", force: Boolean = false, + forceUppercase: Boolean = true, fileSpecBuilder: () -> FileSpec ) { val containingFile = containingFile!! + val simpleName = if (forceUppercase) { + val rawSimpleName = simpleName.asString() + rawSimpleName.replaceFirst(rawSimpleName.first().toString(), rawSimpleName.first().uppercase()) + } else { + simpleName.asString() + } File( File( File(containingFile.filePath).parent, relatedPath ), - "$prefix${simpleName.asString()}$suffix.kt" + "$prefix${simpleName}$suffix.kt" ).takeIf { force || !it.exists() } ?.apply { parentFile.mkdirs() + val fileSpec = fileSpecBuilder() writer().use { writer -> - fileSpecBuilder().writeTo(writer) + fileSpec.writeTo(writer) } } } @@ -42,8 +52,9 @@ fun KSFile.writeFile( "$prefix${fileName.dropLastWhile { it != '.' }.removeSuffix(".")}$suffix.kt" ).takeIf { force || !it.exists() } ?.apply { parentFile.mkdirs() + val fileSpec = fileSpecBuilder() writer().use { writer -> - fileSpecBuilder().writeTo(writer) + fileSpec.writeTo(writer) } } -} \ No newline at end of file +} diff --git a/ksp/generator/src/main/kotlin/KClassWorkarounds.kt b/ksp/generator/src/main/kotlin/KClassWorkarounds.kt new file mode 100644 index 00000000000..c3dd6304e47 --- /dev/null +++ b/ksp/generator/src/main/kotlin/KClassWorkarounds.kt @@ -0,0 +1,25 @@ +package dev.inmo.micro_ksp.generator + +import com.google.devtools.ksp.KSTypeNotPresentException +import com.google.devtools.ksp.KSTypesNotPresentException +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.symbol.KSType +import com.squareup.kotlinpoet.asClassName +import com.squareup.kotlinpoet.ksp.toClassName +import kotlin.reflect.KClass + +@OptIn(KspExperimental::class) +inline fun convertToClassName(getter: () -> KClass<*>) = try { + getter().asClassName() +} catch (e: KSTypeNotPresentException) { + e.ksType.toClassName() +} + +@OptIn(KspExperimental::class) +inline fun convertToClassNames(getter: () -> List>) = try { + getter().map { it.asClassName() } +} catch (e: KSTypesNotPresentException) { + e.ksTypes.map { + it.toClassName() + } +} diff --git a/ksp/sealed/generator/test/build.gradle b/ksp/sealed/generator/test/build.gradle index 9bb06ae1fd8..24de402973e 100644 --- a/ksp/sealed/generator/test/build.gradle +++ b/ksp/sealed/generator/test/build.gradle @@ -5,6 +5,8 @@ plugins { id "com.google.devtools.ksp" } +ext.do_publish = false + apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/ksp/variations/build.gradle b/ksp/variations/build.gradle new file mode 100644 index 00000000000..9e169e84113 --- /dev/null +++ b/ksp/variations/build.gradle @@ -0,0 +1,7 @@ +plugins { + id "org.jetbrains.kotlin.multiplatform" + id "org.jetbrains.kotlin.plugin.serialization" + id "com.android.library" +} + +apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/ksp/variations/generator/build.gradle b/ksp/variations/generator/build.gradle new file mode 100644 index 00000000000..4e4e2e1436d --- /dev/null +++ b/ksp/variations/generator/build.gradle @@ -0,0 +1,22 @@ +plugins { + id "org.jetbrains.kotlin.jvm" +} + +apply from: "$publish_jvm" + +repositories { + mavenCentral() +} + +dependencies { + implementation libs.kt.stdlib + api project(":micro_utils.ksp.generator") + api project(":micro_utils.ksp.variations") + api libs.kotlin.poet + api libs.ksp +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} diff --git a/ksp/variations/generator/src/main/kotlin/Processor.kt b/ksp/variations/generator/src/main/kotlin/Processor.kt new file mode 100644 index 00000000000..59d8ad6bc5e --- /dev/null +++ b/ksp/variations/generator/src/main/kotlin/Processor.kt @@ -0,0 +1,243 @@ +package dev.inmo.micro_utils.ksp.variations.generator + +import com.google.devtools.ksp.KSTypeNotPresentException +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.getAnnotationsByType +import com.google.devtools.ksp.processing.CodeGenerator +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.symbol.* +import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.ksp.toAnnotationSpec +import com.squareup.kotlinpoet.ksp.toClassName +import com.squareup.kotlinpoet.ksp.toKModifier +import com.squareup.kotlinpoet.ksp.toTypeName +import dev.inmo.micro_ksp.generator.convertToClassName +import dev.inmo.micro_ksp.generator.convertToClassNames +import dev.inmo.micro_ksp.generator.findSubClasses +import dev.inmo.micro_ksp.generator.writeFile +import dev.inmo.micro_utils.ksp.variations.GenerateVariations +import dev.inmo.micro_utils.ksp.variations.GenerationVariant +import kotlin.math.pow + +class Processor( + private val codeGenerator: CodeGenerator +) : SymbolProcessor { + private fun KSClassDeclaration.findSealedConnection(potentialSealedParent: KSClassDeclaration): Boolean { + val targetClassname = potentialSealedParent.qualifiedName ?.asString() + return superTypes.any { + val itAsDeclaration = it.resolve().declaration as? KSClassDeclaration ?: return@any false + targetClassname == (itAsDeclaration.qualifiedName ?.asString()) || (itAsDeclaration.getSealedSubclasses().any() && itAsDeclaration.findSealedConnection(potentialSealedParent)) + } + } + + private fun KSClassDeclaration.resolveSubclasses( + searchIn: Sequence, + allowNonSealed: Boolean + ): Sequence { + return findSubClasses(searchIn).let { + if (allowNonSealed) { + it + } else { + it.filter { + it.findSealedConnection(this) + } + } + } + } + + @OptIn(KspExperimental::class) + private fun FileSpec.Builder.generateVariations( + ksFunctionDeclaration: KSFunctionDeclaration, + resolver: Resolver + ) { + val annotation = ksFunctionDeclaration.getAnnotationsByType(GenerateVariations::class).first() + val variations: List, KSValueParameter>> = ksFunctionDeclaration.parameters.mapNotNull { + val variationAnnotations = it.getAnnotationsByType(GenerationVariant::class).toList() + variationAnnotations to it + } + val accumulatedGenerations = mutableSetOf>>() + val baseFunctionParameters = ksFunctionDeclaration.parameters.mapNotNull { + ParameterSpec + .builder( + it.name ?.asString() ?: return@mapNotNull null, + it.type.toTypeName(), + ) + .apply { + if (it.isCrossInline) { + addModifiers(KModifier.CROSSINLINE) + } + if (it.isVal) { + addModifiers(KModifier.VALUE) + } + if (it.isNoInline) { + addModifiers(KModifier.NOINLINE) + } + if (it.isVararg) { + addModifiers(KModifier.VARARG) + } + it.annotations.forEach { + if (it.shortName.asString() == GenerationVariant::class.simpleName) return@forEach + + addAnnotation(it.toAnnotationSpec(omitDefaultValues = false)) + } + } + .build() to it.hasDefault + } + val baseFunctionFunSpecs = mutableListOf>>() + let { + var defaultParametersIndicator = 0u + val maxIndicator = baseFunctionParameters.filter { it.second }.foldIndexed(0u) { index, acc, _ -> + 2.0.pow(index).toUInt() + acc + } + while (defaultParametersIndicator <= maxIndicator) { + var currentDefaultParameterIndex = 0u + val baseFunctionDefaults = mutableMapOf() + val funSpec = FunSpec.builder(ksFunctionDeclaration.simpleName.asString()).apply { + modifiers.addAll(ksFunctionDeclaration.modifiers.mapNotNull { it.toKModifier() }) + ksFunctionDeclaration.annotations.forEach { + if (it.shortName.asString() == GenerateVariations::class.simpleName) return@forEach + + addAnnotation(it.toAnnotationSpec(omitDefaultValues = false)) + } + ksFunctionDeclaration.extensionReceiver ?.let { + receiver(it.toTypeName()) + } + ksFunctionDeclaration.returnType ?.let { + returns(it.toTypeName()) + } + } + baseFunctionParameters.forEach { (parameter, hasDefault) -> + if (hasDefault) { + val shouldBeIncluded = (2.0.pow(currentDefaultParameterIndex.toInt()).toUInt()).and(defaultParametersIndicator) > 0u + currentDefaultParameterIndex++ + + if (!shouldBeIncluded) { + return@forEach + } + } + funSpec.addParameter(parameter) + val name = parameter.name + val defaultValueString = if (parameter.modifiers.contains(KModifier.VARARG)) { + "*$name" + } else { + "$name" + } + baseFunctionDefaults[parameter.name] = defaultValueString + } + baseFunctionFunSpecs.add( + funSpec.build() to baseFunctionDefaults.toMap() + ) + defaultParametersIndicator++ + } + } + variations.forEach { (variations, parameter) -> + (baseFunctionFunSpecs + accumulatedGenerations).forEach { (accumulatedGeneration, baseDefaults) -> + if ((parameter.name ?.asString() ?: "this") !in baseDefaults.keys) { + return@forEach + } + variations.forEach { variation -> + val defaults = mutableMapOf() + accumulatedGenerations.add( + FunSpec.builder(accumulatedGeneration.name).apply { + modifiers.addAll(accumulatedGeneration.modifiers) + accumulatedGeneration.annotations.forEach { + addAnnotation(it) + } + accumulatedGeneration.receiverType ?.let { + receiver(it) + } + returns(accumulatedGeneration.returnType) + accumulatedGeneration.parameters.forEach { + val actualName = if (variation.argName.isEmpty()) it.name else variation.argName + parameters.add( + (if (it.name == (parameter.name ?.asString() ?: "this")) { + val type = convertToClassName { variation.type } + val genericTypes = convertToClassNames { variation.genericTypes.toList() } + ParameterSpec + .builder( + actualName, + if (genericTypes.isEmpty()) { + type + } else { + type.parameterizedBy( + *genericTypes.toTypedArray() + ) + } + ) + .apply { + addModifiers(it.modifiers) + val defaultValueString = """ + with(${actualName}) {${ + if (it.modifiers.contains(KModifier.VARARG)) { + "map { it.${variation.conversion} }.toTypedArray()" + } else { + "${variation.conversion}" + } + }} + """.trimIndent() + defaults[it.name] = defaultValueString + } + } else { + it.toBuilder().apply { + defaults[it.name] = it.name + } + }) + .apply { + it.annotations.forEach { + addAnnotation(it) + } + } + .build() + ) + } + val parameters = accumulatedGeneration.parameters.joinToString(", ") { + val itName = it.name + """ + $itName = ${defaults[itName] ?: baseDefaults[itName] ?: itName} + """.trimIndent() + } + addCode( + """ + return ${ksFunctionDeclaration.simpleName.asString()}( + $parameters + ) + """.trimIndent() + ) + }.build() to defaults.toMap() + ) + } + } + } + accumulatedGenerations.forEach { + addFunction(it.first) + } + } + + @OptIn(KspExperimental::class) + override fun process(resolver: Resolver): List { + (resolver.getSymbolsWithAnnotation(GenerateVariations::class.qualifiedName!!)).filterIsInstance().forEach { + val prefix = (it.getAnnotationsByType(GenerateVariations::class)).firstOrNull() ?.prefix ?.takeIf { + it.isNotEmpty() + } ?: it.simpleName.asString().replaceFirst(it.simpleName.asString(), "") + it.writeFile(prefix = prefix, suffix = "GeneratedVariation") { + FileSpec.builder( + it.packageName.asString(), + "${it.simpleName.getShortName().let { it.replaceFirst(it.first().toString(), it.first().uppercase()) }}GeneratedVariation" + ).apply { + addFileComment( + """ + THIS CODE HAVE BEEN GENERATED AUTOMATICALLY + TO REGENERATE IT JUST DELETE FILE + ORIGINAL FILE: ${it.containingFile ?.fileName} + """.trimIndent() + ) + generateVariations(it, resolver) + }.build() + } + } + + return emptyList() + } +} diff --git a/ksp/variations/generator/src/main/kotlin/Provider.kt b/ksp/variations/generator/src/main/kotlin/Provider.kt new file mode 100644 index 00000000000..e3721a6b7d0 --- /dev/null +++ b/ksp/variations/generator/src/main/kotlin/Provider.kt @@ -0,0 +1,11 @@ +package dev.inmo.micro_utils.ksp.variations.generator + +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider + +class Provider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = Processor( + environment.codeGenerator + ) +} diff --git a/ksp/variations/generator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/ksp/variations/generator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000000..443f688865d --- /dev/null +++ b/ksp/variations/generator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +dev.inmo.micro_utils.ksp.variations.generator.Provider diff --git a/ksp/variations/generator/test/build.gradle b/ksp/variations/generator/test/build.gradle new file mode 100644 index 00000000000..b610e624073 --- /dev/null +++ b/ksp/variations/generator/test/build.gradle @@ -0,0 +1,30 @@ +plugins { + id "org.jetbrains.kotlin.multiplatform" + id "org.jetbrains.kotlin.plugin.serialization" + id "com.android.library" + id "com.google.devtools.ksp" +} + +ext.do_publish = false + +apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" + + +kotlin { + sourceSets { + commonMain { + dependencies { + implementation libs.kt.stdlib + api project(":micro_utils.ksp.variations") + } + } + } +} + + +dependencies { + add("kspCommonMainMetadata", project(":micro_utils.ksp.variations.generator")) +} + +ksp { +} diff --git a/ksp/variations/generator/test/src/commonMain/kotlin/Sample2GeneratedVariation.kt b/ksp/variations/generator/test/src/commonMain/kotlin/Sample2GeneratedVariation.kt new file mode 100644 index 00000000000..6407b494f11 --- /dev/null +++ b/ksp/variations/generator/test/src/commonMain/kotlin/Sample2GeneratedVariation.kt @@ -0,0 +1,61 @@ +// THIS CODE HAVE BEEN GENERATED AUTOMATICALLY +// TO REGENERATE IT JUST DELETE FILE +// ORIGINAL FILE: SampleFun.kt +package dev.inmo.micro_utils.ksp.variations.generator.test + +import kotlin.Boolean +import kotlin.Int +import kotlin.String +import kotlin.Unit + +public suspend fun SimpleType.sample2(arg12: Int): Unit = sample2( + arg1 = with(arg12) {toString()} +) + +public suspend fun SimpleType.sample2(arg12: Int, arg2: Int): Unit = sample2( + arg1 = with(arg12) {toString()}, arg2 = arg2 +) + +public suspend fun SimpleType.sample2(arg12: Int, arg3: Boolean): Unit = sample2( + arg1 = with(arg12) {toString()}, arg3 = arg3 +) + +public suspend fun SimpleType.sample2( + arg12: Int, + arg2: Int, + arg3: Boolean, +): Unit = sample2( + arg1 = with(arg12) {toString()}, arg2 = arg2, arg3 = arg3 +) + +public suspend fun SimpleType.sample2(arg22: String): Unit = sample2( + arg2 = with(arg22) {toInt()} +) + +public suspend fun SimpleType.sample2(arg1: String, arg22: String): Unit = sample2( + arg1 = arg1, arg2 = with(arg22) {toInt()} +) + +public suspend fun SimpleType.sample2(arg22: String, arg3: Boolean): Unit = sample2( + arg2 = with(arg22) {toInt()}, arg3 = arg3 +) + +public suspend fun SimpleType.sample2( + arg1: String, + arg22: String, + arg3: Boolean, +): Unit = sample2( + arg1 = arg1, arg2 = with(arg22) {toInt()}, arg3 = arg3 +) + +public suspend fun SimpleType.sample2(arg12: Int, arg22: String): Unit = sample2( + arg12 = arg12, arg2 = with(arg22) {toInt()} +) + +public suspend fun SimpleType.sample2( + arg12: Int, + arg22: String, + arg3: Boolean, +): Unit = sample2( + arg12 = arg12, arg2 = with(arg22) {toInt()}, arg3 = arg3 +) diff --git a/ksp/variations/generator/test/src/commonMain/kotlin/SampleFun.kt b/ksp/variations/generator/test/src/commonMain/kotlin/SampleFun.kt new file mode 100644 index 00000000000..d3a6f87a2dc --- /dev/null +++ b/ksp/variations/generator/test/src/commonMain/kotlin/SampleFun.kt @@ -0,0 +1,52 @@ +package dev.inmo.micro_utils.ksp.variations.generator.test + +import dev.inmo.micro_utils.ksp.variations.GenerateVariations +import dev.inmo.micro_utils.ksp.variations.GenerationVariant + +data class SimpleType( + val value: String +) + +data class GenericType( + val value: T +) + +@GenerateVariations +fun sample( + @GenerationVariant( + SimpleType::class, + "value", + ) + @GenerationVariant( + GenericType::class, + "value.toString()", + genericTypes = arrayOf(Int::class) + ) + example: String = "12" +) = println(example) + +@GenerateVariations +fun sampleVararg( + @GenerationVariant( + SimpleType::class, + "value", + ) + vararg example: String = arrayOf("12") +) = println(example.joinToString()) + +@GenerateVariations +suspend fun SimpleType.sample2( + @GenerationVariant( + Int::class, + "toString()", + "arg12", + ) + arg1: String = "1", + @GenerationVariant( + String::class, + "toInt()", + "arg22", + ) + arg2: Int = 2, + arg3: Boolean = false +) = println(arg1) diff --git a/ksp/variations/generator/test/src/commonMain/kotlin/SampleGeneratedVariation.kt b/ksp/variations/generator/test/src/commonMain/kotlin/SampleGeneratedVariation.kt new file mode 100644 index 00000000000..01a2fb497f1 --- /dev/null +++ b/ksp/variations/generator/test/src/commonMain/kotlin/SampleGeneratedVariation.kt @@ -0,0 +1,15 @@ +// THIS CODE HAVE BEEN GENERATED AUTOMATICALLY +// TO REGENERATE IT JUST DELETE FILE +// ORIGINAL FILE: SampleFun.kt +package dev.inmo.micro_utils.ksp.variations.generator.test + +import kotlin.Int +import kotlin.Unit + +public fun sample(example: SimpleType): Unit = sample( + example = with(example) {value} +) + +public fun sample(example: GenericType): Unit = sample( + example = with(example) {value.toString()} +) diff --git a/ksp/variations/generator/test/src/commonMain/kotlin/SampleVarargGeneratedVariation.kt b/ksp/variations/generator/test/src/commonMain/kotlin/SampleVarargGeneratedVariation.kt new file mode 100644 index 00000000000..c79ce7b866a --- /dev/null +++ b/ksp/variations/generator/test/src/commonMain/kotlin/SampleVarargGeneratedVariation.kt @@ -0,0 +1,10 @@ +// THIS CODE HAVE BEEN GENERATED AUTOMATICALLY +// TO REGENERATE IT JUST DELETE FILE +// ORIGINAL FILE: SampleFun.kt +package dev.inmo.micro_utils.ksp.variations.generator.test + +import kotlin.Unit + +public fun sampleVararg(vararg example: SimpleType): Unit = sampleVararg( + example = with(example) {map { it.value }.toTypedArray()} +) diff --git a/ksp/variations/src/commonMain/kotlin/GenerateVariations.kt b/ksp/variations/src/commonMain/kotlin/GenerateVariations.kt new file mode 100644 index 00000000000..91122da4bee --- /dev/null +++ b/ksp/variations/src/commonMain/kotlin/GenerateVariations.kt @@ -0,0 +1,7 @@ +package dev.inmo.micro_utils.ksp.variations + +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.FUNCTION) +annotation class GenerateVariations( + val prefix: String = "" +) diff --git a/ksp/variations/src/commonMain/kotlin/GenerationVariant.kt b/ksp/variations/src/commonMain/kotlin/GenerationVariant.kt new file mode 100644 index 00000000000..da9130f089f --- /dev/null +++ b/ksp/variations/src/commonMain/kotlin/GenerationVariant.kt @@ -0,0 +1,18 @@ +package dev.inmo.micro_utils.ksp.variations + +import kotlin.reflect.KClass + +/** + * @param argName New argument name. Default - empty - means "use default arg name" + * @param type Qualified class name, like "dev.inmo.micro_utils.ksp.variants.GenerationVariant" + * @param conversion Conversion string with `this` + */ +@Retention(AnnotationRetention.BINARY) +@Repeatable +@Target(AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.VALUE_PARAMETER) +annotation class GenerationVariant( + val type: KClass<*>, + val conversion: String, + val argName: String = "", + vararg val genericTypes: KClass<*> +) diff --git a/ktor/client/src/jsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualTemporalUpload.kt b/ktor/client/src/jsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualTemporalUpload.kt index 74c34e3414a..0f6a6989ec0 100644 --- a/ktor/client/src/jsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualTemporalUpload.kt +++ b/ktor/client/src/jsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualTemporalUpload.kt @@ -2,7 +2,7 @@ package dev.inmo.micro_utils.ktor.client import dev.inmo.micro_utils.common.MPPFile import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob -import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions import dev.inmo.micro_utils.ktor.common.TemporalFileId import io.ktor.client.HttpClient import io.ktor.client.content.* @@ -27,7 +27,7 @@ suspend fun tempUpload( val request = XMLHttpRequest() request.responseType = XMLHttpRequestResponseType.TEXT request.upload.onprogress = { - subscope.launchSafelyWithoutExceptions { onUpload.onProgress(it.loaded.toLong(), it.total.toLong()) } + subscope.launchLoggingDropExceptions { onUpload.onProgress(it.loaded.toLong(), it.total.toLong()) } } request.onload = { if (request.status == 200.toShort()) { diff --git a/ktor/client/src/jsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualUniUpload.kt b/ktor/client/src/jsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualUniUpload.kt index bf1889a949f..4142f3f69a0 100644 --- a/ktor/client/src/jsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualUniUpload.kt +++ b/ktor/client/src/jsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualUniUpload.kt @@ -2,7 +2,7 @@ package dev.inmo.micro_utils.ktor.client import dev.inmo.micro_utils.common.MPPFile import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob -import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions import io.ktor.client.HttpClient import io.ktor.client.content.* import io.ktor.http.Headers @@ -66,7 +66,7 @@ actual suspend fun HttpClient.uniUpload( } request.responseType = XMLHttpRequestResponseType.TEXT request.upload.onprogress = { - subscope.launchSafelyWithoutExceptions { onUpload.onProgress(it.loaded.toLong(), it.total.toLong()) } + subscope.launchLoggingDropExceptions { onUpload.onProgress(it.loaded.toLong(), it.total.toLong()) } } request.onload = { if (request.status == 200.toShort()) { diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/TemporalFilesRoutingConfigurator.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/TemporalFilesRoutingConfigurator.kt index 08a85af4a04..ad35f0c5d00 100644 --- a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/TemporalFilesRoutingConfigurator.kt +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/TemporalFilesRoutingConfigurator.kt @@ -3,7 +3,7 @@ package dev.inmo.micro_utils.ktor.server import com.benasher44.uuid.uuid4 import dev.inmo.micro_utils.common.FileName import dev.inmo.micro_utils.common.MPPFile -import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions import dev.inmo.micro_utils.ktor.common.DefaultTemporalFilesSubPath import dev.inmo.micro_utils.ktor.common.TemporalFileId import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator @@ -44,7 +44,7 @@ class TemporalFilesRoutingConfigurator( filesMap: MutableMap, filesMutex: Mutex, onNewFileFlow: Flow - ): Job = scope.launchSafelyWithoutExceptions { + ): Job = scope.launchLoggingDropExceptions { while (currentCoroutineContext().isActive) { val filesWithCreationInfo = filesMap.mapNotNull { (fileId, file) -> fileId to ((Files.getAttribute(file.toPath(), "creationTime") as? FileTime) ?.toMillis() ?: return@mapNotNull null) diff --git a/pagination/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/pagination/QueryExtensions.kt b/pagination/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/pagination/QueryExtensions.kt index 616c28e0b7b..f16fb6dcbcb 100644 --- a/pagination/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/pagination/QueryExtensions.kt +++ b/pagination/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/pagination/QueryExtensions.kt @@ -2,19 +2,19 @@ package dev.inmo.micro_utils.pagination import org.jetbrains.exposed.sql.* -fun Query.paginate(with: Pagination, orderBy: Pair, SortOrder>? = null) = limit( - with.size, - with.firstIndex.toLong() -).let { - if (orderBy != null) { - it.orderBy( - orderBy.first, - orderBy.second - ) - } else { - it +fun Query.paginate(with: Pagination, orderBy: Pair, SortOrder>? = null) = + limit(with.size) + .offset(with.firstIndex.toLong()) + .let { + if (orderBy != null) { + it.orderBy( + orderBy.first, + orderBy.second + ) + } else { + it + } } -} fun Query.paginate(with: Pagination, orderBy: Expression<*>?, reversed: Boolean = false) = paginate( with, diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt index d163b968f9d..e5b6bbd7188 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullCRUDCacheRepo.kt @@ -2,7 +2,7 @@ package dev.inmo.micro_utils.repos.cache.full import dev.inmo.micro_utils.common.* import dev.inmo.micro_utils.coroutines.SmartRWLocker -import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions import dev.inmo.micro_utils.coroutines.withReadAcquire import dev.inmo.micro_utils.coroutines.withWriteLock import dev.inmo.micro_utils.pagination.Pagination @@ -116,7 +116,7 @@ open class FullCRUDCacheRepo( CRUDRepo { init { if (!skipStartInvalidate) { - scope.launchSafelyWithoutExceptions { + scope.launchLoggingDropExceptions { if (locker.writeMutex.isLocked) { initialInvalidate() } else { diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt index 7defa718495..84697a5a79c 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValueCacheRepo.kt @@ -2,7 +2,7 @@ package dev.inmo.micro_utils.repos.cache.full import dev.inmo.micro_utils.common.* import dev.inmo.micro_utils.coroutines.SmartRWLocker -import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions import dev.inmo.micro_utils.coroutines.withReadAcquire import dev.inmo.micro_utils.coroutines.withWriteLock import dev.inmo.micro_utils.pagination.Pagination @@ -141,7 +141,7 @@ open class FullKeyValueCacheRepo( ) { init { if (!skipStartInvalidate) { - scope.launchSafelyWithoutExceptions { + scope.launchLoggingDropExceptions { if (locker.writeMutex.isLocked) { initialInvalidate() } else { diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt index c68e238d27d..ec146a26eff 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt @@ -2,7 +2,7 @@ package dev.inmo.micro_utils.repos.cache.full import dev.inmo.micro_utils.common.* import dev.inmo.micro_utils.coroutines.SmartRWLocker -import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions import dev.inmo.micro_utils.coroutines.withReadAcquire import dev.inmo.micro_utils.coroutines.withWriteLock import dev.inmo.micro_utils.pagination.* @@ -210,7 +210,7 @@ open class FullKeyValuesCacheRepo( WriteKeyValuesRepo by parentRepo { init { if (!skipStartInvalidate) { - scope.launchSafelyWithoutExceptions { + scope.launchLoggingDropExceptions { if (locker.writeMutex.isLocked) { initialInvalidate() } else { diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullCRUDCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullCRUDCacheRepo.kt index 1a39813ce40..273728e8f9e 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullCRUDCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullCRUDCacheRepo.kt @@ -2,7 +2,7 @@ package dev.inmo.micro_utils.repos.cache.full.direct import dev.inmo.micro_utils.common.* import dev.inmo.micro_utils.coroutines.SmartRWLocker -import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions import dev.inmo.micro_utils.coroutines.withReadAcquire import dev.inmo.micro_utils.coroutines.withWriteLock import dev.inmo.micro_utils.pagination.Pagination @@ -82,7 +82,7 @@ open class DirectFullCRUDCacheRepo( CRUDRepo { init { if (!skipStartInvalidate) { - scope.launchSafelyWithoutExceptions { + scope.launchLoggingDropExceptions { if (locker.writeMutex.isLocked) { initialInvalidate() } else { diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullKeyValueCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullKeyValueCacheRepo.kt index 41b0f66630b..07830395400 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullKeyValueCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullKeyValueCacheRepo.kt @@ -1,7 +1,7 @@ package dev.inmo.micro_utils.repos.cache.full.direct import dev.inmo.micro_utils.coroutines.SmartRWLocker -import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions import dev.inmo.micro_utils.coroutines.withReadAcquire import dev.inmo.micro_utils.coroutines.withWriteLock import dev.inmo.micro_utils.pagination.Pagination @@ -117,7 +117,7 @@ open class DirectFullKeyValueCacheRepo( DirectFullReadKeyValueCacheRepo(parentRepo, kvCache, locker) { init { if (!skipStartInvalidate) { - scope.launchSafelyWithoutExceptions { + scope.launchLoggingDropExceptions { if (locker.writeMutex.isLocked) { initialInvalidate() } else { diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullKeyValuesCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullKeyValuesCacheRepo.kt index a16d3700c9d..e9fd4dbafef 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullKeyValuesCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/direct/DirectFullKeyValuesCacheRepo.kt @@ -2,7 +2,7 @@ package dev.inmo.micro_utils.repos.cache.full.direct import dev.inmo.micro_utils.common.* import dev.inmo.micro_utils.coroutines.SmartRWLocker -import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions import dev.inmo.micro_utils.coroutines.withReadAcquire import dev.inmo.micro_utils.coroutines.withWriteLock import dev.inmo.micro_utils.pagination.* @@ -145,7 +145,7 @@ open class DirectFullKeyValuesCacheRepo( WriteKeyValuesRepo by parentRepo { init { if (!skipStartInvalidate) { - scope.launchSafelyWithoutExceptions { + scope.launchLoggingDropExceptions { if (locker.writeMutex.isLocked) { initialInvalidate() } else { diff --git a/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/AbstractExposedWriteCRUDRepo.kt b/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/AbstractExposedWriteCRUDRepo.kt index 6126720eb93..c1450e972f3 100644 --- a/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/AbstractExposedWriteCRUDRepo.kt +++ b/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/AbstractExposedWriteCRUDRepo.kt @@ -137,7 +137,7 @@ abstract class AbstractExposedWriteCRUDRepo( override suspend fun deleteById(ids: List) { onBeforeDelete(ids) transaction(db = database) { - val deleted = deleteWhere(null, null) { selectByIds(it, ids) } + val deleted = deleteWhere { selectByIds(it, ids) } if (deleted == ids.size) { ids } else { diff --git a/settings.gradle b/settings.gradle index 3c8ea281c61..6379141d8cb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -62,6 +62,10 @@ String[] includes = [ ":ksp:classcasts:generator", ":ksp:classcasts:generator:test", + ":ksp:variations", + ":ksp:variations:generator", + ":ksp:variations:generator:test", + ":dokka" ]