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 74a4a27..6c4bc9b 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 @@ -55,3 +55,43 @@ public inline infix fun Result.recover(transform: (E) -> V): Ok is Err -> Ok(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.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_ satisfies 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 + } + } +} 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 a47a700..b5ef064 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 @@ -59,4 +59,96 @@ class OrTest { ) } } + + class RecoverIf { + @Test + fun returnsValueIfOk() { + assertEquals( + expected = 3000, + actual = (Ok(3000) as Result).recoverIf( + { it == 4000 }, + { 2000 } + ).get() + ) + } + + @Test + fun returnsTransformedErrorAsOkIfErrAndPredicateMatch() { + assertEquals( + expected = 2000, + actual = Err(4000).recoverIf( + { it == 4000 }, + { 2000 } + ).get() + ) + } + + @Test + fun doesNotReturnTransformedErrorAsOkIfErrAndPredicateDoesNotMatch() { + assertEquals( + expected = null, + actual = Err(4000).recoverIf( + { it == 3000 }, + { 2000 } + ).get() + ) + } + + @Test + fun returnErrIfErrAndPredicateDoesNotMatch() { + assertEquals( + expected = 4000, + actual = Err(4000).recoverIf( + { it == 3000 }, + { 2000 } + ).getError() + ) + } + } + + class RecoverUnless { + @Test + fun returnsValueIfOk() { + assertEquals( + expected = 3000, + actual = (Ok(3000) as Result).recoverUnless( + { it == 4000 }, + { 2000 } + ).get() + ) + } + + @Test + fun returnsTransformedErrorAsOkIfErrAndPredicateDoesNotMatch() { + assertEquals( + expected = 2000, + actual = Err(4000).recoverUnless( + { it == 3000 }, + { 2000 } + ).get() + ) + } + + @Test + fun doesNotReturnTransformedErrorAsOkIfErrAndPredicateMatches() { + assertEquals( + expected = null, + actual = Err(4000).recoverUnless( + { it == 4000 }, + { 2000 } + ).get() + ) + } + + @Test + fun returnErrIfErrAndPredicateDoesMatch() { + assertEquals( + expected = 4000, + actual = Err(4000).recoverUnless( + { it == 4000 }, + { 2000 } + ).getError() + ) + } + } }