From 54b53d36e64fbd3d47524cb748ca5f40b3ef6a26 Mon Sep 17 00:00:00 2001 From: Michael Bull Date: Tue, 6 Feb 2024 13:29:05 +0000 Subject: [PATCH] Extract recover functions to separate file --- .../com/github/michaelbull/result/And.kt | 61 ---- .../com/github/michaelbull/result/Or.kt | 76 ----- .../com/github/michaelbull/result/Recover.kt | 141 +++++++++ .../com/github/michaelbull/result/AndTest.kt | 118 -------- .../com/github/michaelbull/result/OrTest.kt | 148 ---------- .../github/michaelbull/result/RecoverTest.kt | 274 ++++++++++++++++++ 6 files changed, 415 insertions(+), 403 deletions(-) create mode 100644 kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Recover.kt create mode 100644 kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/RecoverTest.kt diff --git a/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/And.kt b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/And.kt index 5cff8f5..e4881e9 100644 --- a/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/And.kt +++ b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/And.kt @@ -41,64 +41,3 @@ public inline infix fun Result.andThen(transform: (V) -> Result< is Err -> this } } - -/** - * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err], - * otherwise this [Result]. - */ -public inline fun Result.andThenRecover(transform: (E) -> Result): Result { - contract { - callsInPlace(transform, InvocationKind.AT_MOST_ONCE) - } - - return when (this) { - is Ok -> this - is Err -> transform(error) - } -} - -/** - * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err] and - * satisfies the given [predicate], otherwise this [Result]. - */ -public inline fun Result.andThenRecoverIf( - predicate: (E) -> Boolean, - transform: (E) -> Result -): Result { - contract { - callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) - callsInPlace(transform, InvocationKind.AT_MOST_ONCE) - } - - return when (this) { - is Ok -> this - is Err -> if (predicate(error)) { - transform(error) - } else { - this - } - } -} - -/** - * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err] - * and _does not_ satisfy the given [predicate], otherwise this [Result]. - */ -public inline fun Result.andThenRecoverUnless( - predicate: (E) -> Boolean, - transform: (E) -> Result -): Result { - contract { - callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) - callsInPlace(transform, InvocationKind.AT_MOST_ONCE) - } - - return when (this) { - is Ok -> this - is Err -> if (!predicate(error)) { - transform(error) - } else { - this - } - } -} diff --git a/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Or.kt b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Or.kt index b6d6ed7..6469b8f 100644 --- a/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Or.kt +++ b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Or.kt @@ -51,82 +51,6 @@ public fun Result.orElseThrow(): Ok { } } -/** - * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err], - * otherwise this [Ok]. - */ -public inline infix fun Result.recover(transform: (E) -> V): Ok { - contract { - callsInPlace(transform, InvocationKind.AT_MOST_ONCE) - } - - return when (this) { - is Ok -> this - is Err -> Ok(transform(error)) - } -} - -/** - * Returns the [transformation][transform] of the [error][Err.error], catching and encapsulating any - * thrown exception as a failure if this [Result] is [Err], otherwise this [Ok]. - */ -public inline infix fun Result.recoverCatching(transform: (E) -> V): Result { - contract { - callsInPlace(transform, InvocationKind.AT_MOST_ONCE) - } - - return when (this) { - is Ok -> this - is Err -> { - try { - Ok(transform(error)) - } catch (e: Throwable) { - Err(e) - } - } - } -} - -/** - * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err] - * and satisfies the given [predicate], otherwise this [Result]. - */ -public inline fun Result.recoverIf(predicate: (E) -> Boolean, transform: (E) -> V): Result { - contract { - callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) - callsInPlace(transform, InvocationKind.AT_MOST_ONCE) - } - - return when (this) { - is Ok -> this - is Err -> if (predicate(error)) { - Ok(transform(error)) - } else { - this - } - } -} - -/** - * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err] - * and _does not_ satisfy the given [predicate], otherwise this [Result]. - */ -public inline fun Result.recoverUnless(predicate: (E) -> Boolean, transform: (E) -> V): Result { - contract { - callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) - callsInPlace(transform, InvocationKind.AT_MOST_ONCE) - } - - return when (this) { - is Ok -> this - is Err -> if (!predicate(error)) { - Ok(transform(error)) - } else { - this - } - } -} - /** * Throws the [error][Err.error] if this [Result] is an [Err] and satisfies the given * [predicate], otherwise returns this [Result]. diff --git a/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Recover.kt b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Recover.kt new file mode 100644 index 0000000..72756ec --- /dev/null +++ b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Recover.kt @@ -0,0 +1,141 @@ +package com.github.michaelbull.result + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +/** + * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err], + * otherwise this [Ok]. + */ +public inline infix fun Result.recover(transform: (E) -> V): Ok { + contract { + callsInPlace(transform, InvocationKind.AT_MOST_ONCE) + } + + return when (this) { + is Ok -> this + is Err -> Ok(transform(error)) + } +} + +/** + * Returns the [transformation][transform] of the [error][Err.error], catching and encapsulating any + * thrown exception as a failure if this [Result] is [Err], otherwise this [Ok]. + */ +public inline infix fun Result.recoverCatching(transform: (E) -> V): Result { + contract { + callsInPlace(transform, InvocationKind.AT_MOST_ONCE) + } + + return when (this) { + is Ok -> this + is Err -> { + try { + Ok(transform(error)) + } catch (e: Throwable) { + Err(e) + } + } + } +} + +/** + * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err] + * and satisfies the given [predicate], otherwise this [Result]. + */ +public inline fun Result.recoverIf(predicate: (E) -> Boolean, transform: (E) -> V): Result { + contract { + callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.AT_MOST_ONCE) + } + + return when (this) { + is Ok -> this + is Err -> if (predicate(error)) { + Ok(transform(error)) + } else { + this + } + } +} + +/** + * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err] + * and _does not_ satisfy the given [predicate], otherwise this [Result]. + */ +public inline fun Result.recoverUnless(predicate: (E) -> Boolean, transform: (E) -> V): Result { + contract { + callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.AT_MOST_ONCE) + } + + return when (this) { + is Ok -> this + is Err -> if (!predicate(error)) { + Ok(transform(error)) + } else { + this + } + } +} + +/** + * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err], + * otherwise this [Result]. + */ +public inline fun Result.andThenRecover(transform: (E) -> Result): Result { + contract { + callsInPlace(transform, InvocationKind.AT_MOST_ONCE) + } + + return when (this) { + is Ok -> this + is Err -> transform(error) + } +} + +/** + * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err] and + * satisfies the given [predicate], otherwise this [Result]. + */ +public inline fun Result.andThenRecoverIf( + predicate: (E) -> Boolean, + transform: (E) -> Result +): Result { + contract { + callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.AT_MOST_ONCE) + } + + return when (this) { + is Ok -> this + is Err -> if (predicate(error)) { + transform(error) + } else { + this + } + } +} + +/** + * Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err] + * and _does not_ satisfy the given [predicate], otherwise this [Result]. + */ +public inline fun Result.andThenRecoverUnless( + predicate: (E) -> Boolean, + transform: (E) -> Result +): Result { + contract { + callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.AT_MOST_ONCE) + } + + return when (this) { + is Ok -> this + is Err -> if (!predicate(error)) { + transform(error) + } else { + this + } + } +} diff --git a/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/AndTest.kt b/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/AndTest.kt index 6305408..4f80d54 100644 --- a/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/AndTest.kt +++ b/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/AndTest.kt @@ -42,122 +42,4 @@ class AndTest { ) } } - - class AndThenRecover { - @Test - fun returnsValueIfOk() { - assertEquals( - expected = 5, - actual = Ok(5).andThenRecover { Ok(7) }.get() - ) - } - - @Test - fun returnsTransformValueIfErr() { - assertEquals( - expected = 20, - actual = Err(AndError).andThenRecover { Ok(20) }.get() - ) - } - } - - class AndThenRecoverIf { - @Test - fun returnsValueIfOk() { - fun predicate(int: Int): Boolean { - return int == 4000 - } - - assertEquals( - expected = Ok(3000), - actual = Ok(3000).andThenRecoverIf(::predicate) { Ok(2000) } - ) - } - - @Test - fun returnsTransformedErrorAsOkIfErrAndPredicateMatch() { - fun predicate(int: Int): Boolean { - return int == 4000 - } - - assertEquals( - expected = Ok(2000), - actual = Err(4000).andThenRecoverIf(::predicate) { Ok(2000) } - ) - } - - @Test - fun returnsTransformedErrorAsErrorIfErrAndPredicateMatch() { - fun predicate(int: Int): Boolean { - return int == 4000 - } - - assertEquals( - expected = Err(2000), - actual = Err(4000).andThenRecoverIf(::predicate) { Err(2000) } - ) - } - - @Test - fun doesNotReturnTransformationResultIfErrAndPredicateDoesNotMatch() { - fun predicate(int: Int): Boolean { - return int == 3000 - } - - assertEquals( - expected = Err(4000), - actual = Err(4000).andThenRecoverIf(::predicate) { Ok(2000) } - ) - } - } - - class AndThenRecoverUnless { - @Test - fun returnsValueIfOk() { - fun predicate(int: Int): Boolean { - return int == 4000 - } - - assertEquals( - expected = Ok(3000), - actual = Ok(3000).andThenRecoverUnless(::predicate) { Ok(2000) } - ) - } - - @Test - fun returnsTransformedErrorAsOkIfErrAndPredicateDoesNotMatch() { - fun predicate(int: Int): Boolean { - return int == 3000 - } - - assertEquals( - expected = Ok(2000), - actual = Err(4000).andThenRecoverUnless(::predicate) { Ok(2000) } - ) - } - - @Test - fun returnsTransformedErrorAsErrorIfErrAndPredicateDoesNotMatch() { - fun predicate(int: Int): Boolean { - return int == 3000 - } - - assertEquals( - expected = Err(2000), - actual = Err(4000).andThenRecoverUnless(::predicate) { Err(2000) } - ) - } - - @Test - fun doesNotReturnTransformationResultIfErrAndPredicateMatches() { - fun predicate(int: Int): Boolean { - return int == 4000 - } - - assertEquals( - expected = Err(4000), - actual = Err(4000).andThenRecoverUnless(::predicate) { Ok(2000) } - ) - } - } } diff --git a/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/OrTest.kt b/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/OrTest.kt index bb20c46..2e7c27f 100644 --- a/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/OrTest.kt +++ b/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/OrTest.kt @@ -64,154 +64,6 @@ class OrTest { } } - class Recover { - @Test - fun returnsValueIfOk() { - assertEquals( - expected = Ok(3000), - actual = Ok(3000).recover { 4000 } - ) - } - - @Test - fun returnsTransformedValueIfErr() { - assertEquals( - expected = Ok(2000), - actual = Err(4000).recover { 2000 } - ) - } - } - - class RecoverCatching { - @Test - fun returnsValueIfOk() { - assertEquals( - expected = Ok(3000), - actual = Ok(3000).recoverCatching { 4000 } - ) - } - - @Test - fun returnsTransformedValueIfErr() { - assertEquals( - expected = Ok(2000), - actual = Err(4000).recoverCatching { 2000 } - ) - } - - @Test - fun returnsErrorIfTransformerThrows() { - val exception = IllegalArgumentException("throw me") - - assertEquals( - expected = exception, - actual = Err(4000) - .recoverCatching { throw exception } - .getError() - ) - } - } - - class RecoverIf { - @Test - fun returnsValueIfOk() { - fun predicate(int: Int): Boolean { - return int == 4000 - } - - assertEquals( - expected = Ok(3000), - actual = Ok(3000).recoverIf(::predicate) { 2000 } - ) - } - - @Test - fun returnsTransformedErrorAsOkIfErrAndPredicateMatch() { - fun predicate(int: Int): Boolean { - return int == 4000 - } - - assertEquals( - expected = Ok(2000), - actual = Err(4000).recoverIf(::predicate) { 2000 } - ) - } - - @Test - fun doesNotReturnTransformedErrorAsOkIfErrAndPredicateDoesNotMatch() { - fun predicate(int: Int): Boolean { - return int == 3000 - } - - assertEquals( - expected = Err(4000), - actual = Err(4000).recoverIf(::predicate) { 2000 } - ) - } - - @Test - fun returnErrIfErrAndPredicateDoesNotMatch() { - fun predicate(int: Int): Boolean { - return int == 3000 - } - - assertEquals( - expected = Err(4000), - actual = Err(4000).recoverIf(::predicate) { 2000 } - ) - } - } - - class RecoverUnless { - @Test - fun returnsValueIfOk() { - fun predicate(int: Int): Boolean { - return int == 4000 - } - - assertEquals( - expected = Ok(3000), - actual = Ok(3000).recoverUnless(::predicate) { 2000 } - ) - } - - @Test - fun returnsTransformedErrorAsOkIfErrAndPredicateDoesNotMatch() { - fun predicate(int: Int): Boolean { - return int == 3000 - } - - assertEquals( - expected = Ok(2000), - actual = Err(4000).recoverUnless(::predicate) { 2000 } - ) - } - - @Test - fun doesNotReturnTransformedErrorAsOkIfErrAndPredicateMatches() { - fun predicate(int: Int): Boolean { - return int == 4000 - } - - assertEquals( - expected = Err(4000), - actual = Err(4000).recoverUnless(::predicate) { 2000 } - ) - } - - @Test - fun returnErrIfErrAndPredicateDoesMatch() { - fun predicate(int: Int): Boolean { - return int == 4000 - } - - assertEquals( - expected = Err(4000), - actual = Err(4000).recoverUnless(::predicate) { 2000 } - ) - } - } - class ThrowIf { @Test fun returnsValueIfOk() { diff --git a/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/RecoverTest.kt b/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/RecoverTest.kt new file mode 100644 index 0000000..badd725 --- /dev/null +++ b/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/RecoverTest.kt @@ -0,0 +1,274 @@ +package com.github.michaelbull.result + +import kotlin.test.Test +import kotlin.test.assertEquals + +class RecoverTest { + private object RecoverError + + class Recover { + @Test + fun returnsValueIfOk() { + assertEquals( + expected = Ok(3000), + actual = Ok(3000).recover { 4000 } + ) + } + + @Test + fun returnsTransformedValueIfErr() { + assertEquals( + expected = Ok(2000), + actual = Err(4000).recover { 2000 } + ) + } + } + + class RecoverCatching { + @Test + fun returnsValueIfOk() { + assertEquals( + expected = Ok(3000), + actual = Ok(3000).recoverCatching { 4000 } + ) + } + + @Test + fun returnsTransformedValueIfErr() { + assertEquals( + expected = Ok(2000), + actual = Err(4000).recoverCatching { 2000 } + ) + } + + @Test + fun returnsErrorIfTransformerThrows() { + val exception = IllegalArgumentException("throw me") + + assertEquals( + expected = exception, + actual = Err(4000) + .recoverCatching { throw exception } + .getError() + ) + } + } + + class RecoverIf { + @Test + fun returnsValueIfOk() { + fun predicate(int: Int): Boolean { + return int == 4000 + } + + assertEquals( + expected = Ok(3000), + actual = Ok(3000).recoverIf(::predicate) { 2000 } + ) + } + + @Test + fun returnsTransformedErrorAsOkIfErrAndPredicateMatch() { + fun predicate(int: Int): Boolean { + return int == 4000 + } + + assertEquals( + expected = Ok(2000), + actual = Err(4000).recoverIf(::predicate) { 2000 } + ) + } + + @Test + fun doesNotReturnTransformedErrorAsOkIfErrAndPredicateDoesNotMatch() { + fun predicate(int: Int): Boolean { + return int == 3000 + } + + assertEquals( + expected = Err(4000), + actual = Err(4000).recoverIf(::predicate) { 2000 } + ) + } + + @Test + fun returnErrIfErrAndPredicateDoesNotMatch() { + fun predicate(int: Int): Boolean { + return int == 3000 + } + + assertEquals( + expected = Err(4000), + actual = Err(4000).recoverIf(::predicate) { 2000 } + ) + } + } + + class RecoverUnless { + @Test + fun returnsValueIfOk() { + fun predicate(int: Int): Boolean { + return int == 4000 + } + + assertEquals( + expected = Ok(3000), + actual = Ok(3000).recoverUnless(::predicate) { 2000 } + ) + } + + @Test + fun returnsTransformedErrorAsOkIfErrAndPredicateDoesNotMatch() { + fun predicate(int: Int): Boolean { + return int == 3000 + } + + assertEquals( + expected = Ok(2000), + actual = Err(4000).recoverUnless(::predicate) { 2000 } + ) + } + + @Test + fun doesNotReturnTransformedErrorAsOkIfErrAndPredicateMatches() { + fun predicate(int: Int): Boolean { + return int == 4000 + } + + assertEquals( + expected = Err(4000), + actual = Err(4000).recoverUnless(::predicate) { 2000 } + ) + } + + @Test + fun returnErrIfErrAndPredicateDoesMatch() { + fun predicate(int: Int): Boolean { + return int == 4000 + } + + assertEquals( + expected = Err(4000), + actual = Err(4000).recoverUnless(::predicate) { 2000 } + ) + } + } + + class AndThenRecover { + @Test + fun returnsValueIfOk() { + assertEquals( + expected = 5, + actual = Ok(5).andThenRecover { Ok(7) }.get() + ) + } + + @Test + fun returnsTransformValueIfErr() { + assertEquals( + expected = 20, + actual = Err(RecoverError).andThenRecover { Ok(20) }.get() + ) + } + } + + class AndThenRecoverIf { + @Test + fun returnsValueIfOk() { + fun predicate(int: Int): Boolean { + return int == 4000 + } + + assertEquals( + expected = Ok(3000), + actual = Ok(3000).andThenRecoverIf(::predicate) { Ok(2000) } + ) + } + + @Test + fun returnsTransformedErrorAsOkIfErrAndPredicateMatch() { + fun predicate(int: Int): Boolean { + return int == 4000 + } + + assertEquals( + expected = Ok(2000), + actual = Err(4000).andThenRecoverIf(::predicate) { Ok(2000) } + ) + } + + @Test + fun returnsTransformedErrorAsErrorIfErrAndPredicateMatch() { + fun predicate(int: Int): Boolean { + return int == 4000 + } + + assertEquals( + expected = Err(2000), + actual = Err(4000).andThenRecoverIf(::predicate) { Err(2000) } + ) + } + + @Test + fun doesNotReturnTransformationResultIfErrAndPredicateDoesNotMatch() { + fun predicate(int: Int): Boolean { + return int == 3000 + } + + assertEquals( + expected = Err(4000), + actual = Err(4000).andThenRecoverIf(::predicate) { Ok(2000) } + ) + } + } + + class AndThenRecoverUnless { + @Test + fun returnsValueIfOk() { + fun predicate(int: Int): Boolean { + return int == 4000 + } + + assertEquals( + expected = Ok(3000), + actual = Ok(3000).andThenRecoverUnless(::predicate) { Ok(2000) } + ) + } + + @Test + fun returnsTransformedErrorAsOkIfErrAndPredicateDoesNotMatch() { + fun predicate(int: Int): Boolean { + return int == 3000 + } + + assertEquals( + expected = Ok(2000), + actual = Err(4000).andThenRecoverUnless(::predicate) { Ok(2000) } + ) + } + + @Test + fun returnsTransformedErrorAsErrorIfErrAndPredicateDoesNotMatch() { + fun predicate(int: Int): Boolean { + return int == 3000 + } + + assertEquals( + expected = Err(2000), + actual = Err(4000).andThenRecoverUnless(::predicate) { Err(2000) } + ) + } + + @Test + fun doesNotReturnTransformationResultIfErrAndPredicateMatches() { + fun predicate(int: Int): Boolean { + return int == 4000 + } + + assertEquals( + expected = Err(4000), + actual = Err(4000).andThenRecoverUnless(::predicate) { Ok(2000) } + ) + } + } +}