diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57a68d1..58acc78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - name: Build testapp and test request (iOS) run: | cd iostests - xcodebuild clean test -project TestApp.xcodeproj -scheme TestApp -destination "platform=iOS Simulator,OS=15.0,name=iPhone 12" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO region=${{ secrets.region }} clientId=${{ secrets.clientid }} + xcodebuild clean test -project TestApp.xcodeproj -scheme TestApp -destination "platform=iOS Simulator,OS=15.2,name=iPhone 12" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO region=${{ secrets.region }} clientId=${{ secrets.clientid }} - name: Upload test result if: ${{ always() }} uses: actions/upload-artifact@v2 diff --git a/src/commonMain/kotlin/com/liftric/cognito/idp/core/Result.kt b/src/commonMain/kotlin/com/liftric/cognito/idp/core/Result.kt index d280fe5..622ddd9 100644 --- a/src/commonMain/kotlin/com/liftric/cognito/idp/core/Result.kt +++ b/src/commonMain/kotlin/com/liftric/cognito/idp/core/Result.kt @@ -13,6 +13,16 @@ class Result constructor(val value: Any?) { companion object { fun success(value: T): Result = Result(value) fun failure(exception: Throwable): Result = Result(Failure(exception)) + + /** + * [Result] builder function. Returns Success if [mightFail] doesn't, otherwise returns + * [mightFail] exception as [Failure] + */ + fun doTry(mightFail: () -> T) : Result = try { + success(mightFail()) + } catch (e: Throwable){ + failure(e) + } } class Failure(val exception: Throwable) { @@ -54,6 +64,19 @@ class Result constructor(val value: Any?) { } } + /** + * Executes the [onSuccess] mapping if Success, or re-wraps the Failure doing nothing + * + * This can be used to pipe transform the result only if it is successful. + * A happy path of results can be modelled with this. + * + * Would be called foldRight if [Result] would be called Either ;) + */ + fun andThen(onSuccess: (value: T) -> R): Result = when(value) { + is Failure -> failure(value.exception) + else -> doTry { onSuccess(value as T) } + } + override fun toString(): String = when (value) { is Failure -> value.toString() @@ -92,4 +115,4 @@ inline fun T.runCatching(block: T.() -> R): Result { } catch (e: Throwable) { Result.failure(e) } -} \ No newline at end of file +} diff --git a/src/commonTest/kotlin/com/liftric/cognito/idp/ResultTests.kt b/src/commonTest/kotlin/com/liftric/cognito/idp/ResultTests.kt index f8d3e76..045824a 100644 --- a/src/commonTest/kotlin/com/liftric/cognito/idp/ResultTests.kt +++ b/src/commonTest/kotlin/com/liftric/cognito/idp/ResultTests.kt @@ -65,6 +65,52 @@ class ResultTests { assertEquals(expected, result) } + @Test + fun testDoTryAndFold() { + Result.doTry { "1" } + .andThen { "${it}2" } + .andThen { it.toInt() } + .andThen { it * 2 } + .fold(onFailure = {fail("shouldn't fail!")}, onSuccess = { assertEquals(24, it) }) + } + + @Test + fun testAndThenOnSuccess() { + var result: String? = null + Result.success("Hans") + .andThen { "$it Wurst" } + .fold( + onSuccess = { + result = it + }, + onFailure = { + fail("onFailure() should not get called") + } + ) + assertEquals("Hans Wurst", result) + } + + @Test + fun testAndThenOnFailure() { + val exception = Exception("firstException") + val failedAndThen = Result.failure(exception) + .andThen { + Result.success("won't happen") + } + + assertEquals(true, failedAndThen.isFailure) + assertEquals(exception, failedAndThen.exceptionOrNull()) + + Result.success("Test") + .andThen { + error("sad") + }.fold(onSuccess = { + fail("onSuccess() should not get called") + }, onFailure = { + assertEquals("sad" ,it.message) + }) + } + @Test fun testFoldOnFailure() { val expected = IOException("No connectivity") @@ -115,4 +161,4 @@ class ResultTests { assertEquals(expected, result.exceptionOrNull()) assertNull(result.getOrNull()) } -} \ No newline at end of file +}