commit f15c03fa2b7c9ac8fb08f226a54d6b8fdba2a216 Author: Andrew Golovashevich Date: Sat Mar 22 14:29:02 2025 +0300 v0.4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f2e4e0d --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/.idea/ +gradle/ +.gradle/ +build/ +*.class +*.jar +/out/ +/gradlew* +.kotlin/ \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..687fa55 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,36 @@ +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion +import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.* +import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.plugin.xomrk + +buildscript { + repositories { + mavenCentral() + maven("https://maven.landgrafhomyak.ru/") + } + + dependencies { + classpath("ru.landgrafhomyak.kotlin:kotlin-mpp-gradle-build:v0.3k2.1.10") + } +} + +group = "ru.landgrafhomyak.utility" +version = "0.4" + +repositories { + mavenCentral() +} + +xomrk { + kotlin { + setCompatibilityWithKotlin(KotlinVersion.KOTLIN_2_0) + optInContracts() + + defineAllMultiplatformTargets() + } + + publishing { + repositories { + defineXomrkGiteaMavenRepo() + } + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..7fc6f1f --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..55661f8 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "highlevel-try-finally" \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/utility/highlevel_try_finally/TryFinallyChainScope.kt b/src/commonMain/kotlin/ru/landgrafhomyak/utility/highlevel_try_finally/TryFinallyChainScope.kt new file mode 100644 index 0000000..91c2ced --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/utility/highlevel_try_finally/TryFinallyChainScope.kt @@ -0,0 +1,25 @@ +package ru.landgrafhomyak.utility.highlevel_try_finally + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +class TryFinallyChainScope @PublishedApi internal constructor() { + @PublishedApi + internal var _actualException: Throwable? = null + + @PublishedApi + internal fun _throw() { + this._actualException?.let { e -> throw e } + } + + inline fun action(fn: () -> Unit) { + contract { + callsInPlace(fn, InvocationKind.EXACTLY_ONCE) + } + safeAutoClose3e( + onCrossReturn = { throw Error("Cross return not allowed in tryFinallyChain{action{}}") }, + onError = { err -> this._actualException?.addSuppressed(err) ?: run { this._actualException = err } }, + action = fn + ) + } +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/utility/highlevel_try_finally/safe_autoclose.kt b/src/commonMain/kotlin/ru/landgrafhomyak/utility/highlevel_try_finally/safe_autoclose.kt new file mode 100644 index 0000000..048ae74 --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/utility/highlevel_try_finally/safe_autoclose.kt @@ -0,0 +1,101 @@ +@file:Suppress("unused") +@file:JvmName("SafeAutocloseKt") + +package ru.landgrafhomyak.utility.highlevel_try_finally + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlin.jvm.JvmName + +@Suppress("WRONG_INVOCATION_KIND") +inline fun safeAutoClose1( + finally: () -> Unit, + action: () -> R +): R { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + callsInPlace(finally, InvocationKind.EXACTLY_ONCE) + } + return safeAutoClose3(onError = finally, onSuccess = finally, onCrossReturn = finally, action = action) +} + +@Suppress("WRONG_INVOCATION_KIND") +inline fun safeAutoClose2( + onError: () -> Unit = {}, + onSuccess: () -> Unit = {}, + action: () -> R +): R { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + callsInPlace(onError, InvocationKind.AT_MOST_ONCE) + callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE) + } + return safeAutoClose3(onError = onError, onSuccess = onSuccess, onCrossReturn = onSuccess, action = action) +} + +@Suppress("WRONG_INVOCATION_KIND") +inline fun safeAutoClose2e( + onError: (Throwable) -> Unit = { _ -> }, + onSuccess: () -> Unit = {}, + action: () -> R +): R { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + callsInPlace(onError, InvocationKind.AT_MOST_ONCE) + callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE) + } + return safeAutoClose3e(onError = onError, onSuccess = onSuccess, onCrossReturn = onSuccess, action = action) +} + +inline fun safeAutoClose3( + onError: () -> Unit = {}, + onSuccess: () -> Unit = {}, + onCrossReturn: () -> Unit = {}, + action: () -> R +): R { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + callsInPlace(onError, InvocationKind.AT_MOST_ONCE) + callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE) + callsInPlace(onCrossReturn, InvocationKind.AT_MOST_ONCE) + } + return safeAutoClose3e(onError = { t -> onError() }, onSuccess = onSuccess, onCrossReturn = onCrossReturn, action = action) +} + +inline fun safeAutoClose3e( + onError: (Throwable) -> Unit = { _ -> }, + onSuccess: () -> Unit = {}, + onCrossReturn: () -> Unit = {}, + action: () -> R +): R { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + callsInPlace(onError, InvocationKind.AT_MOST_ONCE) + callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE) + callsInPlace(onCrossReturn, InvocationKind.AT_MOST_ONCE) + } + + val ret: R + var wasError = false + var crossReturned = true + try { + ret = action() + crossReturned = false + } catch (e1: Throwable) { + wasError = true + try { + onError(e1) + } catch (e2: Throwable) { + e1.addSuppressed(e2) + } + throw e1 + } finally { + if (!wasError) { + if (crossReturned) + onCrossReturn() + else + onSuccess() + } + } + return ret +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/utility/highlevel_try_finally/try_finally_chain.kt b/src/commonMain/kotlin/ru/landgrafhomyak/utility/highlevel_try_finally/try_finally_chain.kt new file mode 100644 index 0000000..684d568 --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/utility/highlevel_try_finally/try_finally_chain.kt @@ -0,0 +1,22 @@ +@file:Suppress("unused") +@file:JvmName("TryFinallyChainKt") + +package ru.landgrafhomyak.utility.highlevel_try_finally + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlin.jvm.JvmName + +inline fun tryFinallyChain(chains: TryFinallyChainScope.() -> Unit) { + contract { + callsInPlace(chains, InvocationKind.EXACTLY_ONCE) + } + + val scope = TryFinallyChainScope() + safeAutoClose3e( + onCrossReturn = { throw Error("Cross return not allowed in tryFinallyChain{}") }, + onError = { err -> throw Error("Unexpected exception in tryFinallyChain{}; calling anything outside action{} block isn't allowed", err) }, + onSuccess = { scope._throw() }, + action = { chains(scope) } + ) +} \ No newline at end of file