diff --git a/example/src/main/kotlin/com/github/michaelbull/result/example/service/CustomerService.kt b/example/src/main/kotlin/com/github/michaelbull/result/example/service/CustomerService.kt index f1420cc..34b4d46 100644 --- a/example/src/main/kotlin/com/github/michaelbull/result/example/service/CustomerService.kt +++ b/example/src/main/kotlin/com/github/michaelbull/result/example/service/CustomerService.kt @@ -15,6 +15,7 @@ import com.github.michaelbull.result.map import com.github.michaelbull.result.mapAll import com.github.michaelbull.result.mapBoth import com.github.michaelbull.result.mapError +import com.github.michaelbull.result.runCatching import com.github.michaelbull.result.toResultOr import java.sql.SQLTimeoutException @@ -22,7 +23,7 @@ object CustomerService { private val repository = InMemoryCustomerRepository() fun getAll(): Result, DomainMessage> { - return Result.of(repository::findAll) + return runCatching(repository::findAll) .mapError(::exceptionToDomainMessage) .mapAll(Customer.Companion::from) } @@ -40,12 +41,12 @@ object CustomerService { } private fun updateCustomer(entity: CustomerEntity, old: Customer, new: Customer) = - Result.of { repository.update(entity) } + runCatching { repository.update(entity) } .map { differenceBetween(old, new) } .mapError(::exceptionToDomainMessage) private fun createCustomer(entity: CustomerEntity) = - Result.of { repository.insert(entity) } + runCatching { repository.insert(entity) } .map { CustomerCreated } .mapError(::exceptionToDomainMessage) diff --git a/src/main/kotlin/com/github/michaelbull/result/Factory.kt b/src/main/kotlin/com/github/michaelbull/result/Factory.kt new file mode 100644 index 0000000..3eeaec2 --- /dev/null +++ b/src/main/kotlin/com/github/michaelbull/result/Factory.kt @@ -0,0 +1,53 @@ +package com.github.michaelbull.result + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +/** + * Calls the specified function [block] and returns its encapsulated result if + * invocation was successful, catching and encapsulating any thrown exception + * as a failure. + */ +inline fun runCatching(block: () -> V): Result { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + + return try { + Ok(block()) + } catch (e: Throwable) { + Err(e) + } +} + +/** + * Calls the specified function [block] with `this` value as its receiver and + * returns its encapsulated result if invocation was successful, catching and + * encapsulating any thrown exception as a failure. + */ +inline infix fun T.runCatching(block: T.() -> V): Result { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + + return try { + Ok(block()) + } catch (e: Throwable) { + Err(e) + } +} + +/** + * Converts a nullable of type [V] to a [Result]. Returns [Ok] if the value is + * non-null, otherwise the supplied [error]. + */ +inline infix fun V?.toResultOr(error: () -> E): Result { + contract { + callsInPlace(error, InvocationKind.AT_MOST_ONCE) + } + + return when (this) { + null -> Err(error()) + else -> Ok(this) + } +} diff --git a/src/main/kotlin/com/github/michaelbull/result/Result.kt b/src/main/kotlin/com/github/michaelbull/result/Result.kt index 6958400..9956591 100644 --- a/src/main/kotlin/com/github/michaelbull/result/Result.kt +++ b/src/main/kotlin/com/github/michaelbull/result/Result.kt @@ -1,8 +1,5 @@ package com.github.michaelbull.result -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract - /** * [Result] is a type that represents either success ([Ok]) or failure ([Err]). * @@ -17,6 +14,7 @@ sealed class Result { * Invokes a [function] and wraps it in a [Result], returning an [Err] * if an [Exception] was thrown, otherwise [Ok]. */ + @Deprecated("Use top-level runCatching instead", ReplaceWith("runCatching(function)")) inline fun of(function: () -> V): Result { return try { Ok(function.invoke()) @@ -36,18 +34,3 @@ data class Ok(val value: V) : Result() * Represents a failed [Result], containing an [error]. */ data class Err(val error: E) : Result() - -/** - * Converts a nullable of type [V] to a [Result]. Returns [Ok] if the value is - * non-null, otherwise the supplied [error]. - */ -inline infix fun V?.toResultOr(error: () -> E): Result { - contract { - callsInPlace(error, InvocationKind.AT_MOST_ONCE) - } - - return when (this) { - null -> Err(error()) - else -> Ok(this) - } -} diff --git a/src/test/kotlin/com/github/michaelbull/result/ResultTest.kt b/src/test/kotlin/com/github/michaelbull/result/FactoryTest.kt similarity index 89% rename from src/test/kotlin/com/github/michaelbull/result/ResultTest.kt rename to src/test/kotlin/com/github/michaelbull/result/FactoryTest.kt index b6bdb6f..c23ede9 100644 --- a/src/test/kotlin/com/github/michaelbull/result/ResultTest.kt +++ b/src/test/kotlin/com/github/michaelbull/result/FactoryTest.kt @@ -4,12 +4,12 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertSame -class ResultTest { - class Of { +class FactoryTest { + class RunCatching { @Test fun returnsOkIfInvocationSuccessful() { val callback = { "example" } - val result = Result.of(callback) + val result = runCatching(callback) assertEquals( expected = "example", @@ -21,7 +21,7 @@ class ResultTest { fun returnsErrIfInvocationFails() { val exception = IllegalArgumentException("throw me") val callback = { throw exception } - val result = Result.of(callback) + val result = runCatching(callback) assertSame( expected = exception,