Skip to content

Commit

Permalink
Adds OutcomeOf::asResult with an onAbsent conversion (#114)
Browse files Browse the repository at this point in the history
* Adds OutcomeOf::asResult with an onAbsent conversion

* Updates lib.api
  • Loading branch information
hugomd authored Nov 22, 2024
1 parent fa94f56 commit e7a5252
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 6 deletions.
1 change: 1 addition & 0 deletions lib/api/lib.api
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public final class app/cash/quiver/OutcomeKt {
public static final fun asOption (Lapp/cash/quiver/Outcome;)Larrow/core/Option;
public static final fun asOutcome (Larrow/core/Either;)Lapp/cash/quiver/Outcome;
public static final fun asResult (Lapp/cash/quiver/Outcome;)Ljava/lang/Object;
public static final fun asResult (Lapp/cash/quiver/Outcome;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
public static final fun failure (Ljava/lang/Object;)Lapp/cash/quiver/Outcome;
public static final fun filter (Lapp/cash/quiver/Outcome;Lkotlin/jvm/functions/Function1;)Lapp/cash/quiver/Outcome;
public static final fun flatMap (Lapp/cash/quiver/Outcome;Lkotlin/jvm/functions/Function1;)Lapp/cash/quiver/Outcome;
Expand Down
22 changes: 17 additions & 5 deletions lib/src/main/kotlin/app/cash/quiver/Outcome.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
package app.cash.quiver

import app.cash.quiver.extensions.OutcomeOf
import app.cash.quiver.extensions.orThrow
import app.cash.quiver.extensions.toResult
import app.cash.quiver.raise.OutcomeRaise
import app.cash.quiver.raise.outcome
import arrow.core.Either
import arrow.core.Either.Left
import arrow.core.Either.Right
Expand All @@ -14,14 +18,10 @@ import arrow.core.flatMap
import arrow.core.getOrElse
import arrow.core.identity
import arrow.core.left
import arrow.core.raise.catch
import arrow.core.right
import arrow.core.some
import arrow.core.valid
import app.cash.quiver.extensions.orThrow
import app.cash.quiver.extensions.toResult
import app.cash.quiver.raise.OutcomeRaise
import app.cash.quiver.raise.outcome
import arrow.core.raise.catch
import kotlin.experimental.ExperimentalTypeInference

/**
Expand Down Expand Up @@ -247,6 +247,18 @@ inline fun <E, A> Outcome<E, A>.asEither(onAbsent: () -> E): Either<E, A> =
*/
fun <A> OutcomeOf<A>.asResult(): Result<Option<A>> = inner.toResult()

/**
* Converts an OutcomeOf<A> to a Result<A>, converting Absent to a Failure.
*/
inline fun <A> OutcomeOf<A>.asResult(onAbsent: () -> Throwable): Result<A> =
inner.toResult()
.flatMap { maybeValue ->
maybeValue.fold(
{ Result.failure(onAbsent()) },
{ Result.success(it) }
)
}

inline fun <E, A, B> Outcome<E, A>.foldOption(onAbsent: () -> B, onPresent: (A) -> B): Either<E, B> =
inner.map { it.fold(onAbsent, onPresent) }

Expand Down
12 changes: 11 additions & 1 deletion lib/src/test/kotlin/app/cash/quiver/OutcomeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import io.kotest.assertions.throwables.shouldThrow
import io.kotest.common.runBlocking
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.result.shouldBeFailure
import io.kotest.matchers.result.shouldBeSuccess
import io.kotest.matchers.shouldBe
import io.kotest.property.Arb
import io.kotest.property.arbitrary.int
Expand All @@ -40,6 +41,7 @@ import io.kotest.property.arrow.core.either
import io.kotest.property.arrow.core.option
import io.kotest.property.checkAll
import kotlinx.coroutines.coroutineScope
import java.lang.IllegalStateException

class OutcomeTest : StringSpec({
"Present flatMap" {
Expand Down Expand Up @@ -475,6 +477,7 @@ class OutcomeTest : StringSpec({
absent.recover { fallback.bind() }.shouldBeAbsent()
}
}

"recover can recover from Failure" {
checkAll(Arb.either(Arb.long(), Arb.int())) { either ->
val failed: Outcome<String, Int> = "failure".failure()
Expand All @@ -495,9 +498,16 @@ class OutcomeTest : StringSpec({
}
}

"Converting to Result" {
"converting to Result" {
Absent.asResult() shouldBe Result.success(None)
1.present().asResult() shouldBe Result.success(Some(1))
Throwable("sad").failure().asResult().shouldBeFailure().message shouldBe "sad"
}

"asResult converts to Either converting Absent to an error" {
1.present().asResult { IllegalStateException("nup") }.shouldBeSuccess(1)
Absent.asEither { IllegalStateException("nup") }.shouldBeLeft().message shouldBe "nup"
"bad".failure().asEither { IllegalStateException("nup") }.shouldBeLeft("bad")
}

})

0 comments on commit e7a5252

Please sign in to comment.