From 0195bd8d2b261bcf084bc7bd31479ee218d90b45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Sep 2025 08:49:21 +0000 Subject: [PATCH 1/3] Initial plan From 34a24c475c0435d13dfe8bcc5d27745e97940c48 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Sep 2025 09:05:04 +0000 Subject: [PATCH 2/3] Add Flow operators: not(), inverted(), and toggle() Co-authored-by: hoc081098 <36917223+hoc081098@users.noreply.github.com> --- CHANGELOG.md | 6 + README.md | 50 ++++++ .../com/hoc081098/flowext/booleanOperators.kt | 54 +++++++ .../hoc081098/flowext/BooleanOperatorsTest.kt | 146 ++++++++++++++++++ 4 files changed, 256 insertions(+) create mode 100644 src/commonMain/kotlin/com/hoc081098/flowext/booleanOperators.kt create mode 100644 src/commonTest/kotlin/com/hoc081098/flowext/BooleanOperatorsTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 32ba743e..ff15e5ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## [Unreleased] +### Added + +- Add `Flow.not()` operator - Returns a Flow that emits the boolean negation of each value emitted by the source Flow. +- Add `Flow.inverted()` operator - Alias for `not()`. +- Add `Flow.toggle()` operator - Alias for `not()`. + ## [1.0.0] - Sep 22, 2024 This is our first stable release! Thanks everyone for using FlowExt, reporting bugs, providing feedback and sending PRs. diff --git a/README.md b/README.md index 1c7e5bf9..da360e70 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,9 @@ dependencies { - Intermediate operators - [`bufferCount`](#buffercount--chunked) + - [`not`](#not--inverted--toggle) (`Flow`) + - [`inverted`](#not--inverted--toggle) (`Flow`) + - [`toggle`](#not--inverted--toggle) (`Flow`) - [`combine`](#combine) - [`cast`](#cast--castnotnull--castnullable--safeCast) - [`castNotNull`](#cast--castnotnull--castnullable--safeCast) @@ -228,6 +231,53 @@ bufferCount: [8, 9] ---- +#### not / inverted / toggle + +Returns a `Flow` that emits the boolean negation (opposite) of each value emitted by the source `Flow`. + +- `not()` - Returns the negated boolean values +- `inverted()` - Alias for `not()` +- `toggle()` - Alias for `not()` + +```kotlin +flowOf(true, false, true, false) + .not() + .collect { println("not: $it") } + +println("---") + +flowOf(true, false, true, false) + .inverted() + .collect { println("inverted: $it") } + +println("---") + +flowOf(true, false, true, false) + .toggle() + .collect { println("toggle: $it") } +``` + +Output: + +```none +not: false +not: true +not: false +not: true +--- +inverted: false +inverted: true +inverted: false +inverted: true +--- +toggle: false +toggle: true +toggle: false +toggle: true +``` + +---- + #### concat - Similar to [RxJS concat](https://rxjs.dev/api/index/function/concat) diff --git a/src/commonMain/kotlin/com/hoc081098/flowext/booleanOperators.kt b/src/commonMain/kotlin/com/hoc081098/flowext/booleanOperators.kt new file mode 100644 index 00000000..56315917 --- /dev/null +++ b/src/commonMain/kotlin/com/hoc081098/flowext/booleanOperators.kt @@ -0,0 +1,54 @@ +/* + * MIT License + * + * Copyright (c) 2021-2024 Petrus Nguyễn Thái Học + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.hoc081098.flowext + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** + * Returns a [Flow] that emits the boolean negation (opposite) of each value emitted by the source [Flow]. + * + * @return A [Flow] that emits `false` when the source emits `true` and `true` when the source emits `false`. + */ +@Suppress("NOTHING_TO_INLINE") +public inline fun Flow.not(): Flow = map { !it } + +/** + * Returns a [Flow] that emits the boolean negation (opposite) of each value emitted by the source [Flow]. + * This is an alias for [not]. + * + * @return A [Flow] that emits `false` when the source emits `true` and `true` when the source emits `false`. + */ +@Suppress("NOTHING_TO_INLINE") +public inline fun Flow.inverted(): Flow = not() + +/** + * Returns a [Flow] that emits the boolean negation (opposite) of each value emitted by the source [Flow]. + * This is an alias for [not]. + * + * @return A [Flow] that emits `false` when the source emits `true` and `true` when the source emits `false`. + */ +@Suppress("NOTHING_TO_INLINE") +public inline fun Flow.toggle(): Flow = not() diff --git a/src/commonTest/kotlin/com/hoc081098/flowext/BooleanOperatorsTest.kt b/src/commonTest/kotlin/com/hoc081098/flowext/BooleanOperatorsTest.kt new file mode 100644 index 00000000..7dc59e54 --- /dev/null +++ b/src/commonTest/kotlin/com/hoc081098/flowext/BooleanOperatorsTest.kt @@ -0,0 +1,146 @@ +/* + * MIT License + * + * Copyright (c) 2021-2024 Petrus Nguyễn Thái Học + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.hoc081098.flowext + +import com.hoc081098.flowext.utils.BaseTest +import com.hoc081098.flowext.utils.TestException +import com.hoc081098.flowext.utils.test +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.toList + +@ExperimentalCoroutinesApi +@InternalCoroutinesApi +class BooleanOperatorsTest : BaseTest() { + @Test + fun notBasic() = runTest { + val values = flowOf(true, false, true, false).not().toList() + assertEquals(listOf(false, true, false, true), values) + } + + @Test + fun notEmpty() = runTest { + val values = flowOf().not().toList() + assertEquals(emptyList(), values) + } + + @Test + fun notSingleTrue() = runTest { + val values = flowOf(true).not().toList() + assertEquals(listOf(false), values) + } + + @Test + fun notSingleFalse() = runTest { + val values = flowOf(false).not().toList() + assertEquals(listOf(true), values) + } + + @Test + fun notUpstreamError() = runTest { + val throwable = TestException() + + flow { throw throwable } + .not() + .test(listOf(Event.Error(throwable))) + } + + @Test + fun notSequence() = runTest { + listOf(true, false, true, false, true) + .asFlow() + .not() + .test( + listOf( + Event.Value(false), + Event.Value(true), + Event.Value(false), + Event.Value(true), + Event.Value(false), + Event.Complete, + ), + ) + } + + @Test + fun invertedBasic() = runTest { + val values = flowOf(true, false, true, false).inverted().toList() + assertEquals(listOf(false, true, false, true), values) + } + + @Test + fun invertedEmpty() = runTest { + val values = flowOf().inverted().toList() + assertEquals(emptyList(), values) + } + + @Test + fun invertedUpstreamError() = runTest { + val throwable = TestException() + + flow { throw throwable } + .inverted() + .test(listOf(Event.Error(throwable))) + } + + @Test + fun toggleBasic() = runTest { + val values = flowOf(true, false, true, false).toggle().toList() + assertEquals(listOf(false, true, false, true), values) + } + + @Test + fun toggleEmpty() = runTest { + val values = flowOf().toggle().toList() + assertEquals(emptyList(), values) + } + + @Test + fun toggleUpstreamError() = runTest { + val throwable = TestException() + + flow { throw throwable } + .toggle() + .test(listOf(Event.Error(throwable))) + } + + @Test + fun allVariantsProduceSameResult() = runTest { + val input = flowOf(true, false, true, false) + + val notResult = input.not().toList() + val invertedResult = input.inverted().toList() + val toggleResult = input.toggle().toList() + + assertEquals(notResult, invertedResult) + assertEquals(notResult, toggleResult) + assertEquals(listOf(false, true, false, true), notResult) + } +} From 6ee7d551611236f1bb0a1ed9e021fc7902b58814 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Sep 2025 10:39:31 +0000 Subject: [PATCH 3/3] Update API files with new Boolean operators Co-authored-by: hoc081098 <36917223+hoc081098@users.noreply.github.com> --- api/FlowExt.api | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/FlowExt.api b/api/FlowExt.api index 20e0ec5e..22a173e0 100644 --- a/api/FlowExt.api +++ b/api/FlowExt.api @@ -1,3 +1,9 @@ +public final class com/hoc081098/flowext/BooleanOperatorsKt { + public static final fun inverted (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow; + public static final fun not (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow; + public static final fun toggle (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow; +} + public final class com/hoc081098/flowext/BufferCountKt { public static final fun bufferCount (Lkotlinx/coroutines/flow/Flow;I)Lkotlinx/coroutines/flow/Flow; public static final fun bufferCount (Lkotlinx/coroutines/flow/Flow;II)Lkotlinx/coroutines/flow/Flow;