diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e35c84e..7b88df3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ - Add `Flow.ignoreElements` operator. - Add `Flow.scanWith` operator. - +- Add `Flow.safeCast` operator. ## [0.7.0] ~ [0.7.1] - Jul 31, 2023 ### Changed diff --git a/README.md b/README.md index 1390197b..3f731132 100644 --- a/README.md +++ b/README.md @@ -122,9 +122,10 @@ dependencies { - Intermediate operators - [`bufferCount`](#buffercount) - [`combine`](#combine) - - [`cast`](#cast--castnotnull--castnullable) - - [`castNotNull`](#cast--castnotnull--castnullable) - - [`castNullable`](#cast--castnotnull--castnullable) + - [`cast`](#cast--castnotnull--castnullable--safeCast) + - [`castNotNull`](#cast--castnotnull--castnullable--safeCast) + - [`castNullable`](#cast--castnotnull--castnullable--safeCast) + - [`safeCast`](#cast--castnotnull--castnullable--safeCast) - [`concatWith`](#concatwith) - [`startWith`](#startwith) - [`flatMapFirst`](#flatmapfirst--exhaustmap) @@ -432,7 +433,7 @@ timer: kotlin.Unit ---- -#### cast / castNotNull / castNullable +#### cast / castNotNull / castNullable / safeCast - Similar to [RxJava cast](http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Flowable.html#cast-java.lang.Class-) @@ -487,6 +488,31 @@ castNotNull: 3 ---- + +##### safeCast + +Adapt this `Flow<*>` to be a `Flow`. + +At the collection time, if this `Flow` has any value that is not an instance of R, null will be emitted. + +```kotlin +flowOf(1, 2, 3, "Kotlin", null) + .safeCast() + .collect { v: Int? -> println("safeCast: $v") } +``` + +Output: + +```none +safeCast: 1 +safeCast: 2 +safeCast: 3 +safeCast: null +safeCast: null +``` + +---- + #### concatWith - Similar to [RxJS concatWith](https://rxjs.dev/api/operators/concatWith) diff --git a/src/commonMain/kotlin/com/hoc081098/flowext/cast.kt b/src/commonMain/kotlin/com/hoc081098/flowext/cast.kt index d3da3f96..806313cf 100644 --- a/src/commonMain/kotlin/com/hoc081098/flowext/cast.kt +++ b/src/commonMain/kotlin/com/hoc081098/flowext/cast.kt @@ -53,3 +53,11 @@ public inline fun Flow.castNotNull(): Flow = map { it!! */ @Suppress("NOTHING_TO_INLINE") public inline fun Flow.castNullable(): Flow = this + +/** + * Adapt this `Flow<*>` to be a `Flow`. + * At the collection time, if this [Flow] has any value that is not an instance of R, + * null will be emitted + */ +@Suppress("NOTHING_TO_INLINE") +public inline fun Flow<*>.safeCast(): Flow = map { it as? R } diff --git a/src/commonTest/kotlin/com/hoc081098/flowext/CastTest.kt b/src/commonTest/kotlin/com/hoc081098/flowext/CastTest.kt index f663e32c..0b9e5132 100644 --- a/src/commonTest/kotlin/com/hoc081098/flowext/CastTest.kt +++ b/src/commonTest/kotlin/com/hoc081098/flowext/CastTest.kt @@ -28,11 +28,13 @@ import com.hoc081098.flowext.utils.BaseTest import com.hoc081098.flowext.utils.assertFailsWith import com.hoc081098.flowext.utils.test import kotlin.test.Test +import kotlin.test.assertContentEquals import kotlin.test.assertIs import kotlin.test.assertSame import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.toList @ExperimentalCoroutinesApi class CastTest : BaseTest() { @@ -96,4 +98,45 @@ class CastTest : BaseTest() { val flow = flowOf(1, 2, 3) assertSame(flow.castNullable(), flow) } + + @Test + fun testSafeCastSuccess() = runTest { + assertIs>( + flowOf(1, 2, 3, "hello").safeCast(), + ) + + assertIs>( + flowOf(1, 2, 3, "kotlin", null) + .safeCast(), + ) + .test( + listOf( + Event.Value(1), + Event.Value(2), + Event.Value(3), + Event.Value(null), + Event.Value(null), + Event.Complete, + ), + ) + } + + @Test + fun testSafeCast() = runTest { + val stringFlow: Flow = flowOf("Hello", 42, "World", 123, "Kotlin").safeCast() + assertIs>(stringFlow) + + assertContentEquals( + listOf("Hello", null, "World", null, "Kotlin"), + stringFlow.toList(), + ) + + val intFlow: Flow = flowOf(1, 2, 3, null).safeCast() + assertIs>(intFlow) + + assertContentEquals( + listOf(1, 2, 3, null), + intFlow.toList(), + ) + } }