Skip to content

Commit

Permalink
Add coroutines/flow support (#43)
Browse files Browse the repository at this point in the history
* Add coroutines/flow support

* Add another note

* Remove unneeded casts and suppress the needed ones.

* Fixed nits

* Update based on build changes

* Prepare for release

* Feedback

* Remove from README for now

* Undo changes to rxjava3 dep

* Add initial tests

* Corrections to start and error emissions
  • Loading branch information
Jawnnypoo committed Jun 20, 2024
1 parent 1620773 commit 3419904
Show file tree
Hide file tree
Showing 19 changed files with 629 additions and 6 deletions.
1 change: 1 addition & 0 deletions .buildscript/upload_archives.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
./gradlew clean
./gradlew :lce:build -PreleaseBuild
./gradlew :lce-rxjava3:build -PreleaseBuild
./gradlew :lce-coroutines:build -PreleaseBuild
./gradlew :lce-test:build -PreleaseBuild

# Disabling parallelism and daemon sharing is required by the vanniktech maven publish plugin.
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
- uses: actions/setup-java@v1
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 17
Expand All @@ -25,5 +25,7 @@ jobs:
run: ./gradlew :lce:test
- name: RxJava3 Module Unit Tests
run: ./gradlew :lce-rxjava3:test
- name: Coroutine/Flow Module Unit Tests
run: ./gradlew :lce-coroutines:test
- name: Test Library tests
run: ./gradlew :lce-test:test
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Changelog
## [0.4.0] - June 3, 2024
- Added `lce-coroutines` module for Coroutine/Flow support.

## [0.3.4] - January 10, 2024
- Added `asUCE` map operation for `UCT`.

Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ fun fetchData(): Observable<UCT<MyData>> {
```
You'll need to add the `lce-rxjava3` dependency to access these features.

### Coroutines Support
Converting a `Flow<T>` operation into `Flow<UCT<T>>`
```kotlin
fun fetchData(): Flow<UCT<MyData>> = flow {
emit(MyData())
}.toUCT()
```
You'll need to add the `lce-coroutines` dependency to access these features.

### Testing
Use `lce-test` artifact in your tests to simplify making assertions on the `LCE` types.
```kotlin
Expand All @@ -137,7 +146,9 @@ Add the library to your list of dependencies:
```groovy
dependencies {
implementation("com.laimiux.lce:lce:0.3.4")
// For RxJava 3 support
implementation("com.laimiux.lce:lce-rxjava3:0.3.4")
// Helper functions for assertions in tests
implementation("com.laimiux.lce:lce-test:0.3.4")
}
```
Expand All @@ -147,6 +158,7 @@ To run tests
```sh
./gradlew :lce:test
./gradlew :lce-rxjava3:test
./gradlew :lce-coroutines:test
```

# License
Expand Down
9 changes: 5 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
[versions]
kotlin = "1.9.10"
dokka-gradle = "1.9.10"
junit = "4.12"
truth = "1.0.1"
kotlin = "1.9.20"
dokka-gradle = "1.9.20"
coroutines = "1.8.1"

[libraries]
kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
Expand All @@ -11,3 +10,5 @@ maven-publish-gradle = { module = "com.vanniktech:gradle-maven-publish-plugin",
junit = { module = "junit:junit", version = "4.12" }
truth = { module = "com.google.truth:truth", version = "1.0.1" }
rxjava = { module = "io.reactivex.rxjava3:rxjava", version = "3.0.4" }
coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
1 change: 1 addition & 0 deletions lce-coroutines/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
7 changes: 7 additions & 0 deletions lce-coroutines/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# lce-coroutines
Coroutines/Flow support for LCE.

If coming over from RxJava, here is the typical mapping

- `switchMapContent` -> `flatMapLatestContent`
- `onlyContentEvents` -> `onlyContentEvents` (same name, with `flatMapMerge` used under the hood)
26 changes: 26 additions & 0 deletions lce-coroutines/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
plugins {
id("kotlin")
id("org.jetbrains.dokka")
}

apply {
from("$rootDir/.buildscript/configure-signing.gradle")
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
kotlin {
jvmToolchain(17)
}

dependencies {
api(project(":lce"))
api(libs.coroutines)

testImplementation(libs.truth)
testImplementation(libs.junit)
testImplementation(libs.coroutines.test)
}
3 changes: 3 additions & 0 deletions lce-coroutines/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_ARTIFACT_ID=lce-coroutines
POM_NAME=LCE type Coroutines Support
POM_PACKAGING=jar
145 changes: 145 additions & 0 deletions lce-coroutines/src/main/java/com/laimiux/lce/flow/ContentEvents.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
@file:OptIn(ExperimentalCoroutinesApi::class)

package com.laimiux.lce.flow

import com.laimiux.lce.CT
import com.laimiux.lce.LC
import com.laimiux.lce.LCE
import com.laimiux.lce.UC
import com.laimiux.lce.UCE
import com.laimiux.lce.UCT
import com.laimiux.lce.fold
import com.laimiux.lce.foldTypes
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flowOf

@JvmName("onlyContentEventsUCE")
fun <C, E> Flow<UCE<C, E>>.onlyContentEvents(): Flow<C> {
return flatMapMerge {
it.fold(
onLoading = { emptyFlow() },
onError = { emptyFlow() },
onContent = { flowOf(it) }
)
}
}

@JvmName("onlyContentEventsUCT")
fun <C> Flow<UCT<C>>.onlyContentEvents(): Flow<C> {
return flatMapMerge {
it.fold(
onLoading = { emptyFlow() },
onError = { emptyFlow() },
onContent = { flowOf(it) }
)
}
}

@JvmName("onlyContentEventsCT")
fun <C> Flow<CT<C>>.onlyContentEvents(): Flow<C> {
return flatMapMerge {
it.fold(
onError = { emptyFlow() },
onContent = { flowOf(it) }
)
}
}

@JvmName("onlyContentEventsLC")
fun <L, C> Flow<LC<L, C>>.onlyContentEvents(): Flow<C> {
return flatMapMerge {
it.fold(
onLoading = { emptyFlow() },
onContent = { flowOf(it) }
)
}
}

@JvmName("onlyContentEventsUC")
fun <C> Flow<UC<C>>.onlyContentEvents(): Flow<C> {
return flatMapMerge {
it.fold(
onLoading = { emptyFlow() },
onContent = { flowOf(it) }
)
}
}

@JvmName("flatMapLatestContentLCE")
inline fun <L, C, E, NewC> Flow<LCE<L, C, E>>.flatMapLatestContent(
crossinline transform: (C) -> Flow<LCE<L, NewC, E>>
): Flow<LCE<L, NewC, E>> {
return flatMapLatest {
it.foldTypes(
onLoading = { flowOf(it) },
onContent = { transform(it.value) },
onError = { flowOf(it) }
)
}
}

@JvmName("flatMapLatestContentContentUCE")
inline fun <C, E, NewC> Flow<UCE<C, E>>.flatMapLatestContent(
crossinline transform: (C) -> Flow<UCE<NewC, E>>
): Flow<UCE<NewC, E>> {
return flatMapLatest {
it.foldTypes(
onLoading = { flowOf(it) },
onContent = { transform(it.value) },
onError = { flowOf(it) }
)
}
}

@JvmName("flatMapLatestContentContentUCT")
inline fun <C, NewC> Flow<UCT<C>>.flatMapLatestContent(
crossinline transform: (C) -> Flow<UCT<NewC>>
): Flow<UCT<NewC>> {
return flatMapLatest {
it.foldTypes(
onLoading = { flowOf(it) },
onContent = { transform(it.value) },
onError = { flowOf(it) }
)
}
}

@JvmName("flatMapLatestContentContentCT")
inline fun <C, NewC> Flow<CT<C>>.flatMapLatestContent(
crossinline transform: (C) -> Flow<CT<NewC>>
): Flow<CT<NewC>> {
return flatMapLatest {
it.foldTypes(
onContent = { transform(it.value) },
onError = { flowOf(it) }
)
}
}

@JvmName("flatMapLatestContentContentLC")
inline fun <L, C, NewC> Flow<LC<L, C>>.flatMapLatestContent(
crossinline transform: (C) -> Flow<LC<L, NewC>>
): Flow<LC<L, NewC>> {
return flatMapLatest {
it.foldTypes(
onLoading = { flowOf(it) },
onContent = { transform(it.value) }
)
}
}

@JvmName("flatMapLatestContentContentUC")
inline fun <C, NewC> Flow<UC<C>>.flatMapLatestContent(
crossinline transform: (C) -> Flow<UC<NewC>>
): Flow<UC<NewC>> {
return flatMapLatest {
it.foldTypes(
onLoading = { flowOf(it) },
onContent = { transform(it.value) }
)
}
}
26 changes: 26 additions & 0 deletions lce-coroutines/src/main/java/com/laimiux/lce/flow/Convert.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@file:OptIn(ExperimentalCoroutinesApi::class)

package com.laimiux.lce.flow

import com.laimiux.lce.CT
import com.laimiux.lce.UCT
import com.laimiux.lce.asCT
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf

/**
* Converts [UCT] type to [CT]. Essentially it ignores loading events.
*/
fun <C> Flow<UCT<C>>.toCT(): Flow<CT<C>> {
return flatMapLatest {
val ct = it.asCT()
if (ct != null) {
flowOf(ct)
} else {
emptyFlow()
}
}
}
73 changes: 73 additions & 0 deletions lce-coroutines/src/main/java/com/laimiux/lce/flow/DoOnContent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.laimiux.lce.flow

import com.laimiux.lce.CE
import com.laimiux.lce.CT
import com.laimiux.lce.LC
import com.laimiux.lce.LCE
import com.laimiux.lce.UC
import com.laimiux.lce.UCE
import com.laimiux.lce.UCT
import kotlinx.coroutines.flow.Flow

@JvmName("doOnContentLCE")
inline fun <L, C, E> Flow<LCE<L, C, E>>.doOnContent(
crossinline action: (C) -> Unit
): Flow<LCE<L, C, E>> {
return doOnEvent(
onContent = action
)
}

@JvmName("doOnContentUCE")
inline fun <C, E> Flow<UCE<C, E>>.doOnContent(
crossinline action: (C) -> Unit
): Flow<UCE<C, E>> {
return doOnEvent(
onContent = action
)
}

@JvmName("doOnContentUCT")
inline fun <C> Flow<UCT<C>>.doOnContent(
crossinline action: (C) -> Unit
): Flow<UCT<C>> {
return doOnEvent(
onContent = action
)
}

@JvmName("doOnContentCE")
inline fun <C, E> Flow<CE<C, E>>.doOnContent(
crossinline action: (C) -> Unit
): Flow<CE<C, E>> {
return doOnEvent(
onContent = action
)
}

@JvmName("doOnContentCT")
inline fun <C> Flow<CT<C>>.doOnContent(
crossinline action: (C) -> Unit
): Flow<CT<C>> {
return doOnEvent(
onContent = action
)
}

@JvmName("doOnContentLC")
inline fun <L, C> Flow<LC<L, C>>.doOnContent(
crossinline action: (C) -> Unit
): Flow<LC<L, C>> {
return doOnEvent(
onContent = action
)
}

@JvmName("doOnContentUC")
inline fun <C> Flow<UC<C>>.doOnContent(
crossinline action: (C) -> Unit
): Flow<UC<C>> {
return doOnEvent(
onContent = action
)
}
Loading

0 comments on commit 3419904

Please sign in to comment.