diff --git a/result-flow/CHANGELOG.md b/result-flow/CHANGELOG.md new file mode 100644 index 0000000..81199b7 --- /dev/null +++ b/result-flow/CHANGELOG.md @@ -0,0 +1,3 @@ +## Unreleased + +Initial release diff --git a/result-flow/README.md b/result-flow/README.md new file mode 100644 index 0000000..b1096a8 --- /dev/null +++ b/result-flow/README.md @@ -0,0 +1,56 @@ +# result-flow +[![Version](https://img.shields.io/maven-central/v/com.redmadrobot.gears/result-flow?style=flat-square)][mavenCentral] +[![License](https://img.shields.io/github/license/RedMadRobot/gears-android?style=flat-square)][license] + +--- + + + +- [Installation](#installation) +- [Usage](#usage) +- [Contributing](#contributing) + + + +Utilities to deal with `Flow>`. + +## Installation + +Add the dependency: + +```kotlin +repositories { + mavenCentral() +} + +dependencies { + implementation("com.redmadrobot.gears:result-flow:") +} +``` + +## Usage + +> [!WARNING] +> The documentation is under construction + +Tracking request status and handling error: + +```kotlin +resultFlow { repository.fetchData() } + .onEachState { resultState -> + state = state.copy(loading = resultState.isLoading) + } + .foldEach( + onSuccess = { handleContent(it) }, + onFailure = { showError(it) }, + ) +``` + +## Contributing + +Merge requests are welcome. +For major changes, open an issue first to discuss what you would like to change. + + +[mavenCentral]: https://search.maven.org/artifact/com.redmadrobot.gears/gears-kotlin +[license]: ../LICENSE diff --git a/result-flow/build.gradle.kts b/result-flow/build.gradle.kts new file mode 100644 index 0000000..c1b0ccd --- /dev/null +++ b/result-flow/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + convention.library.kotlin +} + +version = "0.1.0" +description = "Utilities to deal with Flow>" + +dependencies { + api(kotlin("stdlib")) + api(stack.kotlinx.coroutines.core) +} diff --git a/result-flow/src/main/kotlin/ResultFlow.kt b/result-flow/src/main/kotlin/ResultFlow.kt new file mode 100644 index 0000000..a1fea58 --- /dev/null +++ b/result-flow/src/main/kotlin/ResultFlow.kt @@ -0,0 +1,31 @@ +package com.redmadrobot.gears.resultflow + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map + +public fun resultFlow(block: suspend () -> T): Flow> { + return flow { emit(block()) } + .toResultFlow() +} + +public fun Flow.toResultFlow(): Flow> { + return map { Result.success(it) } + .catch { emit(Result.failure(it)) } +} + +@Deprecated( + "Call toResultFlow() on Flow> is redundant and can be removed.", + ReplaceWith("this"), + level = DeprecationLevel.ERROR, +) +@JvmName("-redundant_toResultFlow") +public fun Flow>.toResultFlow(): Flow> = this + +public inline fun Flow>.foldEach( + crossinline onSuccess: (T) -> R, + crossinline onFailure: (Throwable) -> R, +): Flow { + return map { it.fold(onSuccess, onFailure) } +} diff --git a/result-flow/src/main/kotlin/ResultState.kt b/result-flow/src/main/kotlin/ResultState.kt new file mode 100644 index 0000000..77c53de --- /dev/null +++ b/result-flow/src/main/kotlin/ResultState.kt @@ -0,0 +1,48 @@ +package com.redmadrobot.gears.resultflow + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart + +/** + * Represents three possible states during a result pending. + * + * - [Pending] – A result that is still pending and has not yet completed. + * - [Success] – A successful result. + * - [Failure] – A failed result, contains the exception that caused the failure. + * + * @see onEachState + */ +public sealed interface ResultState { + + /** Shorthand for `status is ResultState.Pending`. */ + public val isPending: Boolean + get() = this is Pending + + /** Shorthand for `status is ResultState.Success`. */ + public val isSuccess: Boolean + get() = this is Success + + /** Shorthand for `status is ResultState.Failure`. */ + public val isFailure: Boolean + get() = this is Failure + + public fun exceptionOrNull(): Throwable? = if (this is Failure) exception else null + + public data object Pending : ResultState + public data object Success : ResultState + public data class Failure(val exception: Throwable) : ResultState + + public companion object +} + +public fun Flow>.onEachState(action: suspend (ResultState) -> Unit): Flow> { + return onStart { action(ResultState.Pending) } + .onEach { result -> + val state = result.fold( + onSuccess = { ResultState.Success }, + onFailure = { ResultState.Failure(it) }, + ) + action(state) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index d66aa98..2e63a03 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -36,4 +36,5 @@ include( ":ktx:viewbinding-ktx", ":gears:gears-compose", ":gears:gears-kotlin", + ":result-flow", )