From eb110a9aa7734f77d5cffe377a19939097e5ca12 Mon Sep 17 00:00:00 2001 From: krzykrucz Date: Mon, 9 Mar 2020 20:20:44 +0100 Subject: [PATCH] BIO migration --- build.gradle.kts | 2 + gradle.properties | 2 +- result-arrow/.gitignore | 2 - result-arrow/build.gradle.kts | 25 -- .../basetypes/result/arrow/AsyncResult.kt | 65 --- .../arrow/AsyncResultInfixExtensions.kt | 34 -- .../arrow/AsyncResultInfixExtensionsKtTest.kt | 99 ----- .../result/arrow/AsyncResultKtTest.kt | 148 ------- .../basetypes/result/arrow/IOTester.kt | 23 - .../basetypes/result/arrow/SomeFailure.kt | 3 - result-reactor/build.gradle.kts | 4 +- .../basetypes/result/reactor/FluxEither.kt | 6 + .../basetypes/result/reactor/MonoEither.kt | 69 +++ .../basetypes/result/reactor/MonoResult.kt | 73 --- .../result/reactor/arrowExtensions.kt | 41 ++ .../result/reactor/MonoEitherKtTest.kt | 136 ++++++ .../result/reactor/MonoResultKtTest.kt | 163 ------- .../basetypes/result/reactor/SomeFailure.kt | 3 - .../basetypes/result/reactor/helpers.kt | 7 + result-rxjava/build.gradle.kts | 3 +- .../basetypes/result/rxjava/SingleEither.kt | 68 +++ .../basetypes/result/rxjava/SingleResult.kt | 87 ---- .../result/rxjava/arrowExtensions.kt | 17 + .../result/rxjava/SingleEitherKtTest.kt | 128 ++++++ .../result/rxjava/SingleResultKtTest.kt | 162 ------- .../basetypes/result/rxjava/SomeFailure.kt | 3 - .../basetypes/result/rxjava/helpers.kt | 6 + result/.gitignore | 2 - result/LICENSE.md | 21 - result/build.gradle.kts | 17 - .../virtuslab/basetypes/result/NoException.kt | 3 - .../com/virtuslab/basetypes/result/Result.kt | 159 ------- .../virtuslab/basetypes/result/Validation.kt | 8 - result/src/test/assets/bar.txt | 1 - result/src/test/assets/foo.txt | 1 - .../virtuslab/basetypes/result/ResultTests.kt | 418 ------------------ .../basetypes/result/SampleException.kt | 5 - .../basetypes/result/ValidationTests.kt | 33 -- settings.gradle.kts | 2 +- 39 files changed, 487 insertions(+), 1562 deletions(-) delete mode 100644 result-arrow/.gitignore delete mode 100644 result-arrow/build.gradle.kts delete mode 100644 result-arrow/src/main/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResult.kt delete mode 100644 result-arrow/src/main/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultInfixExtensions.kt delete mode 100644 result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultInfixExtensionsKtTest.kt delete mode 100644 result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultKtTest.kt delete mode 100644 result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/IOTester.kt delete mode 100644 result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/SomeFailure.kt create mode 100644 result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/FluxEither.kt create mode 100644 result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/MonoEither.kt delete mode 100644 result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/MonoResult.kt create mode 100644 result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/arrowExtensions.kt create mode 100644 result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/MonoEitherKtTest.kt delete mode 100644 result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/MonoResultKtTest.kt delete mode 100644 result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/SomeFailure.kt create mode 100644 result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/helpers.kt create mode 100644 result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/SingleEither.kt delete mode 100644 result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/SingleResult.kt create mode 100644 result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/arrowExtensions.kt create mode 100644 result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SingleEitherKtTest.kt delete mode 100644 result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SingleResultKtTest.kt delete mode 100644 result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SomeFailure.kt create mode 100644 result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/helpers.kt delete mode 100644 result/.gitignore delete mode 100644 result/LICENSE.md delete mode 100644 result/build.gradle.kts delete mode 100644 result/src/main/kotlin/com/virtuslab/basetypes/result/NoException.kt delete mode 100644 result/src/main/kotlin/com/virtuslab/basetypes/result/Result.kt delete mode 100644 result/src/main/kotlin/com/virtuslab/basetypes/result/Validation.kt delete mode 100644 result/src/test/assets/bar.txt delete mode 100644 result/src/test/assets/foo.txt delete mode 100644 result/src/test/kotlin/com/virtuslab/basetypes/result/ResultTests.kt delete mode 100644 result/src/test/kotlin/com/virtuslab/basetypes/result/SampleException.kt delete mode 100644 result/src/test/kotlin/com/virtuslab/basetypes/result/ValidationTests.kt diff --git a/build.gradle.kts b/build.gradle.kts index 9a815fb..150db53 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,6 +18,8 @@ buildscript { allprojects { repositories { jcenter() + mavenCentral() + maven(url = "https://oss.jfrog.org/artifactory/oss-snapshot-local/") } } diff --git a/gradle.properties b/gradle.properties index 15bb4cc..a57fa2c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ bintray=1.8.4 coroutines=1.3.0-RC2 jacoco=0.8.3 junit=5.1.0 -arrow=0.10.3 +arrow=0.11.0-SNAPSHOT kotlintest=3.3.0 artifactPublish=1.0.0-SNAPSHOT diff --git a/result-arrow/.gitignore b/result-arrow/.gitignore deleted file mode 100644 index 3ddb9e8..0000000 --- a/result-arrow/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build -.kotlintest diff --git a/result-arrow/build.gradle.kts b/result-arrow/build.gradle.kts deleted file mode 100644 index 8e2de99..0000000 --- a/result-arrow/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -sourceSets { - getByName("main").java.srcDirs("src/main/kotlin") - getByName("test").java.srcDirs("src/main/kotlin") -} - -tasks.withType { - useJUnitPlatform() -} - -dependencies { - val junit: String by project - val kotlinVersion: String by project - val arrow: String by project - val kotlintest: String by project - - implementation(kotlin("stdlib", kotlinVersion)) - - implementation("io.arrow-kt:arrow-core:$arrow") - implementation("io.arrow-kt:arrow-fx:$arrow") - api(project(":result")) - - - testImplementation("io.kotlintest:kotlintest-runner-junit5:$kotlintest") - testImplementation("org.junit.jupiter:junit-jupiter-engine:$junit") -} diff --git a/result-arrow/src/main/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResult.kt b/result-arrow/src/main/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResult.kt deleted file mode 100644 index 241a2e6..0000000 --- a/result-arrow/src/main/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResult.kt +++ /dev/null @@ -1,65 +0,0 @@ -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 = IO> - -fun AsyncResult.mapSuccess(mapper: (S) -> S2): AsyncResult = - this.map { - it.map(mapper) - } - -fun AsyncResult.mapFailure(mapper: (E) -> E2): AsyncResult = - this.map { - it.mapError(mapper) - } - -fun AsyncResult.flatMapResult(mapper: (S) -> Result): AsyncResult = - this.map { - it.flatMap(mapper) - } - -fun AsyncResult.flatMapSuccess(mapper: (S) -> AsyncResult): AsyncResult = - this.flatMap { result1 -> - when (result1) { - is Success -> mapper(result1.value) - is Failure -> Failure(result1.error).toAsync() - } - } - -fun AsyncResult.flatMapSuccess(mapper: (S) -> IO, errorMapper: (Throwable) -> E): AsyncResult = - this.flatMap { result1 -> - when (result1) { - is Success -> mapper(result1.value).liftResult(errorMapper) - is Failure -> Failure(result1.error).toAsync() - } - } - -fun Result.liftMap(mapper: (S) -> AsyncResult): AsyncResult = - when (this) { - is Success -> mapper(value) - is Failure -> Failure(error).toAsync() - } - -fun S.justAsyncResult(): AsyncResult = - Result.of { this } - .let { IO.just(it) } - -fun E.failedAsyncResult(): AsyncResult = - Result.error(this) - .let { IO.just(it) } - -fun IO.liftResult(errorMapper: (Throwable) -> E): AsyncResult = - this.map { Result.success(it) as Result } - .handleError { Result.error(errorMapper(it)) } - -fun S.toAsync(): IO = IO.just(this) - -fun Result.liftAsync(): AsyncResult = IO.just(this) \ No newline at end of file diff --git a/result-arrow/src/main/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultInfixExtensions.kt b/result-arrow/src/main/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultInfixExtensions.kt deleted file mode 100644 index aa5ca0b..0000000 --- a/result-arrow/src/main/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultInfixExtensions.kt +++ /dev/null @@ -1,34 +0,0 @@ -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 Result.then(f: (T1) -> Result): Result = - when (this) { - is Success -> f(this.value) - is Failure -> Failure(this.error) - } - -infix fun AsyncResult.thenSync(f: (T1) -> Result): AsyncResult = - map { it.flatMap(f) } - -infix fun AsyncResult.then(f: (T1) -> AsyncResult): AsyncResult = - this.flatMap { result1 -> - when (result1) { - is Success -> f(result1.value) - is Failure -> Failure(result1.error).liftAsync() - } - } - -infix fun Result.thenAsync(f: (T1) -> AsyncResult): AsyncResult = - when (this) { - is Success -> f(this.value) - is Failure -> Failure(this.error).liftAsync() - } - -infix fun T1.to(f: (T1) -> Result): Result = Success(this) then f - -infix fun T1.toAsyncResult(f: (T1) -> AsyncResult): AsyncResult = Success(this) thenAsync f diff --git a/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultInfixExtensionsKtTest.kt b/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultInfixExtensionsKtTest.kt deleted file mode 100644 index bcc3185..0000000 --- a/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultInfixExtensionsKtTest.kt +++ /dev/null @@ -1,99 +0,0 @@ -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") } - .getSuccessUnsafe() shouldBe "Some other value" - } - - @Test - fun `given first Result is failure when doing then then it whole should return failure`() { - shouldThrow { - Result.error(RuntimeException("Boo")) - .then { Result.success("Some other value") } - .getSuccessUnsafe() - } - } - - @Test - fun `given second Result is failure when doing then then it whole should return failure`() { - shouldThrow { - Result.success("Some value") - .then { Result.error(RuntimeException("Boo")) } - .getSuccessUnsafe() - } - } - - @Test - fun `given first AsyncResult is failure when doing then then it whole should return failure`() { - shouldThrow { - Result.error(RuntimeException("Boo")).liftAsync() - .then { Result.success("Some other value").liftAsync() } - .unsafeRunSync() - .getSuccessUnsafe() - } - } - - @Test - fun `given second AsyncResult is failure when doing then then it whole should return failure`() { - shouldThrow { - Result.success("Some value").liftAsync() - .thenSync { Result.error(RuntimeException("Boo")) } - .unsafeRunSync() - .getSuccessUnsafe() - } - } - - @Test - fun `given first AsyncResult is failure when doing then thenDoAsync it whole should return failure`() { - shouldThrow { - Result.error(RuntimeException("Boo")) - .thenAsync { Result.success("Some other value").liftAsync() } - .unsafeRunSync() - .getSuccessUnsafe() - } - } - - - @Test - fun `AsyncResult should be thenable with Result`() { - Result.success("Some success value").liftAsync() - .thenSync { Result.success("Some other value") } - .unsafeRunSync().getSuccessUnsafe() 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().getSuccessUnsafe() 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().getSuccessUnsafe() shouldBe "Some other value" - } - - @Test - fun `any type should be thenable to Result`() { - val result = "Success" to { Result.success("A") } - result.getSuccessUnsafe() shouldBe "A" - } - - @Test - fun `any type should be thenable to AsyncResult`() { - val result = "Success" toAsyncResult { Result.success("A").liftAsync() } - result.unsafeRunSync().getSuccessUnsafe() shouldBe "A" - } -} diff --git a/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultKtTest.kt b/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultKtTest.kt deleted file mode 100644 index ba00f53..0000000 --- a/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/AsyncResultKtTest.kt +++ /dev/null @@ -1,148 +0,0 @@ -package com.virtuslab.basetypes.result.arrow - -import arrow.fx.IO -import com.virtuslab.basetypes.result.Result -import io.kotlintest.specs.StringSpec - -internal class AsyncResultKtTest : StringSpec() { - init { - "should map success" { - val asyncResult: AsyncResult = "Some value".justAsyncResult() - - asyncResult.mapSuccess { "Some other value" } - .test() - .assertResult(Result.success("Some other value")) - } - - "should propagate exception when mapping success" { - val asyncResult: AsyncResult = "Some value".justAsyncResult() - val runtimeException = RuntimeException() - - asyncResult.mapSuccess { if (true) throw runtimeException else "Some other value" } - .test() - .assertError(runtimeException) - } - - "should propagate exception when mapping result success" { - val asyncResult: AsyncResult = "Some value".justAsyncResult() - val runtimeException = RuntimeException() - - asyncResult.flatMapResult { if (true) throw runtimeException else Result.success("Some other value") } - .test() - .assertError(runtimeException) - } - - "should propagate exception when flatMapping AsyncResult" { - val asyncResult: AsyncResult = "Some value".justAsyncResult() - val runtimeException = RuntimeException() - - asyncResult.flatMapSuccess { if (true) throw runtimeException else "Some other value".justAsyncResult() } - .test() - .assertError(runtimeException) - } - - "should keep error when mapping success" { - val asyncResult: AsyncResult = SomeFailure("Some failure").failedAsyncResult() - - asyncResult.mapSuccess { "Some other value" } - .test() - .assertResult(Result.error(SomeFailure("Some failure"))) - } - - "should be able to map error in result"{ - val asyncResult: AsyncResult = Result.error(RuntimeException("exception")).liftAsync() - - asyncResult.mapFailure { SomeFailure("Failure from ${it.localizedMessage}") } - .test() - .assertResult(Result.error(SomeFailure("Failure from exception"))) - } - - "should flatMap success"{ - val asyncResult: AsyncResult = "Some value".justAsyncResult() - - asyncResult.flatMapResult { x: String -> Result.success("$x some other") } - .test() - .assertResult(Result.success("Some value some other")) - } - - "should keep error when flatMapping result success" { - val asyncResult: AsyncResult = SomeFailure("Some failure").failedAsyncResult() - - asyncResult.flatMapResult { x: String -> Result.success("$x some other") } - .test() - .assertResult(Result.error(SomeFailure("Some failure"))) - } - - "should flatMap AsyncResult"{ - val asyncResult: AsyncResult = "Some value".justAsyncResult() - - asyncResult.flatMapSuccess { x: String -> Result.success("$x some other").liftAsync() } - .test() - .assertResult(Result.success("Some value some other")) - } - - "should keep error when flatMapping AsyncResult"{ - val asyncResult: AsyncResult = SomeFailure("Some failure").failedAsyncResult() - - asyncResult.flatMapSuccess { x: String -> (Result.success("$x some other") as Result).liftAsync() } - .test() - .assertResult(Result.error(SomeFailure("Some failure"))) - } - - "should flatMap Async"{ - val asyncResult: AsyncResult = "Some value".justAsyncResult() - - asyncResult.flatMapSuccess( - mapper = { x: String -> "$x some other".toAsync() }, - errorMapper = { ex -> RuntimeException(ex) }) - .test() - .assertResult(Result.success("Some value some other")) - } - - "should keep error when flatmapping Async"{ - val asyncResult: AsyncResult = SomeFailure("Some failure").failedAsyncResult() - - asyncResult.flatMapSuccess( - mapper = { x: String -> "$x some other".toAsync() }, - errorMapper = { ex -> SomeFailure(ex.localizedMessage) }) - .test() - .assertResult(Result.error(SomeFailure("Some failure"))) - } - - "should propagate exception when flatMap failed"{ - val asyncResult: AsyncResult = "Some value".justAsyncResult() - - asyncResult.flatMapSuccess( - mapper = { x: String -> IO.raiseError(RuntimeException("$x some other")) }, - errorMapper = { ex -> SomeFailure(ex.localizedMessage) } - ) - .test() - .assertResult(Result.error(SomeFailure("Some value some other")) as Result) - } - - "should liftmap result"{ - val result = Result.success("Some value") - - result.liftMap { x -> "$x Some value".justAsyncResult() } - .test() - .assertResult(Result.success("Some value Some value")) - } - - "should convert Async with error to AsyncResult"{ - val async = IO.raiseError(RuntimeException("Runtime exception")) - - async.liftResult { SomeFailure(it.localizedMessage) } - .test() - .assertResult(Result.error(SomeFailure("Runtime exception"))) - } - - "should convert Async with success to AsyncResult"{ - val async = IO.just("Some value") - - async.liftResult { SomeFailure(it.localizedMessage) } - .test() - .assertResult(Result.success("Some value")) - } - } - -} diff --git a/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/IOTester.kt b/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/IOTester.kt deleted file mode 100644 index 4ed5ff8..0000000 --- a/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/IOTester.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.virtuslab.basetypes.result.arrow - -import arrow.core.getOrElse -import arrow.fx.IO -import arrow.fx.typeclasses.seconds -import io.kotlintest.shouldBe - -fun IO.test(): IOTester = IOTester(this) -class IOTester(private val io: IO) { - - fun assertResult(t: T) { - io.unsafeRunTimed(5.seconds) - .getOrElse { null } shouldBe t - } - - fun assertError(ex: Throwable) { - io.attempt().unsafeRunTimed(5.seconds) - .getOrElse { null } - ?.swap() - ?.toOption() - ?.getOrElse { null } shouldBe ex - } -} \ No newline at end of file diff --git a/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/SomeFailure.kt b/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/SomeFailure.kt deleted file mode 100644 index f12681d..0000000 --- a/result-arrow/src/test/kotlin/com/virtuslab/basetypes/result/arrow/SomeFailure.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.virtuslab.basetypes.result.arrow - -data class SomeFailure(val errorMessage: String) : Exception() \ No newline at end of file diff --git a/result-reactor/build.gradle.kts b/result-reactor/build.gradle.kts index 80de200..7fb80ea 100644 --- a/result-reactor/build.gradle.kts +++ b/result-reactor/build.gradle.kts @@ -11,12 +11,14 @@ dependencies { val junit: String by project val kotlinVersion: String by project val kotlintest: String by project + val arrow: String by project val reactor = "3.3.0.RELEASE" implementation(kotlin("stdlib", kotlinVersion)) implementation("io.projectreactor:reactor-core:$reactor") - api(project(":result")) + implementation("io.arrow-kt:arrow-fx:$arrow") + implementation("io.arrow-kt:arrow-fx-reactor:$arrow") testImplementation("io.kotlintest:kotlintest-runner-junit5:$kotlintest") diff --git a/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/FluxEither.kt b/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/FluxEither.kt new file mode 100644 index 0000000..69f8141 --- /dev/null +++ b/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/FluxEither.kt @@ -0,0 +1,6 @@ +package com.virtuslab.basetypes.result.reactor + +import arrow.core.Either +import arrow.fx.reactor.FluxK + +typealias FluxEither = FluxK> \ No newline at end of file diff --git a/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/MonoEither.kt b/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/MonoEither.kt new file mode 100644 index 0000000..3624d3e --- /dev/null +++ b/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/MonoEither.kt @@ -0,0 +1,69 @@ +package com.virtuslab.basetypes.Either.reactor + +import arrow.core.Either +import arrow.core.flatMap +import arrow.fx.reactor.MonoK +import arrow.fx.reactor.handleErrorWith +import arrow.fx.reactor.k +import com.virtuslab.basetypes.result.reactor.toMonoK +import reactor.core.publisher.Mono + +typealias MonoEither = MonoK> + +typealias CompletableEither = MonoEither + +fun MonoEither.mapRight(mapper: (S) -> S2): MonoEither = + this.map { + it.map(mapper) + } + +fun MonoEither.mapLeft(mapper: (E) -> E2): MonoEither = + this.map { + it.mapLeft(mapper) + } + +fun MonoEither.flatMapEither(mapper: (S) -> Either): MonoEither = + this.map { + it.flatMap(mapper) + } + +fun MonoEither.flatMapRight(mapper: (S) -> MonoEither): MonoEither = + this.flatMap { either -> + when (either) { + is Either.Right -> mapper(either.b) + is Either.Left -> Either.left(either.a).toMonoK() + } + } + +//fun MonoEither.flatMap(mapper: (S) -> MonoEither, errorMapper: (E) -> E2): MonoEither = TODO() +// +//fun MonoEither.flatMapEither(mapper: (S) -> Either, errorMapper: (E) -> E2): MonoEither = TODO() + +fun Either.liftMapRight(mapper: (S) -> MonoEither): MonoEither = + when (this) { + is Either.Right -> mapper(b) + is Either.Left -> Either.left(a).toMonoK() + } + +fun MonoK.Companion.justRight(s: S): MonoEither = Either.right(s).toMonoK() + +fun MonoK.Companion.justLeft(e: E): MonoEither = Either.left(e).toMonoK() + +fun Either.liftMono(): MonoEither = this.toMonoK() + +fun MonoK.liftEither(): MonoEither = this.map { Either.right(it) as Either } + +fun Mono.liftEither(): MonoEither = k().liftEither() + +fun MonoK.liftEither(errorMapper: (Throwable) -> E): MonoEither = + this.map { Either.right(it) as Either } + .handleErrorWith { Either.left(errorMapper(it)).toMonoK() } + +fun Mono.liftEither(errorMapper: (Throwable) -> E): MonoEither = k().liftEither(errorMapper) + +fun MonoEither.any(predicate: (V) -> Boolean): MonoK = this.map { + when (it) { + is Either.Right -> predicate(it.b) + else -> false + } +} diff --git a/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/MonoResult.kt b/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/MonoResult.kt deleted file mode 100644 index 9e21624..0000000 --- a/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/MonoResult.kt +++ /dev/null @@ -1,73 +0,0 @@ -package com.virtuslab.basetypes.result.reactor - -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 -import reactor.core.publisher.Mono -import reactor.core.publisher.toMono - -typealias MonoResult = Mono> - -typealias Completable = Mono> - -fun MonoResult.mapSuccess(mapper: (S) -> S2): MonoResult = - this.map { - it.map(mapper) - } - -fun MonoResult.mapFailure(mapper: (E) -> E2): MonoResult = - this.map { - it.mapError(mapper) - } - -fun MonoResult.flatMapResult(mapper: (S) -> Result): MonoResult = - this.map { - it.flatMap(mapper) - } - -fun MonoResult.flatMapSuccess(mapper: (S) -> MonoResult): MonoResult = - this.flatMap { result1 -> - when (result1) { - is Success -> mapper(result1.value) - is Failure -> Failure(result1.error).toMono() - } - } - -//fun MonoResult.flatMap(mapper: (S) -> MonoResult, errorMapper: (E) -> E2): MonoResult = TODO() -// -//fun MonoResult.flatMapResult(mapper: (S) -> Result, errorMapper: (E) -> E2): MonoResult = TODO() - - -fun MonoResult.flatMapSuccess(mapper: (S) -> Mono, errorMapper: (Throwable) -> E): MonoResult = - this.flatMap { result1 -> - when (result1) { - is Success -> mapper(result1.value).liftResult(errorMapper) - is Failure -> Failure(result1.error).toMono() - } - } - -fun Result.liftMap(mapper: (S) -> MonoResult): MonoResult = - when (this) { - is Success -> mapper(value) - is Failure -> Failure(error).toMono() - } - -fun S.justMonoResult(): MonoResult = - Result.of { this } - .let { Mono.just(it) } - -fun E.failedMonoResult(): MonoResult = - Result.error(this) - .let { Mono.just(it) } - -fun Result.liftMono(): MonoResult = - this.toMono() - -fun Mono.liftResult(errorMapper: (Throwable) -> E): MonoResult = - this.map { Result.success(it) as Result } - .onErrorResume { Result.error(errorMapper(it)).toMono() } - -//fun MonoResult.any(predicate: (V) -> Boolean): Boolean = TODO() \ No newline at end of file diff --git a/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/arrowExtensions.kt b/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/arrowExtensions.kt new file mode 100644 index 0000000..a982459 --- /dev/null +++ b/result-reactor/src/main/kotlin/com/virtuslab/basetypes/result/reactor/arrowExtensions.kt @@ -0,0 +1,41 @@ +package com.virtuslab.basetypes.result.reactor + +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import arrow.fx.IO +import arrow.fx.IOResult +import arrow.fx.reactor.MonoK +import arrow.fx.reactor.k +import arrow.fx.reactor.value +import com.virtuslab.basetypes.Either.reactor.MonoEither +import reactor.core.publisher.Mono + + +fun IO.toMonoK(): MonoEither = + Mono.create> { sink -> + val dispose = unsafeRunAsyncCancellableEither { result -> + result.fold( + { sink.error(it) }, + { sink.success(Either.left(it)) }, + { sink.success(Either.right(it)) } + ) + } + sink.onCancel { dispose.invoke() } + }.let { MonoK(it) } + +fun MonoK.toIO(): IO = + IO.cancelable { cb -> + val dispose = this.value().subscribe({ a -> cb(IOResult.Success(a)) }, { e -> cb(IOResult.Exception(e)) }) + IO { dispose.dispose() } + } + +fun Mono.toIO(): IO = k().toIO() + +fun T.toMonoK(): MonoK = Mono.just(this).k() + +fun T.toMonoRight(): MonoEither = this.right().toMonoK() + +fun T.toMonoLeft(): MonoEither = this.left().toMonoK() + +fun Throwable.toMonoK(): MonoK = Mono.error(this).k() diff --git a/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/MonoEitherKtTest.kt b/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/MonoEitherKtTest.kt new file mode 100644 index 0000000..01a156b --- /dev/null +++ b/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/MonoEitherKtTest.kt @@ -0,0 +1,136 @@ +package com.virtuslab.basetypes.result.reactor + +import arrow.core.Either +import com.virtuslab.basetypes.Either.reactor.MonoEither +import com.virtuslab.basetypes.Either.reactor.flatMapEither +import com.virtuslab.basetypes.Either.reactor.flatMapRight +import com.virtuslab.basetypes.Either.reactor.liftEither +import com.virtuslab.basetypes.Either.reactor.liftMapRight +import com.virtuslab.basetypes.Either.reactor.liftMono +import com.virtuslab.basetypes.Either.reactor.mapLeft +import com.virtuslab.basetypes.Either.reactor.mapRight +import io.kotlintest.specs.StringSpec +import reactor.core.publisher.Mono + +internal class MonoEitherKtTest : StringSpec() { + + init { + "should map success" { + val monoEither: MonoEither = "Some value".toMonoRight() + + monoEither.mapRight { "Some other value" } + .test() + .expectNext(Either.right("Some other value")) + .verifyComplete() + } + + "should handle exception when mapping success" { + val monoEither: MonoEither = "Some value".toMonoRight() + val runtimeException = RuntimeException() + + monoEither.mapRight { if (true) throw runtimeException else "Some other value" } + .test() + .verifyError(RuntimeException::class.java) + } + + "should handle exception when mapping either success" { + val monoEither: MonoEither = "Some value".toMonoRight() + val runtimeException = RuntimeException() + + monoEither.flatMapEither { if (true) throw runtimeException else Either.right("Some other value") } + .test() + .verifyError(RuntimeException::class.java) + } + + "should handle exception when flatMapping MonoEither" { + val monoEither: MonoEither = "Some value".toMonoRight() + val runtimeException = RuntimeException() + + monoEither.flatMapRight { if (true) throw runtimeException else "Some other value".toMonoRight() } + .test() + .verifyError(RuntimeException::class.java) + } + + "should keep error when mapping success" { + val monoEither: MonoEither = "Some failure".toMonoLeft() + + monoEither.mapRight { "Some other value" } + .test() + .expectNext(Either.left("Some failure")) + .verifyComplete() + } + + "should be able to map left in either"{ + val monoEither: MonoEither = "exception".toMonoLeft() + + monoEither.mapLeft { "Left from ${it}" } + .test() + .expectNext(Either.left("Left from exception")) + .verifyComplete() + } + + "should flatMap success"{ + val monoEither: MonoEither = "Some value".toMonoRight() + + monoEither.flatMapEither { x: String -> Either.right("$x some other") } + .test() + .expectNext(Either.right("Some value some other")) + .verifyComplete() + } + + "should keep error when flatMapping either success" { + val monoEither: MonoEither = "Some left".toMonoLeft() + + monoEither.flatMapEither { x: String -> Either.right("$x some other") } + .test() + .expectNext(Either.left("Some left")) + .verifyComplete() + } + + "should flatMap MonoEither"{ + val monoEither: MonoEither = "Some value".toMonoRight() + + monoEither.flatMapRight { x: String -> Either.right("$x some other").liftMono() } + .test() + .expectNext(Either.right("Some value some other")) + .verifyComplete() + } + + "should keep error when flatMapping MonoEither"{ + val monoEither: MonoEither = "Some failure".toMonoLeft() + + monoEither.flatMapRight { x: String -> (Either.right("$x some other") as Either).liftMono() } + .test() + .expectNext(Either.left("Some failure")) + .verifyComplete() + } + + "should liftmap either"{ + val either = Either.right("Some value") + + either.liftMapRight { x -> "$x 2".toMonoRight() } + .test() + .expectNext(Either.right("Some value 2")) + .verifyComplete() + } + + "should convert Mono with error to MonoEither"{ + val mono = Mono.error(RuntimeException("Runtime exception")) + + mono.liftEither { it.localizedMessage } + .test() + .expectNext(Either.left("Runtime exception")) + .verifyComplete() + } + + "should convert Mono with success to MonoEither"{ + val mono = Mono.just("Some value") + + mono.liftEither { it.localizedMessage } + .test() + .expectNext(Either.right("Some value")) + .verifyComplete() + } + } +} + diff --git a/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/MonoResultKtTest.kt b/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/MonoResultKtTest.kt deleted file mode 100644 index 616db23..0000000 --- a/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/MonoResultKtTest.kt +++ /dev/null @@ -1,163 +0,0 @@ -package com.virtuslab.basetypes.result.reactor - -import com.virtuslab.basetypes.result.Result -import io.kotlintest.specs.StringSpec -import reactor.core.publisher.Mono -import reactor.core.publisher.toMono -import reactor.test.test - -internal class MonoResultKtTest : StringSpec() { - - init { - "should map success" { - val monoResult: MonoResult = "Some value".justMonoResult() - - monoResult.mapSuccess { "Some other value" } - .test() - .expectNext(Result.success("Some other value")) - .verifyComplete() - } - - "should handle exception when mapping success" { - val monoResult: MonoResult = "Some value".justMonoResult() - val runtimeException = RuntimeException() - - monoResult.mapSuccess { if (true) throw runtimeException else "Some other value" } - .test() - .verifyError(RuntimeException::class.java) - } - - "should handle exception when mapping result success" { - val monoResult: MonoResult = "Some value".justMonoResult() - val runtimeException = RuntimeException() - - monoResult.flatMapResult { if (true) throw runtimeException else Result.success("Some other value") } - .test() - .verifyError(RuntimeException::class.java) - } - - "should handle exception when flatMapping MonoResult" { - val monoResult: MonoResult = "Some value".justMonoResult() - val runtimeException = RuntimeException() - - monoResult.flatMapSuccess { if (true) throw runtimeException else "Some other value".justMonoResult() } - .test() - .verifyError(RuntimeException::class.java) - } - - "should keep error when mapping success" { - val monoResult: MonoResult = SomeFailure("Some failure").failedMonoResult() - - monoResult.mapSuccess { "Some other value" } - .test() - .expectNext(Result.error(SomeFailure("Some failure"))) - .verifyComplete() - } - - "should be able to map error in result"{ - val monoResult: MonoResult = Result.error(RuntimeException("exception")).liftMono() - - monoResult.mapFailure { SomeFailure("Failure from ${it.localizedMessage}") } - .test() - .expectNext(Result.error(SomeFailure("Failure from exception"))) - .verifyComplete() - } - - "should flatMap success"{ - val monoResult: MonoResult = "Some value".justMonoResult() - - monoResult.flatMapResult { x: String -> Result.success("$x some other") } - .test() - .expectNext(Result.success("Some value some other")) - .verifyComplete() - } - - "should keep error when flatMapping result success" { - val monoResult: MonoResult = SomeFailure("Some failure").failedMonoResult() - - monoResult.flatMapResult { x: String -> Result.success("$x some other") } - .test() - .expectNext(Result.error(SomeFailure("Some failure"))) - .verifyComplete() - } - - "should flatMap MonoResult"{ - val monoResult: MonoResult = "Some value".justMonoResult() - - monoResult.flatMapSuccess { x: String -> Result.success("$x some other").liftMono() } - .test() - .expectNext(Result.success("Some value some other")) - .verifyComplete() - } - - "should keep error when flatMapping MonoResult"{ - val monoResult: MonoResult = SomeFailure("Some failure").failedMonoResult() - - monoResult.flatMapSuccess { x: String -> (Result.success("$x some other") as Result).liftMono() } - .test() - .expectNext(Result.error(SomeFailure("Some failure"))) - .verifyComplete() - } - - "should flatMap Mono"{ - val monoResult: MonoResult = "Some value".justMonoResult() - - monoResult.flatMapSuccess( - mapper = { x: String -> "$x some other".toMono() }, - errorMapper = { ex -> RuntimeException(ex) }) - .test() - .expectNext(Result.success("Some value some other")) - .verifyComplete() - } - - "should keep error when flatmapping Mono"{ - val monoResult: MonoResult = SomeFailure("Some failure").failedMonoResult() - - monoResult.flatMapSuccess( - mapper = { x: String -> "$x some other".toMono() }, - errorMapper = { ex -> SomeFailure(ex.localizedMessage) }) - .test() - .expectNext(Result.error(SomeFailure("Some failure"))) - .verifyComplete() - } - - "should flatMap failed Mono"{ - val monoResult: MonoResult = "Some value".justMonoResult() - - monoResult.flatMapSuccess( - mapper = { x: String -> RuntimeException("$x some other").toMono() }, - errorMapper = { ex -> SomeFailure(ex.localizedMessage) }) - .test() - .expectNext(Result.error(SomeFailure("Some value some other"))) - .verifyComplete() - } - - "should liftmap result"{ - val result = Result.success("Some value") - - result.liftMap { x -> "$x Some value".justMonoResult() } - .test() - .expectNext(Result.success("Some value Some value")) - .verifyComplete() - } - - "should convert Mono with error to MonoResult"{ - val mono = Mono.error(RuntimeException("Runtime exception")) - - mono.liftResult { SomeFailure(it.localizedMessage) } - .test() - .expectNext(Result.error(SomeFailure("Runtime exception"))) - .verifyComplete() - } - - "should convert Mono with success to MonoResult"{ - val mono = Mono.just("Some value") - - mono.liftResult { SomeFailure(it.localizedMessage) } - .test() - .expectNext(Result.success("Some value")) - .verifyComplete() - } - } -} - diff --git a/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/SomeFailure.kt b/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/SomeFailure.kt deleted file mode 100644 index 9b02d91..0000000 --- a/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/SomeFailure.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.virtuslab.basetypes.result.reactor - -data class SomeFailure(val errorMessage: String) : Exception() \ No newline at end of file diff --git a/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/helpers.kt b/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/helpers.kt new file mode 100644 index 0000000..fcf9863 --- /dev/null +++ b/result-reactor/src/test/kotlin/com/virtuslab/basetypes/result/reactor/helpers.kt @@ -0,0 +1,7 @@ +package com.virtuslab.basetypes.result.reactor + +import arrow.core.Either +import com.virtuslab.basetypes.Either.reactor.MonoEither +import reactor.test.test + +fun MonoEither.test() = mono.map { it as Either }.test() \ No newline at end of file diff --git a/result-rxjava/build.gradle.kts b/result-rxjava/build.gradle.kts index 95c233d..d95b230 100644 --- a/result-rxjava/build.gradle.kts +++ b/result-rxjava/build.gradle.kts @@ -16,8 +16,9 @@ dependencies { implementation(kotlin("stdlib", kotlinVersion)) implementation("io.reactivex.rxjava2:rxjava:2.2.14") - api(project(":result")) implementation("io.arrow-kt:arrow-core:$arrow") + implementation("io.arrow-kt:arrow-fx:$arrow") + implementation("io.arrow-kt:arrow-fx-rx2:$arrow") testImplementation("io.kotlintest:kotlintest-runner-junit5:$kotlintest") testImplementation("org.junit.jupiter:junit-jupiter-engine:$junit") diff --git a/result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/SingleEither.kt b/result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/SingleEither.kt new file mode 100644 index 0000000..13492a3 --- /dev/null +++ b/result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/SingleEither.kt @@ -0,0 +1,68 @@ +package com.virtuslab.basetypes.result.rxjava + +import arrow.core.Either +import arrow.core.flatMap +import arrow.fx.rx2.SingleK +import arrow.fx.rx2.handleErrorWith +import arrow.fx.rx2.k +import io.reactivex.Single + + +typealias SingleEither = SingleK> + + +fun SingleEither.mapRight(mapper: (S) -> S2): SingleEither = + this.map { + it.map(mapper) + } + +fun SingleEither.mapLeft(mapper: (E) -> E2): SingleEither = + this.map { + it.mapLeft(mapper) + } + +fun SingleEither.flatMapEither(mapper: (S) -> Either): SingleEither = + this.map { + it.flatMap(mapper) + } + +fun SingleEither.flatMapRight(mapper: (S) -> SingleEither): SingleEither = + this.flatMap { either -> + when (either) { + is Either.Right -> mapper(either.b) + is Either.Left -> Either.left(either.a).toSingleK() + } + } + +//fun SingleEither.flatMap(mapper: (S) -> SingleEither, errorMapper: (E) -> E2): SingleEither = TODO() +// +//fun SingleEither.flatMapEither(mapper: (S) -> Either, errorMapper: (E) -> E2): SingleEither = TODO() + +fun Either.liftMapRight(mapper: (S) -> SingleEither): SingleEither = + when (this) { + is Either.Right -> mapper(b) + is Either.Left -> Either.left(a).toSingleK() + } + +fun SingleK.Companion.justRight(s: S): SingleEither = Either.right(s).toSingleK() + +fun SingleK.Companion.justLeft(e: E): SingleEither = Either.left(e).toSingleK() + +fun Either.liftSingle(): SingleEither = this.toSingleK() + +fun SingleK.liftEither(): SingleEither = this.map { Either.right(it) as Either } + +fun Single.liftEither(): SingleEither = k().liftEither() + +fun SingleK.liftEither(errorMapper: (Throwable) -> E): SingleEither = + this.map { Either.right(it) as Either } + .handleErrorWith { Either.left(errorMapper(it)).toSingleK() } + +fun Single.liftEither(errorMapper: (Throwable) -> E): SingleEither = k().liftEither(errorMapper) + +fun SingleEither.any(predicate: (V) -> Boolean): SingleK = this.map { + when (it) { + is Either.Right -> predicate(it.b) + else -> false + } +} diff --git a/result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/SingleResult.kt b/result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/SingleResult.kt deleted file mode 100644 index 9caf715..0000000 --- a/result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/SingleResult.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.virtuslab.basetypes.result.rxjava - -import arrow.core.None -import arrow.core.Option -import arrow.core.Some -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 -import io.reactivex.Single - -typealias SingleResult = Single> - - -fun SingleResult.mapSuccess(mapper: (S) -> S2): SingleResult = - this.map { - it.map(mapper) - } - -fun SingleResult.mapFailure(mapper: (E) -> E2): SingleResult = - this.map { - it.mapError(mapper) - } - -fun SingleResult.flatMapResult(mapper: (S) -> Result): SingleResult = - this.map { - it.flatMap(mapper) - } - -fun SingleResult.flatMapSuccess(mapper: (S) -> SingleResult): SingleResult = - this.flatMap { result1 -> - when (result1) { - is Success -> mapper(result1.value) - is Failure -> Failure(result1.error).toSingle() - } - } - -//fun SingleResult.flatMap(mapper: (S) -> SingleResult, errorMapper: (E) -> E2): SingleResult = TODO() -// -//fun SingleResult.flatMapResult(mapper: (S) -> Result, errorMapper: (E) -> E2): SingleResult = TODO() - - -fun SingleResult.flatMapSuccess(mapper: (S) -> Single, errorMapper: (Throwable) -> E): SingleResult = - this.flatMap { result1 -> - when (result1) { - is Success -> mapper(result1.value).liftResult(errorMapper) - is Failure -> Failure(result1.error).toSingle() - } - } - -fun Result.liftMap(mapper: (S) -> SingleResult): SingleResult = - when (this) { - is Success -> mapper(value) - is Failure -> Failure(error).toSingle() - } - -fun S.justSingleResult(): SingleResult = - Result.of { this } - .let { Single.just(it) } - - -fun E.failedSingleResult(): SingleResult = - Result.error(this) - .let { Single.just(it) } - -fun Result.liftSingle(): SingleResult = - this.toSingle() - -fun Single.liftResult(errorMapper: (Throwable) -> E): SingleResult = - this.map { Result.success(it) as Result } - .onErrorReturn { Result.error(errorMapper(it)) } - -fun S.toSingle(): Single = Single.just(this) - -// TODO test -fun Result.toFailure(): Option = this.fold({ None }, { Some(it) }) - -// TODO test -fun SingleResult.toFailure(): Single> = this.map { it.toFailure() } - -// TODO test -fun Result.toSuccess(): Option = this.fold({ Some(it) }, { None }) - -// TODO test -fun SingleResult.toSuccess(): Single> = this.map { it.toSuccess() } \ No newline at end of file diff --git a/result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/arrowExtensions.kt b/result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/arrowExtensions.kt new file mode 100644 index 0000000..1d78c48 --- /dev/null +++ b/result-rxjava/src/main/kotlin/com/virtuslab/basetypes/result/rxjava/arrowExtensions.kt @@ -0,0 +1,17 @@ +package com.virtuslab.basetypes.result.rxjava + +import arrow.core.left +import arrow.core.right +import arrow.fx.IO +import arrow.fx.rx2.SingleK +import arrow.fx.rx2.k +import io.reactivex.Single + + +fun T.toSingleK(): SingleK = Single.just(this).k() + +fun T.toSingleRight(): SingleEither = this.right().toSingleK() + +fun T.toSingleLeft(): SingleEither = this.left().toSingleK() + +fun Throwable.toSingleK(): SingleK = Single.error(this).k() diff --git a/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SingleEitherKtTest.kt b/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SingleEitherKtTest.kt new file mode 100644 index 0000000..39ea2b8 --- /dev/null +++ b/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SingleEitherKtTest.kt @@ -0,0 +1,128 @@ +package com.virtuslab.basetypes.result.rxjava + +import arrow.core.Either +import io.kotlintest.specs.StringSpec +import io.reactivex.Single + + +internal class SingleEitherKtTest : StringSpec() { + init { + "should map success" { + val singleEither: SingleEither = "Some value".toSingleRight() + + singleEither.mapRight { "Some other value" } + .test() + .assertResult(Either.right("Some other value")) + .assertComplete() + } + + "should handle exception when mapping success" { + val singleEither: SingleEither = "Some value".toSingleRight() + val runtimeException = RuntimeException() + + singleEither.mapRight { if (true) throw runtimeException else "Some other value" } + .test() + .assertError(RuntimeException::class.java) + } + + "should handle exception when mapping either success" { + val singleEither: SingleEither = "Some value".toSingleRight() + val runtimeException = RuntimeException() + + singleEither.flatMapEither { if (true) throw runtimeException else Either.right("Some other value") } + .test() + .assertError(RuntimeException::class.java) + } + + "should handle exception when flatMapping SingleEither" { + val singleEither: SingleEither = "Some value".toSingleRight() + val runtimeException = RuntimeException() + + singleEither.flatMapRight { if (true) throw runtimeException else "Some other value".toSingleRight() } + .test() + .assertError(RuntimeException::class.java) + } + + "should keep error when mapping success" { + val singleEither: SingleEither = "Some failure".toSingleLeft() + + singleEither.mapRight { "Some other value" } + .test() + .assertResult(Either.left("Some failure")) + .assertComplete() + } + + "should be able to map left in either"{ + val singleEither: SingleEither = "exception".toSingleLeft() + + singleEither.mapLeft { "Left from ${it}" } + .test() + .assertResult(Either.left("Left from exception")) + .assertComplete() + } + + "should flatMap success"{ + val singleEither: SingleEither = "Some value".toSingleRight() + + singleEither.flatMapEither { x: String -> Either.right("$x some other") } + .test() + .assertResult(Either.right("Some value some other")) + .assertComplete() + } + + "should keep error when flatMapping either success" { + val singleEither: SingleEither = "Some left".toSingleLeft() + + singleEither.flatMapEither { x: String -> Either.right("$x some other") } + .test() + .assertResult(Either.left("Some left")) + .assertComplete() + } + + "should flatMap SingleEither"{ + val singleEither: SingleEither = "Some value".toSingleRight() + + singleEither.flatMapRight { x: String -> Either.right("$x some other").liftSingle() } + .test() + .assertResult(Either.right("Some value some other")) + .assertComplete() + } + + "should keep error when flatMapping SingleEither"{ + val singleEither: SingleEither = "Some failure".toSingleLeft() + + singleEither.flatMapRight { x: String -> (Either.right("$x some other") as Either).liftSingle() } + .test() + .assertResult(Either.left("Some failure")) + .assertComplete() + } + + "should liftmap either"{ + val either = Either.right("Some value") + + either.liftMapRight { x -> "$x 2".toSingleRight() } + .test() + .assertResult(Either.right("Some value 2")) + .assertComplete() + } + + "should convert Single with error to SingleEither"{ + val single = Single.error(RuntimeException("Runtime exception")) + + single.liftEither { it.localizedMessage } + .test() + .assertResult(Either.left("Runtime exception")) + .assertComplete() + } + + "should convert Single with success to SingleEither"{ + val single = Single.just("Some value") + + single.liftEither { it.localizedMessage } + .test() + .assertResult(Either.right("Some value")) + .assertComplete() + } + } + +} diff --git a/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SingleResultKtTest.kt b/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SingleResultKtTest.kt deleted file mode 100644 index 8635d5c..0000000 --- a/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SingleResultKtTest.kt +++ /dev/null @@ -1,162 +0,0 @@ -package com.virtuslab.basetypes.result.rxjava - -import com.virtuslab.basetypes.result.Result -import io.kotlintest.specs.StringSpec -import io.reactivex.Single - - -internal class SingleResultKtTest: StringSpec() { - init { - "should map success" { - val monoResult: SingleResult = "Some value".justSingleResult() - - monoResult.mapSuccess { "Some other value" } - .test() - .assertResult(Result.success("Some other value")) - .assertComplete() - } - - "should propagate exception when mapping success" { - val monoResult: SingleResult = "Some value".justSingleResult() - val runtimeException = RuntimeException() - - monoResult.mapSuccess { if (true) throw runtimeException else "Some other value" } - .test() - .assertError(runtimeException) - } - - "should propagate exception when mapping result success" { - val monoResult: SingleResult = "Some value".justSingleResult() - val runtimeException = RuntimeException() - - monoResult.flatMapResult { if (true) throw runtimeException else Result.success("Some other value") } - .test() - .assertError(runtimeException) - } - - "should propagate exception when flatMapping SingleResult" { - val monoResult: SingleResult = "Some value".justSingleResult() - val runtimeException = RuntimeException() - - monoResult.flatMapSuccess { if (true) throw runtimeException else "Some other value".justSingleResult() } - .test() - .assertError(runtimeException) - } - - "should keep error when mapping success" { - val monoResult: SingleResult = SomeFailure("Some failure").failedSingleResult() - - monoResult.mapSuccess { "Some other value" } - .test() - .assertResult(Result.error(SomeFailure("Some failure"))) - .assertComplete() - } - - "should be able to map error in result"{ - val monoResult: SingleResult = Result.error(RuntimeException("exception")).liftSingle() - - monoResult.mapFailure { SomeFailure("Failure from ${it.localizedMessage}") } - .test() - .assertResult(Result.error(SomeFailure("Failure from exception"))) - .assertComplete() - } - - "should flatMap success"{ - val monoResult: SingleResult = "Some value".justSingleResult() - - monoResult.flatMapResult { x: String -> Result.success("$x some other") } - .test() - .assertResult(Result.success("Some value some other")) - .assertComplete() - } - - "should keep error when flatMapping result success" { - val monoResult: SingleResult = SomeFailure("Some failure").failedSingleResult() - - monoResult.flatMapResult { x: String -> Result.success("$x some other") } - .test() - .assertResult(Result.error(SomeFailure("Some failure"))) - .assertComplete() - } - - "should flatMap SingleResult"{ - val monoResult: SingleResult = "Some value".justSingleResult() - - monoResult.flatMapSuccess { x: String -> Result.success("$x some other").liftSingle() } - .test() - .assertResult(Result.success("Some value some other")) - .assertComplete() - } - - "should keep error when flatMapping SingleResult"{ - val monoResult: SingleResult = SomeFailure("Some failure").failedSingleResult() - - monoResult.flatMapSuccess { x: String -> (Result.success("$x some other") as Result).liftSingle() } - .test() - .assertResult(Result.error(SomeFailure("Some failure"))) - .assertComplete() - } - - "should flatMap Single"{ - val monoResult: SingleResult = "Some value".justSingleResult() - - monoResult.flatMapSuccess( - mapper = { x: String -> "$x some other".toSingle() }, - errorMapper = { ex -> RuntimeException(ex) }) - .test() - .assertResult(Result.success("Some value some other")) - .assertComplete() - } - - "should keep error when flatmapping Single"{ - val monoResult: SingleResult = SomeFailure("Some failure").failedSingleResult() - - monoResult.flatMapSuccess( - mapper = { x: String -> "$x some other".toSingle() }, - errorMapper = { ex -> SomeFailure(ex.localizedMessage) }) - .test() - .assertResult(Result.error(SomeFailure("Some failure"))) - .assertComplete() - } - -// TODO: -// "should flatMap failed Single"{ -// val monoResult: SingleResult = "Some value".justSingleResult() -// -// monoResult.flatMapSuccess( -// mapper = { x: String -> RuntimeException("$x some other").toSingle() }, -// errorMapper = { ex -> SomeFailure(ex.localizedMessage) }) -// .test() -// .assertResult(Result.error(SomeFailure("Some value some other"))) -// .assertComplete() -// } - - "should liftmap result"{ - val result = Result.success("Some value") - - result.liftMap { x -> "$x Some value".justSingleResult() } - .test() - .assertResult(Result.success("Some value Some value")) - .assertComplete() - } - - "should convert Single with error to SingleResult"{ - val mono = Single.error(RuntimeException("Runtime exception")) - - mono.liftResult { SomeFailure(it.localizedMessage) } - .test() - .assertResult(Result.error(SomeFailure("Runtime exception"))) - .assertComplete() - } - - "should convert Single with success to SingleResult"{ - val mono = Single.just("Some value") - - mono.liftResult { SomeFailure(it.localizedMessage) } - .test() - .assertResult(Result.success("Some value")) - .assertComplete() - } - } - -} diff --git a/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SomeFailure.kt b/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SomeFailure.kt deleted file mode 100644 index a41a6d3..0000000 --- a/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/SomeFailure.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.virtuslab.basetypes.result.rxjava - -data class SomeFailure(val errorMessage: String) : Exception() \ No newline at end of file diff --git a/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/helpers.kt b/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/helpers.kt new file mode 100644 index 0000000..fcb13eb --- /dev/null +++ b/result-rxjava/src/test/kotlin/com/virtuslab/basetypes/result/rxjava/helpers.kt @@ -0,0 +1,6 @@ +package com.virtuslab.basetypes.result.rxjava + +import arrow.core.Either + + +fun SingleEither.test() = single.map { it as Either }.test() \ No newline at end of file diff --git a/result/.gitignore b/result/.gitignore deleted file mode 100644 index a774788..0000000 --- a/result/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build - diff --git a/result/LICENSE.md b/result/LICENSE.md deleted file mode 100644 index 22aae75..0000000 --- a/result/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Kittinun Vantasin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/result/build.gradle.kts b/result/build.gradle.kts deleted file mode 100644 index be76654..0000000 --- a/result/build.gradle.kts +++ /dev/null @@ -1,17 +0,0 @@ -sourceSets { - getByName("main").java.srcDirs("src/main/kotlin") - getByName("test").java.srcDirs("src/main/kotlin") -} - -dependencies { - val kotlinVersion: String by project - val junit: String by project - val arrow: String by project - - implementation(kotlin("stdlib", kotlinVersion)) - implementation("io.arrow-kt:arrow-core:$arrow") - - testImplementation("junit:junit:4.12") - - -} diff --git a/result/src/main/kotlin/com/virtuslab/basetypes/result/NoException.kt b/result/src/main/kotlin/com/virtuslab/basetypes/result/NoException.kt deleted file mode 100644 index b659186..0000000 --- a/result/src/main/kotlin/com/virtuslab/basetypes/result/NoException.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.virtuslab.basetypes.result - -class NoException private constructor() : Exception() \ No newline at end of file diff --git a/result/src/main/kotlin/com/virtuslab/basetypes/result/Result.kt b/result/src/main/kotlin/com/virtuslab/basetypes/result/Result.kt deleted file mode 100644 index 00d30a7..0000000 --- a/result/src/main/kotlin/com/virtuslab/basetypes/result/Result.kt +++ /dev/null @@ -1,159 +0,0 @@ -package com.virtuslab.basetypes.result - -import arrow.core.Either -import arrow.core.None -import arrow.core.Option -import arrow.core.some -import arrow.core.toOption - -inline fun Result<*, *>.getAs(): Option = when (this) { - is Result.Success -> value as? X - is Result.Failure -> error as? X -}.toOption() - -fun Result.handleSuccess(f: (V) -> Unit) = fold(f, {}) - -fun Result<*, E>.handleError(f: (E) -> Unit) = fold({}, f) - -infix fun Result.or(fallback: V) = when (this) { - is Result.Success -> this - else -> Result.Success(fallback) -} - -infix fun Result.getOrElse(fallback: V) = when (this) { - is Result.Success -> value - else -> fallback -} - -inline fun Result.map(transform: (V) -> U): Result = - when (this) { - is Result.Success -> Result.Success(transform(value)) - is Result.Failure -> this - } - -inline fun Result.flatMap(transform: (V) -> Result): Result = - when (this) { - is Result.Success -> transform(value) - is Result.Failure -> this - } - -fun Result.mapError(transform: (E) -> E2) = when (this) { - is Result.Success -> this - is Result.Failure -> Result.Failure(transform(error)) -} - -fun Result.flatMapError(transform: (E) -> Result) = when (this) { - is Result.Success -> this - is Result.Failure -> transform(error) -} - -fun Result.any(predicate: (V) -> Boolean): Boolean = - when (this) { - is Result.Success -> predicate(value) - is Result.Failure -> false - } - -fun Result.pairWith(s: S): Result, E> = map { it to s } - -fun Result.zip(other: () -> Result): Result, *> = - flatMap { outer -> other().map { outer to it } } - -fun List>.sequence(): Result, E> = fold(Result.success(mutableListOf()) as Result, E>) { acc, result -> - acc.flatMap { combine -> - result.map { combine.apply { add(it) } } - } -} - -fun T.toResult() = Result.success(this) - -fun E.toResultFailure() = Result.error(this) - -sealed class Result { - - open operator fun component1(): V? = null - open operator fun component2(): E? = null - - fun getSuccess(): Option = when (this) { - is Success -> this.value.some() - is Failure -> None - } - - fun getFailure(): Option = when (this) { - is Success -> None - is Failure -> this.error.some() - } - - fun toEither(): Either = when (this) { - is Success -> Either.right(this.value) - is Failure -> Either.left(this.error) - } - - inline fun fold(success: (V) -> X, failure: (E) -> X): X = when (this) { - is Success -> success(this.value) - is Failure -> failure(this.error) - } - - abstract fun getSuccessUnsafe(): V - - abstract fun isSuccess(): Boolean - abstract fun isFailure(): Boolean - - class Success(val value: V) : Result() { - override fun component1(): V? = value - - override fun getSuccessUnsafe(): V = value - - override fun toString() = "[Success: $value]" - - override fun hashCode(): Int = value.hashCode() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - return other is Success<*> && value == other.value - } - - override fun isSuccess() = true - - override fun isFailure() = false - } - - class Failure(val error: E) : Result() { - override fun component2(): E? = error - - override fun getSuccessUnsafe() = throw NoSuchElementException("No failure - Result is successful") - - fun getException(): E = error - - override fun toString() = "[Failure: $error]" - - override fun hashCode(): Int = error.hashCode() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - return other is Failure<*> && error == other.error - } - - override fun isSuccess() = false - - override fun isFailure() = true - } - - companion object { - // Factory methods - fun error(ex: E) = Failure(ex) - - fun success(v: V) = Success(v) - - fun of(value: V?, fallback: (() -> Any) = { Unit }): Result = - value?.let { success(it) } ?: error(fallback()) - - fun of(f: () -> V): Result = success(f()) - - fun ofSafe(f: () -> V, fallback: (Throwable) -> E): Result = try { - success(f()) - } catch (ex: Throwable) { - error(fallback(ex)) - } - } - -} diff --git a/result/src/main/kotlin/com/virtuslab/basetypes/result/Validation.kt b/result/src/main/kotlin/com/virtuslab/basetypes/result/Validation.kt deleted file mode 100644 index 8a8f1de..0000000 --- a/result/src/main/kotlin/com/virtuslab/basetypes/result/Validation.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.virtuslab.basetypes.result - -class Validation(vararg resultSequence: Result<*, E>) { - - val failures: List = resultSequence.filterIsInstance>().map { it.getException() } - - val hasFailure = failures.isNotEmpty() -} diff --git a/result/src/test/assets/bar.txt b/result/src/test/assets/bar.txt deleted file mode 100644 index ae75f35..0000000 --- a/result/src/test/assets/bar.txt +++ /dev/null @@ -1 +0,0 @@ -Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. \ No newline at end of file diff --git a/result/src/test/assets/foo.txt b/result/src/test/assets/foo.txt deleted file mode 100644 index e83bcc9..0000000 --- a/result/src/test/assets/foo.txt +++ /dev/null @@ -1 +0,0 @@ -Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. \ No newline at end of file diff --git a/result/src/test/kotlin/com/virtuslab/basetypes/result/ResultTests.kt b/result/src/test/kotlin/com/virtuslab/basetypes/result/ResultTests.kt deleted file mode 100644 index af52f6f..0000000 --- a/result/src/test/kotlin/com/virtuslab/basetypes/result/ResultTests.kt +++ /dev/null @@ -1,418 +0,0 @@ -package com.virtuslab.basetypes.result - -import org.hamcrest.CoreMatchers.equalTo -import org.hamcrest.CoreMatchers.instanceOf -import org.hamcrest.CoreMatchers.notNullValue -import org.hamcrest.CoreMatchers.nullValue -import org.junit.Assert.assertThat -import org.junit.Ignore -import org.junit.Test -import java.io.File -import java.io.FileNotFoundException - -class ResultTests { - - @Test - fun createValue() { - val v = Result.of(1) - - assertThat("Result is created successfully", v, notNullValue()) - assertThat("v is Result.Success type", v, instanceOf(Result.Success::class.java)) - } - - @Test - fun createError() { - val e = Result.error(RuntimeException()) - - assertThat("Result is created successfully", e, notNullValue()) - assertThat("e is Result.Failure type", e, instanceOf(Result.Failure::class.java)) - } - - @Test - fun createOptionalValue() { - val value1: String? = null - val value2: String? = "1" - - val result1 = Result.of(value1) { UnsupportedOperationException("value is null") } - val result2 = Result.of(value2) { IllegalStateException("value is null") } - - assertThat("result1 is Result.Failure type", result1, instanceOf(Result.Failure::class.java)) - assertThat("result2 is Result.Success type", result2, instanceOf(Result.Success::class.java)) - } - - @Test - @Ignore - fun createFromLambda() { - val f1 = { "foo" } - val f2 = { - val v = arrayListOf() - v[1] - } - - val f3 = { - val s: String? - s = null - s - } - - val result1 = Result.of(f1) - val result2 = Result.of(f2) - val result3 = Result.of(f3()) - - assertThat("result1 is Result.Success type", result1, instanceOf(Result.Success::class.java)) - assertThat("result2 is Result.Failure type", result2, instanceOf(Result.Failure::class.java)) - assertThat("result3 is Result.Failure type", result3, instanceOf(Result.Failure::class.java)) - } - - @Test - fun or() { - val one = Result.of(null) or 1 - - assertThat("one is Result.Success type", one, instanceOf(Result.Success::class.java)) - assertThat("value one is 1", one.component1()!!, equalTo(1)) - } - - @Test - fun orElse() { - val one = Result.of(null) getOrElse 1 - - assertThat("one is 1", one, equalTo(1)) - } - - @Test - fun success() { - val result = Result.of { true } - - var beingCalled = false - result.handleSuccess { - beingCalled = true - } - - var notBeingCalled = true - result.handleError { - notBeingCalled = false - } - - assertThat(beingCalled, equalTo(true)) - assertThat(notBeingCalled, equalTo(true)) - } - - @Test(expected = FileNotFoundException::class) - fun failure() { - val result = Result.of { File("not_found_file").readText() } - - var beingCalled = false - result.handleError { - beingCalled = true - } - - var notBeingCalled = true - result.handleSuccess { - notBeingCalled = false - } - - assertThat(beingCalled, equalTo(true)) - assertThat(notBeingCalled, equalTo(true)) - } - - @Test - @Ignore - fun get() { - val f1 = { true } - val f2 = { File("not_found_file").readText() } - - val result1 = Result.of(f1) - val result2 = Result.of(f2) - - assertThat("result1 is true", result1.getSuccessUnsafe(), equalTo(true)) - - var result = false - try { - result2.getSuccessUnsafe() - } catch (e: FileNotFoundException) { - result = true - } - - assertThat("result2 expecting to throw FileNotFoundException", result, equalTo(true)) - } - - @Suppress("UNUSED_VARIABLE") - @Test - fun getAsValue() { - val result1 = Result.of(22) - val result2 = Result.error(KotlinNullPointerException()) - - val v1: Int = result1.getAs().orNull()!! - val (v2, err) = result2 - - assertThat("v1 is equal 22", v1, equalTo(22)) - assertThat("err is KotlinNullPointerException type", err is KotlinNullPointerException, equalTo(true)) - } - - @Test - fun fold() { - val success = Result.of("success") - val failure = Result.error(RuntimeException("failure")) - - val v1 = success.fold({ 1 }, { 0 }) - val v2 = failure.fold({ 1 }, { 0 }) - - assertThat("v1 is equal 1", v1, equalTo(1)) - assertThat("v2 is equal 1", v2, equalTo(0)) - } - - @Test - fun map() { - val success: Result = Result.success("success") - val failure: Result = Result.error(RuntimeException("failure")) - - val v1 = success.map { it.count() } - val v2 = failure.map { it.count() } - - assertThat("v1 getAsInt equals 7", v1.getAs().orNull(), equalTo(7)) - assertThat("v2 getAsInt null", v2.getAs().orNull(), nullValue()) - } - - @Test - fun flatMap() { - val success: Result = Result.success("success") - val failure: Result = Result.error(RuntimeException("failure")) - - val v1 = success.flatMap { Result.of(it.last()) } - val v2 = failure.flatMap { Result.of(it.count()) } - - assertThat("v1 getAsChar equals s", v1.getAs().orNull(), equalTo('s')) - assertThat("v2 getAsInt null", v2.getAs().orNull(), nullValue()) - } - - @Test - fun mapError() { - val success = Result.success("success") as Result - val failure = Result.error(Exception("failure")) - - val v1 = success.mapError { InstantiationException(it.message) } - val v2 = failure.mapError { InstantiationException(it.message) } - - assertThat("v1 is success", v1, instanceOf(Result.Success::class.java)) - assertThat("v1 is success", v1.component1(), equalTo("success")) - assertThat("v2 is failure", v2, instanceOf(Result.Failure::class.java)) - assertThat("v2 is failure", v2.component2()!!.message, equalTo("failure")) - } - - @Test - fun flatMapError() { - val success = Result.of("success") - val failure = Result.error(Exception("failure")) - - val v1 = success.flatMapError { Result.error(IllegalArgumentException()) } - val v2 = failure.flatMapError { Result.error(IllegalArgumentException()) } - - - assertThat("v1 is success", v1, instanceOf(Result.Success::class.java)) - assertThat("v1 is success", v1.getAs().orNull(), equalTo("success")) - assertThat("v2 is failure", v2, instanceOf(Result.Failure::class.java)) - assertThat("v2 is failure", v2.component2() is IllegalArgumentException, equalTo(true)) - } - - @Test - @Ignore - fun any() { - val foo = Result.of { readFromAssetFileName("foo.txt") } - val fooo = Result.of { readFromAssetFileName("fooo.txt") } - - val v1 = foo.any { "Lorem" in it } - val v2 = fooo.any { "Lorem" in it } - val v3 = foo.any { "LOREM" in it } - - assertThat(v1, equalTo(true)) - assertThat(v2, equalTo(false)) - assertThat(v3, equalTo(false)) - } - - @Test - fun anyWithThrow() { - val foo = Result.of { readFromAssetFileName("foo.txt") } - - val v1 = foo.any { "Lorem" in it } - val v2 = foo.any { "Wrong" in it } - - assertThat(v1, equalTo(true)) - assertThat(v2, equalTo(false)) - } - - @Test - @Ignore - fun composableFunctions1() { - val foo = { readFromAssetFileName("foo.txt") } - val bar = { readFromAssetFileName("bar.txt") } - - val notFound = { readFromAssetFileName("fooo.txt") } - - val (value1, error1) = Result.of(foo).map { it.count() }.mapError { IllegalStateException() } - val (value2, error2) = Result.of(notFound).map { bar }.mapError { IllegalStateException() } - - assertThat("value1 is 574", value1, equalTo(574)) - assertThat("error1 is null", error1, nullValue()) - assertThat("value2 is null", value2, nullValue()) - assertThat("error2 is Exception", error2 is IllegalStateException, equalTo(true)) - } - - @Test - fun composableFunctions2() { - val r1 = Result.of(functionThatCanReturnNull(false)).flatMap { resultReadFromAssetFileName("bar.txt") }.mapError { Exception("this should not happen") } - val r2 = Result.of(functionThatCanReturnNull(true)).map { it.rangeTo(Int.MAX_VALUE) }.mapError { KotlinNullPointerException() } - - assertThat("r1 is Result.Success type", r1, instanceOf(Result.Success::class.java)) - assertThat("r2 is Result.Failure type", r2, instanceOf(Result.Failure::class.java)) - } - - @Test - fun noException() { - val r = concat("1", "2") - assertThat("r is Result.Success type", r, instanceOf(Result.Success::class.java)) - } - - @Test - fun fanoutSuccesses() { - val readFooResult = resultReadFromAssetFileName("foo.txt") - val readBarResult = resultReadFromAssetFileName("bar.txt") - - val finalResult = readFooResult.zip { readBarResult } - val (v, e) = finalResult - - assertThat("finalResult is success", finalResult, instanceOf(Result.Success::class.java)) - assertThat("finalResult has a pair type when both are successes", v is Pair, equalTo(true)) - assertThat("value of finalResult has text from foo as left and text from bar as right", - v!!.first.startsWith("Lorem Ipsum is simply dummy text") && v.second.startsWith("Contrary to popular belief"), equalTo(true)) - assertThat("value of the second component is null", e, nullValue()) - } - - @Test - @Ignore - fun fanoutFailureOnLeft() { - val readFoooResult = resultReadFromAssetFileName("fooo.txt") - val readBarResult = resultReadFromAssetFileName("bar.txt") - - val finalResult = readFoooResult.zip { readBarResult } - val (v, e) = finalResult - - assertThat("value of the first component is null", v, nullValue()) - assertThat("finalResult is failure", finalResult, instanceOf(Result.Failure::class.java)) - assertThat("error is a file not found exception", e, instanceOf(FileNotFoundException::class.java)) - } - - @Test - @Ignore - fun fanoutFailureOnRight() { - val readFoooResult = resultReadFromAssetFileName("foo.txt") - val readBarResult = resultReadFromAssetFileName("barr.txt") - - val finalResult = readFoooResult.zip { readBarResult } - val (v, e) = finalResult - - assertThat("value of the first component is null", v, nullValue()) - assertThat("finalResult is failure", finalResult, instanceOf(Result.Failure::class.java)) - assertThat("error is a file not found exception", e, instanceOf(FileNotFoundException::class.java)) - } - - @Test(expected = SampleException::class) - fun mapThatThrows() { - val result = Result.of(1) - - var isCalled = false - - val newResult = result.map { throws() } - newResult.handleError { isCalled = true } - - assertThat("newResult is transformed into failure", newResult, instanceOf(Result.Failure::class.java)) - assertThat("isCalled is being set as true", isCalled, equalTo(true)) - } - - @Test(expected = SampleException::class) - fun flatMapThatThrows() { - val result = Result.of("hello") - - var isCalled = false - - val newResult = result.flatMap { throwsForFlatmap() } - newResult.handleError { isCalled = true } - - assertThat("newResult is transformed into failure", newResult, instanceOf(Result.Failure::class.java)) - assertThat("isCalled is being set as true", isCalled, equalTo(true)) - } - - @Test - fun successIsSubtypeOfResult() { - class AlwaysSuccess : GetFoo { - override fun foo(): Result = Result.success(Foo) - } - - val s = AlwaysSuccess() - - assertThat(s.foo(), instanceOf(Result::class.java)) - assertThat(s.foo().getSuccessUnsafe(), equalTo(Foo)) - } - - @Test(expected = NoSuchElementException::class) - fun failureIsSubtypeOfResult() { - class AlwaysFailure : GetFoo { - override fun foo(): Result = Result.error(IllegalAccessException("Can't get foo")) - } - - val e = AlwaysFailure() - - assertThat(e.foo(), instanceOf(Result::class.java)) - - e.foo().getSuccessUnsafe() - } - - @Test - fun liftListToResultOfListSuccess() { - val rs = listOf("bar", "foo").map { "$it.txt" }.map { resultReadFromAssetFileName(it) }.sequence() - - assertThat(rs, instanceOf(Result::class.java)) - assertThat(rs, instanceOf(Result.Success::class.java)) - assertThat(rs.getSuccessUnsafe()[0], equalTo(readFromAssetFileName("bar.txt"))) - } - - @Test - @Ignore - fun liftListToResultOfListFailure() { - val rs = listOf("bar", "not_found").map { "$it.txt" }.map { resultReadFromAssetFileName(it) }.sequence() - - assertThat(rs, instanceOf(Result::class.java)) - assertThat(rs, instanceOf(Result.Failure::class.java)) - val (_, error) = rs - assertThat(error, instanceOf(FileNotFoundException::class.java)) - } - - object Foo - - interface GetFoo { - fun foo(): Result - } - - // helper - private fun readFromAssetFileName(name: String): String { - val dir = System.getProperty("user.dir") - val assetsDir = File(dir, "src/test/assets/") - return File(assetsDir, name).readText() - } - - private fun resultReadFromAssetFileName(name: String): Result { - val operation = { readFromAssetFileName(name) } - return Result.of(operation) - } - - private fun functionThatCanReturnNull(nullEnabled: Boolean): Int? = if (nullEnabled) null else Int.MIN_VALUE - - private fun concat(a: String, b: String): Result = Result.Success(a + b) - - private fun throws() { - throw SampleException("") - } - - private fun throwsForFlatmap(): Result { - throw SampleException("") - } -} diff --git a/result/src/test/kotlin/com/virtuslab/basetypes/result/SampleException.kt b/result/src/test/kotlin/com/virtuslab/basetypes/result/SampleException.kt deleted file mode 100644 index a645292..0000000 --- a/result/src/test/kotlin/com/virtuslab/basetypes/result/SampleException.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.virtuslab.basetypes.result - -import java.lang.RuntimeException - -class SampleException(message: String): RuntimeException(message) \ No newline at end of file diff --git a/result/src/test/kotlin/com/virtuslab/basetypes/result/ValidationTests.kt b/result/src/test/kotlin/com/virtuslab/basetypes/result/ValidationTests.kt deleted file mode 100644 index 09adcfd..0000000 --- a/result/src/test/kotlin/com/virtuslab/basetypes/result/ValidationTests.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.virtuslab.basetypes.result - -import org.junit.Assert.assertThat -import org.junit.Test -import org.hamcrest.CoreMatchers.`is` as isEqualTo - -class ValidationTests { - - @Test - fun testValidation() { - val r1: Result = Result.success(1) - val r2: Result = Result.success(2) - val r3: Result = Result.success(3) - - val validation = Validation(r1, r2, r3) - assertThat("validation.hasFailures", validation.hasFailure, isEqualTo(false)) - assertThat("validation.failures", validation.failures, isEqualTo(listOf())) - } - - @Test(expected = SampleException::class) - fun testValidationWithError() { - - val r1: Result = Result.success(1) - val r2: Result = Result.of { throw SampleException("Not a number") } - val r3: Result = Result.success(3) - val r4: Result = Result.of { throw SampleException("Division by zero") } - - val validation = Validation(r1, r2, r3, r4) - assertThat("validation.hasFailures", validation.hasFailure, isEqualTo(true)) - assertThat("validation.failures", validation.failures.map { it.message }, isEqualTo(listOf("Not a number", "Division by zero"))) - } - -} diff --git a/settings.gradle.kts b/settings.gradle.kts index 8621b7f..f7010ae 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1 +1 @@ -include(":result", ":result-rxjava", ":result-reactor", ":result-arrow", ":refined-types") +include(":result-rxjava", ":result-reactor", ":refined-types")