Skip to content

Commit

Permalink
AsyncResult new shiny functions
Browse files Browse the repository at this point in the history
  • Loading branch information
krzykrucz committed Dec 7, 2019
1 parent e819530 commit bda630e
Show file tree
Hide file tree
Showing 6 changed files with 324 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -1,40 +1,65 @@
package com.virtuslab.basetypes.result.arrow

import arrow.fx.IO
import arrow.fx.handleError
import com.virtuslab.basetypes.result.Result
import com.virtuslab.basetypes.result.Result.Failure
import com.virtuslab.basetypes.result.Result.Success
import com.virtuslab.basetypes.result.flatMap
import com.virtuslab.basetypes.result.map
import com.virtuslab.basetypes.result.mapError

typealias AsyncResult<T, E> = IO<Result<T, E>>

fun <T : Any, E : Exception> Result<T, E>.liftAsync(): AsyncResult<T, E> =
IO.just(this)
fun <S : Any, E : Exception, S2 : Any> AsyncResult<S, E>.mapSuccess(mapper: (S) -> S2): AsyncResult<S2, E> =
this.map {
it.map(mapper)
}

fun <S : Any, E : Exception, E2 : java.lang.Exception> AsyncResult<S, E>.mapFailure(mapper: (E) -> E2): AsyncResult<S, E2> =
this.map {
it.mapError(mapper)
}

infix fun <T1 : Any, T2 : Any, E : Exception> Result<T1, E>.then(f: (T1) -> Result<T2, E>): Result<T2, E> =
when (this) {
is Success -> f(this.value)
is Failure -> Failure(this.error)
fun <S : Any, E : Exception, S2 : Any> AsyncResult<S, E>.flatMapResult(mapper: (S) -> Result<S2, E>): AsyncResult<S2, E> =
this.map {
it.flatMap(mapper)
}

infix fun <T1 : Any, T2 : Any, E : Exception> AsyncResult<T1, E>.thenSync(f: (T1) -> Result<T2, E>): AsyncResult<T2, E> =
map { it.flatMap(f) }
fun <S : Any, E : Exception, S2 : Any> AsyncResult<S, E>.flatMapSuccess(mapper: (S) -> AsyncResult<S2, E>): AsyncResult<S2, E> =
this.flatMap { result1 ->
when (result1) {
is Success -> mapper(result1.value)
is Failure -> Failure(result1.error).toAsync()
}
}

infix fun <T1 : Any, T2 : Any, E : Exception> AsyncResult<T1, E>.then(f: (T1) -> AsyncResult<T2, E>): AsyncResult<T2, E> =
fun <S : Any, E : Exception, S2 : Any> AsyncResult<S, E>.flatMapSuccess(mapper: (S) -> IO<S2>, errorMapper: (Throwable) -> E): AsyncResult<S2, E> =
this.flatMap { result1 ->
when (result1) {
is Success -> f(result1.value)
is Failure -> Failure(result1.error).liftAsync()
is Success -> mapper(result1.value).liftResult(errorMapper)
is Failure -> Failure(result1.error).toAsync()
}
}

infix fun <T1 : Any, T2 : Any, E : Exception> Result<T1, E>.thenAsync(f: (T1) -> AsyncResult<T2, E>): AsyncResult<T2, E> =
fun <S : Any, E : Exception, S2 : Any> Result<S, E>.liftMap(mapper: (S) -> AsyncResult<S2, E>): AsyncResult<S2, E> =
when (this) {
is Success -> f(this.value)
is Failure -> Failure(this.error).liftAsync()
is Success -> mapper(value)
is Failure -> Failure(error).toAsync()
}

infix fun <T1 : Any, T2 : Any, E : Exception> T1.to(f: (T1) -> Result<T2, E>): Result<T2, E> = Success(this) then f
fun <S : Any, E : Exception> S.justAsyncResult(): AsyncResult<S, E> =
Result.of<S, E> { this }
.let { IO.just(it) }

fun <S : Any, E : Exception> E.failedAsyncResult(): AsyncResult<S, E> =
Result.error(this)
.let { IO.just(it) }

fun <S : Any, E : Exception> IO<S>.liftResult(errorMapper: (Throwable) -> E): AsyncResult<S, E> =
this.map { Result.success(it) as Result<S, E> }
.handleError { Result.error(errorMapper(it)) }

fun <S> S.toAsync(): IO<S> = IO.just(this)

infix fun <T1 : Any, T2 : Any, E : Exception> T1.toAsyncResult(f: (T1) -> AsyncResult<T2, E>): AsyncResult<T2, E> = Success(this) thenAsync f
fun <T : Any, E : Exception> Result<T, E>.liftAsync(): AsyncResult<T, E> = IO.just(this)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.virtuslab.basetypes.result.arrow

import com.virtuslab.basetypes.result.Result
import com.virtuslab.basetypes.result.Result.Failure
import com.virtuslab.basetypes.result.Result.Success
import com.virtuslab.basetypes.result.flatMap


infix fun <T1 : Any, T2 : Any, E : Exception> Result<T1, E>.then(f: (T1) -> Result<T2, E>): Result<T2, E> =
when (this) {
is Success -> f(this.value)
is Failure -> Failure(this.error)
}

infix fun <T1 : Any, T2 : Any, E : Exception> AsyncResult<T1, E>.thenSync(f: (T1) -> Result<T2, E>): AsyncResult<T2, E> =
map { it.flatMap(f) }

infix fun <T1 : Any, T2 : Any, E : Exception> AsyncResult<T1, E>.then(f: (T1) -> AsyncResult<T2, E>): AsyncResult<T2, E> =
this.flatMap { result1 ->
when (result1) {
is Success -> f(result1.value)
is Failure -> Failure(result1.error).liftAsync()
}
}

infix fun <T1 : Any, T2 : Any, E : Exception> Result<T1, E>.thenAsync(f: (T1) -> AsyncResult<T2, E>): AsyncResult<T2, E> =
when (this) {
is Success -> f(this.value)
is Failure -> Failure(this.error).liftAsync()
}

infix fun <T1 : Any, T2 : Any, E : Exception> T1.to(f: (T1) -> Result<T2, E>): Result<T2, E> = Success(this) then f

infix fun <T1 : Any, T2 : Any, E : Exception> T1.toAsyncResult(f: (T1) -> AsyncResult<T2, E>): AsyncResult<T2, E> = Success(this) thenAsync f
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.virtuslab.basetypes.result.arrow

import com.virtuslab.basetypes.result.Result
import io.kotlintest.shouldBe
import io.kotlintest.shouldThrow
import org.junit.jupiter.api.Test

internal class AsyncResultInfixExtensionsKtTest {


@Test
fun `Successful Result should be thenable`() {
Result.success("Some success value")
.then { Result.success("Some other value") }
.get() shouldBe "Some other value"
}

@Test
fun `given first Result is failure when doing then then it whole should return failure`() {
shouldThrow<RuntimeException> {
Result.error(RuntimeException("Boo"))
.then { Result.success("Some other value") }
.get()
}
}

@Test
fun `given second Result is failure when doing then then it whole should return failure`() {
shouldThrow<RuntimeException> {
Result.success("Some value")
.then { Result.error(RuntimeException("Boo")) }
.get()
}
}

@Test
fun `given first AsyncResult is failure when doing then then it whole should return failure`() {
shouldThrow<RuntimeException> {
Result.error(RuntimeException("Boo")).liftAsync()
.then { Result.success("Some other value").liftAsync() }
.unsafeRunSync()
.get()
}
}

@Test
fun `given second AsyncResult is failure when doing then then it whole should return failure`() {
shouldThrow<RuntimeException> {
Result.success("Some value").liftAsync()
.thenSync { Result.error(RuntimeException("Boo")) }
.unsafeRunSync()
.get()
}
}

@Test
fun `given first AsyncResult is failure when doing then thenDoAsync it whole should return failure`() {
shouldThrow<RuntimeException> {
Result.error(RuntimeException("Boo"))
.thenAsync { Result.success("Some other value").liftAsync() }
.unsafeRunSync()
.get()
}
}


@Test
fun `AsyncResult should be thenable with Result`() {
Result.success("Some success value").liftAsync()
.thenSync { Result.success("Some other value") }
.unsafeRunSync().get() shouldBe "Some other value"
}

@Test
fun `AsyncResult should be thenable with AsyncResult`() {
Result.success("Some success value").liftAsync()
.then { Result.success("Some other value").liftAsync() }
.unsafeRunSync().get() shouldBe "Some other value"
}

@Test
fun `Result should be thenable with AsyncResult`() {
Result.success("Some success value")
.thenAsync { Result.success("Some other value").liftAsync() }
.unsafeRunSync().get() shouldBe "Some other value"
}

@Test
fun `any type should be thenable to Result`() {
val result = "Success" to { Result.success("A") }
result.get() shouldBe "A"
}

@Test
fun `any type should be thenable to AsyncResult`() {
val result = "Success" toAsyncResult { Result.success("A").liftAsync() }
result.unsafeRunSync().get() shouldBe "A"
}
}
Loading

0 comments on commit bda630e

Please sign in to comment.