From fc00457c4b1320906fd98a27928d4dc1a9dcdfa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernd=20Pr=C3=BCnster?= Date: Fri, 25 Oct 2024 07:44:05 +0200 Subject: [PATCH] 1.9.0: More plain Result features --- .github/workflows/build-jvm.yml | 4 +-- CHANGELOG.md | 9 ++++- README.md | 7 ++-- gradle.properties | 2 +- kmmresult-test/build.gradle.kts | 15 +++++--- kmmresult/build.gradle.kts | 14 +++++--- .../kotlin/at/asitplus/KmmResult.kt | 36 +++++++++++++++---- .../commonMain/kotlin/at/asitplus/NonFatal.kt | 12 +++++-- .../src/commonTest/kotlin/KmmResultTest.kt | 11 +++++- 9 files changed, 84 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build-jvm.yml b/.github/workflows/build-jvm.yml index cd9b378..3403f55 100644 --- a/.github/workflows/build-jvm.yml +++ b/.github/workflows/build-jvm.yml @@ -1,8 +1,8 @@ -name: Build JVM/JS/iOS/macOS/tvOS/MinGW/Linux +name: Build JVM/JS/iOS/macOS/tvOS/MinGW/wasmJS/wasmWasi/Linux on: [push] jobs: build: - runs-on: ubuntu-latest + runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dd00d0..a8b4522 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,4 +55,11 @@ - `result.shouldSucceed()` returning the contained value - remove Arrow dependency and import arrow's list of Fatal exceptions directly into our code - Introduce `Result.nonFatalOrThrow` to mimic KmmResult's non-fatal-only behaviour, but without the object instantiation overhead -- Introduce `carchingUnwrapped`, which mimics KmmResult's non-fatal-only behaviour, but without the object instantiation overhead \ No newline at end of file +- Introduce `carchingUnwrapped`, which mimics KmmResult's non-fatal-only behaviour, but without the object instantiation overhead + +## 1.9.0 +- add WasmJS target +- add WasmWasi target +- add `wrappingPlain`, which works just like `wrapping` but on a `Result` rather than a `KmmResult` to avoid instantiation overhead +- rename `catchingUnwrapped` to `catchingPlain` to avoid confusions with `wrapping` but keep the old name as deprecated alternative +- add `nonFatalOrThrow` diff --git a/README.md b/README.md index 38a76b4..99d265d 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ is *not* a value class (a sealed `Either` type also does not interop well with S `KmmResult` comes to the rescue! → [Full documentation](https://a-sit-plus.github.io/KmmResult/). - ## Using in your Projects This library is available at maven central. @@ -88,12 +87,12 @@ KmmResult comes with `catching`. This is a non-fatal-only-catching version of st It re-throws any fatal exceptions, such as `OutOfMemoryError`. The underlying logic is borrowed from [Arrow's](https://arrow-kt.io)'s [`nonFatalOrThrow`](https://apidocs.arrow-kt.io/arrow-core/arrow.core/non-fatal-or-throw.html). -The only downside of `catching` is that it incurs instatiation overhead, because it creates a `KmmResult` instance. +The only downside of `catching` is that it incurs instantiation overhead, because it creates a `KmmResult` instance. Internally, though, only the behaviour is important, not Swift interop. Hence, you don't care for a `KmmResult` and you certainly don't care for the cost of instantiating an object. Here, the `Result.nonFatalOrThrow()` extension shipped with KmmResult comes to the rescue. It does exactly what the name suggest: It re-throws any fatal exception and leaved the `Result` object -untouched otherwise. - +untouched otherwise. +In addition, there's `catchingPlain` which directly returns an stdlib `Result` Happy folding! diff --git a/gradle.properties b/gradle.properties index 9dd40b5..3f4a648 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,5 +3,5 @@ kotlin.mpp.enableCInteropCommonization=true kotlin.mpp.stability.nowarn=true kotlin.native.ignoreDisabledTargets=true -artifactVersion = 1.8.0 +artifactVersion = 1.9.0 org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled \ No newline at end of file diff --git a/kmmresult-test/build.gradle.kts b/kmmresult-test/build.gradle.kts index 871c252..f56d58d 100644 --- a/kmmresult-test/build.gradle.kts +++ b/kmmresult-test/build.gradle.kts @@ -1,6 +1,5 @@ import io.gitlab.arturbosch.detekt.Detekt -import org.gradle.kotlin.dsl.support.listFilesOrdered -import java.lang.management.ManagementFactory +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import java.net.URI plugins { @@ -86,10 +85,16 @@ kotlin { withJava() //for Java Interop tests } - js(IR) { - browser { testTask { enabled = false } } - nodejs() + listOf( + js(IR).apply { browser { testTask { enabled = false } } }, + @OptIn(ExperimentalWasmDsl::class) + wasmJs().apply { browser { testTask { enabled = false } } }, + @OptIn(ExperimentalWasmDsl::class) + wasmWasi() + ).forEach { + it.nodejs() } + linuxX64() linuxArm64() mingwX64() diff --git a/kmmresult/build.gradle.kts b/kmmresult/build.gradle.kts index c139fd6..b20e9e2 100644 --- a/kmmresult/build.gradle.kts +++ b/kmmresult/build.gradle.kts @@ -1,4 +1,5 @@ import io.gitlab.arturbosch.detekt.Detekt +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import java.net.URI plugins { @@ -78,7 +79,6 @@ kotlin { it.binaries.framework { baseName = "KmmResult" binaryOption("bundleId", "at.asitplus.KmmResult") - embedBitcode("bitcode") xcf.add(this) isStatic = true } @@ -96,9 +96,15 @@ kotlin { withJava() //for Java Interop tests } - js(IR) { - browser { testTask { enabled = false } } - nodejs() + + listOf( + js(IR).apply { browser { testTask { enabled = false } } }, + @OptIn(ExperimentalWasmDsl::class) + wasmJs().apply { browser { testTask { enabled = false } } }, + @OptIn(ExperimentalWasmDsl::class) + wasmWasi() + ).forEach { + it.nodejs() } linuxX64() diff --git a/kmmresult/src/commonMain/kotlin/at/asitplus/KmmResult.kt b/kmmresult/src/commonMain/kotlin/at/asitplus/KmmResult.kt index 6c59566..bcb04fe 100644 --- a/kmmresult/src/commonMain/kotlin/at/asitplus/KmmResult.kt +++ b/kmmresult/src/commonMain/kotlin/at/asitplus/KmmResult.kt @@ -7,6 +7,7 @@ package at.asitplus +import at.asitplus.KmmResult.Companion.wrap import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.experimental.ExperimentalObjCName @@ -311,16 +312,26 @@ inline fun T.catching(block: T.() -> R): KmmResult { * Usage: `wrapping(asA = ::ThrowableType) { block }`. */ @Suppress("TooGenericExceptionCaught") -inline fun wrapping(asA: (String?, Throwable) -> E, block: () -> R): KmmResult { +inline fun wrapping(asA: (String?, Throwable) -> E, block: () -> R): KmmResult = + wrappingPlain(asA, block).wrap() + +/** + * Runs the specified function [block], returning a [Result]. + * Any non-fatal exception will be wrapped as the specified exception, unless it is already the specified type. + * + * Usage: `wrapping(asA = ::ThrowableType) { block }`. + */ +@Suppress("TooGenericExceptionCaught") +inline fun wrappingPlain(asA: (String?, Throwable) -> E, block: () -> R): Result { contract { callsInPlace(asA, InvocationKind.AT_MOST_ONCE) // not EXACTLY_ONCE, because inside a try block! callsInPlace(block, InvocationKind.AT_MOST_ONCE) } return try { - KmmResult.success(block()) + Result.success(block()) } catch (e: Throwable) { - KmmResult.failure( + Result.failure( when (e.nonFatalOrThrow()) { is E -> e else -> asA(e.message, e) @@ -329,6 +340,7 @@ inline fun wrapping(asA: (String?, Throwable) -> E, b } } + /** * Runs the specified function [block] with `this` as its receiver, returning a [KmmResult]. * Any non-fatal exception will be wrapped as the specified exception, unless it is already the specified type. @@ -336,16 +348,28 @@ inline fun wrapping(asA: (String?, Throwable) -> E, b * Usage: `wrapping(asA = ::ThrowableType) { block }`. */ @Suppress("TooGenericExceptionCaught") -inline fun T.wrapping(asA: (String?, Throwable) -> E, block: T.() -> R): KmmResult { +inline fun T.wrapping( + asA: (String?, Throwable) -> E, + block: T.() -> R +): KmmResult = this.wrappingPlain(asA, block).wrap() + +/** + * Runs the specified function [block] with `this` as its receiver, returning a [KmmResult]. + * Any non-fatal exception will be wrapped as the specified exception, unless it is already the specified type. + * + * Usage: `wrapping(asA = ::ThrowableType) { block }`. + */ +@Suppress("TooGenericExceptionCaught") +inline fun T.wrappingPlain(asA: (String?, Throwable) -> E, block: T.() -> R): Result { contract { callsInPlace(asA, InvocationKind.AT_MOST_ONCE) // not EXACTLY_ONCE, because inside a try block! callsInPlace(block, InvocationKind.AT_MOST_ONCE) } return try { - KmmResult.success(block()) + Result.success(block()) } catch (e: Throwable) { - KmmResult.failure( + Result.failure( when (e.nonFatalOrThrow()) { is E -> e else -> asA(e.message, e) diff --git a/kmmresult/src/commonMain/kotlin/at/asitplus/NonFatal.kt b/kmmresult/src/commonMain/kotlin/at/asitplus/NonFatal.kt index 767bee6..13c2f63 100644 --- a/kmmresult/src/commonMain/kotlin/at/asitplus/NonFatal.kt +++ b/kmmresult/src/commonMain/kotlin/at/asitplus/NonFatal.kt @@ -24,7 +24,11 @@ inline fun Result.nonFatalOrThrow(): Result = this.onFailure { it.nonF * logic to avoid a dependency on Arrow for a single function. */ @Suppress("NOTHING_TO_INLINE") -inline fun catchingUnwrapped(block: () -> T): Result = runCatching(block).nonFatalOrThrow() +inline fun catchingPlain(block: () -> T): Result = runCatching(block).nonFatalOrThrow() + +@Suppress("NOTHING_TO_INLINE") +@Deprecated("This function was badly named", ReplaceWith("catchingPlain(block)")) +inline fun catchingUnwrapped(block: () -> T): Result = catchingPlain(block) /** * Non-fatal-only-catching version of stdlib's [runCatching] (calling the specified function [block] with `this` value @@ -34,4 +38,8 @@ inline fun catchingUnwrapped(block: () -> T): Result = runCatching(block) * logic to avoid a dependency on Arrow for a single function. */ @Suppress("NOTHING_TO_INLINE") -inline fun T.catchingUnwrapped(block: T.() -> R): Result = runCatching(block).nonFatalOrThrow() +inline fun T.catchingPlain(block: T.() -> R): Result = runCatching(block).nonFatalOrThrow() + +@Suppress("NOTHING_TO_INLINE") +@Deprecated("This function was badly named", ReplaceWith("catchingPlain { }")) +inline fun T.catchingUnwrapped(block: T.() -> R): Result = catchingPlain(block) diff --git a/kmmresult/src/commonTest/kotlin/KmmResultTest.kt b/kmmresult/src/commonTest/kotlin/KmmResultTest.kt index 507c914..7989b09 100644 --- a/kmmresult/src/commonTest/kotlin/KmmResultTest.kt +++ b/kmmresult/src/commonTest/kotlin/KmmResultTest.kt @@ -228,9 +228,18 @@ class KmmResultTest { assertFailsWith(CancellationException::class) { catching { throw CancellationException() } } + assertFailsWith(CancellationException::class) { + "Receiver".catching { throw CancellationException() } + } + + assertFailsWith(CancellationException::class) { + catchingPlain { throw CancellationException() } + } + assertFailsWith(CancellationException::class) { + "Receiver".catchingPlain { throw CancellationException() } + } runCatching { throw IndexOutOfBoundsException() }.nonFatalOrThrow() catching { throw IndexOutOfBoundsException() } - } }