Skip to content

Commit

Permalink
feat: Add result-state module with initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
osipxd committed Jul 18, 2024
1 parent f696bba commit 2d68279
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 0 deletions.
3 changes: 3 additions & 0 deletions result-flow/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Unreleased

Initial release
56 changes: 56 additions & 0 deletions result-flow/README.md
Original file line number Diff line number Diff line change
@@ -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]

---
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Installation](#installation)
- [Usage](#usage)
- [Contributing](#contributing)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

Utilities to deal with `Flow<Result<T>>`.

## Installation

Add the dependency:

```kotlin
repositories {
mavenCentral()
}

dependencies {
implementation("com.redmadrobot.gears:result-flow:<version>")
}
```

## 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
11 changes: 11 additions & 0 deletions result-flow/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
plugins {
convention.library.kotlin
}

version = "0.1.0"
description = "Utilities to deal with Flow<Result<T>>"

dependencies {
api(kotlin("stdlib"))
api(stack.kotlinx.coroutines.core)
}
31 changes: 31 additions & 0 deletions result-flow/src/main/kotlin/ResultFlow.kt
Original file line number Diff line number Diff line change
@@ -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 <T> resultFlow(block: suspend () -> T): Flow<Result<T>> {
return flow { emit(block()) }
.toResultFlow()
}

public fun <T> Flow<T>.toResultFlow(): Flow<Result<T>> {
return map { Result.success(it) }
.catch { emit(Result.failure(it)) }
}

@Deprecated(
"Call toResultFlow() on Flow<Result<T>> is redundant and can be removed.",
ReplaceWith("this"),
level = DeprecationLevel.ERROR,
)
@JvmName("-redundant_toResultFlow")
public fun <T> Flow<Result<T>>.toResultFlow(): Flow<Result<T>> = this

public inline fun <T, R : Any> Flow<Result<T>>.foldEach(
crossinline onSuccess: (T) -> R,
crossinline onFailure: (Throwable) -> R,
): Flow<R> {
return map { it.fold(onSuccess, onFailure) }
}
48 changes: 48 additions & 0 deletions result-flow/src/main/kotlin/ResultState.kt
Original file line number Diff line number Diff line change
@@ -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 <T> Flow<Result<T>>.onEachState(action: suspend (ResultState) -> Unit): Flow<Result<T>> {
return onStart { action(ResultState.Pending) }
.onEach { result ->
val state = result.fold(
onSuccess = { ResultState.Success },
onFailure = { ResultState.Failure(it) },
)
action(state)
}
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ include(
":ktx:viewbinding-ktx",
":gears:gears-compose",
":gears:gears-kotlin",
":result-flow",
)

0 comments on commit 2d68279

Please sign in to comment.