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 e4881e9..5cff8f5 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,3 +41,64 @@ 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/commonTest/kotlin/com/github/michaelbull/result/AndTest.kt b/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result/AndTest.kt index 4f80d54..6305408 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,4 +42,122 @@ 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) } + ) + } + } }