Extract recover functions to separate file

This commit is contained in:
Michael Bull 2024-02-06 13:29:05 +00:00
parent 325cebc8f3
commit 54b53d36e6
6 changed files with 415 additions and 403 deletions

View File

@ -41,64 +41,3 @@ public inline infix fun <V, E, U> Result<V, E>.andThen(transform: (V) -> Result<
is Err -> this is Err -> this
} }
} }
/**
* Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err],
* otherwise this [Result].
*/
public inline fun <V, E> Result<V, E>.andThenRecover(transform: (E) -> Result<V, E>): Result<V, E> {
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 <V, E> Result<V, E>.andThenRecoverIf(
predicate: (E) -> Boolean,
transform: (E) -> Result<V, E>
): Result<V, E> {
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 <V, E> Result<V, E>.andThenRecoverUnless(
predicate: (E) -> Boolean,
transform: (E) -> Result<V, E>
): Result<V, E> {
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
}
}
}

View File

@ -51,82 +51,6 @@ public fun <V, E : Throwable> Result<V, E>.orElseThrow(): Ok<V> {
} }
} }
/**
* Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err],
* otherwise this [Ok].
*/
public inline infix fun <V, E> Result<V, E>.recover(transform: (E) -> V): Ok<V> {
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 <V, E> Result<V, E>.recoverCatching(transform: (E) -> V): Result<V, Throwable> {
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 <V, E> Result<V, E>.recoverIf(predicate: (E) -> Boolean, transform: (E) -> V): Result<V, E> {
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 <V, E> Result<V, E>.recoverUnless(predicate: (E) -> Boolean, transform: (E) -> V): Result<V, E> {
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 * Throws the [error][Err.error] if this [Result] is an [Err] and satisfies the given
* [predicate], otherwise returns this [Result]. * [predicate], otherwise returns this [Result].

View File

@ -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 <V, E> Result<V, E>.recover(transform: (E) -> V): Ok<V> {
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 <V, E> Result<V, E>.recoverCatching(transform: (E) -> V): Result<V, Throwable> {
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 <V, E> Result<V, E>.recoverIf(predicate: (E) -> Boolean, transform: (E) -> V): Result<V, E> {
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 <V, E> Result<V, E>.recoverUnless(predicate: (E) -> Boolean, transform: (E) -> V): Result<V, E> {
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 <V, E> Result<V, E>.andThenRecover(transform: (E) -> Result<V, E>): Result<V, E> {
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 <V, E> Result<V, E>.andThenRecoverIf(
predicate: (E) -> Boolean,
transform: (E) -> Result<V, E>
): Result<V, E> {
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 <V, E> Result<V, E>.andThenRecoverUnless(
predicate: (E) -> Boolean,
transform: (E) -> Result<V, E>
): Result<V, E> {
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
}
}
}

View File

@ -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) }
)
}
}
} }

View File

@ -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 { class ThrowIf {
@Test @Test
fun returnsValueIfOk() { fun returnsValueIfOk() {

View File

@ -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) }
)
}
}
}