|
7 | 7 |
|
8 | 8 | package kotlinx.coroutines.flow
|
9 | 9 |
|
| 10 | +import kotlinx.coroutines.* |
10 | 11 | import kotlinx.coroutines.flow.internal.*
|
11 | 12 | import kotlin.jvm.*
|
| 13 | +import kotlinx.coroutines.flow.flow as safeFlow |
12 | 14 | import kotlinx.coroutines.flow.internal.unsafeFlow as flow
|
13 | 15 |
|
14 | 16 | /**
|
@@ -49,35 +51,70 @@ public fun <T> Flow<T>.take(count: Int): Flow<T> {
|
49 | 51 | require(count > 0) { "Requested element count $count should be positive" }
|
50 | 52 | return flow {
|
51 | 53 | var consumed = 0
|
52 |
| - try { |
53 |
| - collect { value -> |
54 |
| - if (++consumed < count) { |
55 |
| - return@collect emit(value) |
56 |
| - } else { |
57 |
| - return@collect emitAbort(value) |
58 |
| - } |
59 |
| - } |
60 |
| - } catch (e: AbortFlowException) { |
61 |
| - e.checkOwnership(owner = this) |
| 54 | + collectWhile { value -> |
| 55 | + emit(value) |
| 56 | + ++consumed < count |
62 | 57 | }
|
63 | 58 | }
|
64 | 59 | }
|
65 | 60 |
|
66 |
| -private suspend fun <T> FlowCollector<T>.emitAbort(value: T) { |
67 |
| - emit(value) |
68 |
| - throw AbortFlowException(this) |
69 |
| -} |
70 |
| - |
71 | 61 | /**
|
72 | 62 | * Returns a flow that contains first elements satisfying the given [predicate].
|
| 63 | + * |
| 64 | + * Note, that the resulting flow does not contain the element on which the [predicate] returned `true`. |
| 65 | + * See [transformWhile] for a more flexible operator. |
73 | 66 | */
|
74 | 67 | public fun <T> Flow<T>.takeWhile(predicate: suspend (T) -> Boolean): Flow<T> = flow {
|
75 |
| - try { |
76 |
| - collect { value -> |
77 |
| - if (predicate(value)) emit(value) |
78 |
| - else throw AbortFlowException(this) |
| 68 | + collectWhile { value -> |
| 69 | + if (predicate(value)) { |
| 70 | + emit(value) |
| 71 | + true |
| 72 | + } else { |
| 73 | + false |
79 | 74 | }
|
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +/** |
| 79 | + * Applies [transform] function to each value of the given flow while this |
| 80 | + * function returns `true`. |
| 81 | + * |
| 82 | + * The receiver of the `transformWhile` is [FlowCollector] and thus `transformWhile` is a |
| 83 | + * flexible function that may transform emitted element, skip it or emit it multiple times. |
| 84 | + * |
| 85 | + * This operator generalizes [takeWhile] and can be used as a building block for other operators. |
| 86 | + * For example, a flow of download progress messages can be completed when the |
| 87 | + * download is done but emit this last message (unlike `takeWhile`): |
| 88 | + * |
| 89 | + * ``` |
| 90 | + * fun Flow<DownloadProgress>.completeWhenDone(): Flow<DownloadProgress> = |
| 91 | + * transformWhile { progress -> |
| 92 | + * emit(progress) // always emit progress |
| 93 | + * !progress.isDone() // continue while download is not done |
| 94 | + * } |
| 95 | + * } |
| 96 | + * ``` |
| 97 | + */ |
| 98 | +@ExperimentalCoroutinesApi |
| 99 | +public fun <T, R> Flow<T>.transformWhile( |
| 100 | + @BuilderInference transform: suspend FlowCollector<R>.(value: T) -> Boolean |
| 101 | +): Flow<R> = |
| 102 | + safeFlow { // Note: safe flow is used here, because collector is exposed to transform on each operation |
| 103 | + collectWhile { value -> |
| 104 | + transform(value) |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | +// Internal building block for all flow-truncating operators |
| 109 | +internal suspend inline fun <T> Flow<T>.collectWhile(crossinline predicate: suspend (value: T) -> Boolean) { |
| 110 | + val collector = object : FlowCollector<T> { |
| 111 | + override suspend fun emit(value: T) { |
| 112 | + if (!predicate(value)) throw AbortFlowException(this) |
| 113 | + } |
| 114 | + } |
| 115 | + try { |
| 116 | + collect(collector) |
80 | 117 | } catch (e: AbortFlowException) {
|
81 |
| - e.checkOwnership(owner = this) |
| 118 | + e.checkOwnership(collector) |
82 | 119 | }
|
83 | 120 | }
|
0 commit comments