<lambda>null1 @file:JvmMultifileClass
2 @file:JvmName("FlowKt")
3 @file:Suppress("UNCHECKED_CAST")
4
5 package kotlinx.coroutines.flow
6
7 import kotlinx.coroutines.flow.internal.*
8 import kotlinx.coroutines.internal.Symbol
9 import kotlin.jvm.*
10
11 /**
12 * Accumulates value starting with the first element and applying [operation] to current accumulator value and each element.
13 * Throws [NoSuchElementException] if flow was empty.
14 */
15 public suspend fun <S, T : S> Flow<T>.reduce(operation: suspend (accumulator: S, value: T) -> S): S {
16 var accumulator: Any? = NULL
17
18 collect { value ->
19 accumulator = if (accumulator !== NULL) {
20 @Suppress("UNCHECKED_CAST")
21 operation(accumulator as S, value)
22 } else {
23 value
24 }
25 }
26
27 if (accumulator === NULL) throw NoSuchElementException("Empty flow can't be reduced")
28 @Suppress("UNCHECKED_CAST")
29 return accumulator as S
30 }
31
32 /**
33 * Accumulates value starting with [initial] value and applying [operation] current accumulator value and each element
34 */
foldnull35 public suspend inline fun <T, R> Flow<T>.fold(
36 initial: R,
37 crossinline operation: suspend (acc: R, value: T) -> R
38 ): R {
39 var accumulator = initial
40 collect { value ->
41 accumulator = operation(accumulator, value)
42 }
43 return accumulator
44 }
45
46 /**
47 * The terminal operator that awaits for one and only one value to be emitted.
48 * Throws [NoSuchElementException] for empty flow and [IllegalArgumentException] for flow
49 * that contains more than one element.
50 */
singlenull51 public suspend fun <T> Flow<T>.single(): T {
52 var result: Any? = NULL
53 collect { value ->
54 require(result === NULL) { "Flow has more than one element" }
55 result = value
56 }
57
58 if (result === NULL) throw NoSuchElementException("Flow is empty")
59 return result as T
60 }
61
62 /**
63 * The terminal operator that awaits for one and only one value to be emitted.
64 * Returns the single value or `null`, if the flow was empty or emitted more than one value.
65 */
singleOrNullnull66 public suspend fun <T> Flow<T>.singleOrNull(): T? {
67 var result: Any? = NULL
68 collectWhile {
69 // No values yet, update result
70 if (result === NULL) {
71 result = it
72 true
73 } else {
74 // Second value, reset result and bail out
75 result = NULL
76 false
77 }
78 }
79 return if (result === NULL) null else result as T
80 }
81
82 /**
83 * The terminal operator that returns the first element emitted by the flow and then cancels flow's collection.
84 * Throws [NoSuchElementException] if the flow was empty.
85 */
firstnull86 public suspend fun <T> Flow<T>.first(): T {
87 var result: Any? = NULL
88 collectWhile {
89 result = it
90 false
91 }
92 if (result === NULL) throw NoSuchElementException("Expected at least one element")
93 return result as T
94 }
95
96 /**
97 * The terminal operator that returns the first element emitted by the flow matching the given [predicate] and then cancels flow's collection.
98 * Throws [NoSuchElementException] if the flow has not contained elements matching the [predicate].
99 */
firstnull100 public suspend fun <T> Flow<T>.first(predicate: suspend (T) -> Boolean): T {
101 var result: Any? = NULL
102 collectWhile {
103 if (predicate(it)) {
104 result = it
105 false
106 } else {
107 true
108 }
109 }
110 if (result === NULL) throw NoSuchElementException("Expected at least one element matching the predicate $predicate")
111 return result as T
112 }
113
114 /**
115 * The terminal operator that returns the first element emitted by the flow and then cancels flow's collection.
116 * Returns `null` if the flow was empty.
117 */
firstOrNullnull118 public suspend fun <T> Flow<T>.firstOrNull(): T? {
119 var result: T? = null
120 collectWhile {
121 result = it
122 false
123 }
124 return result
125 }
126
127 /**
128 * The terminal operator that returns the first element emitted by the flow matching the given [predicate] and then cancels flow's collection.
129 * Returns `null` if the flow did not contain an element matching the [predicate].
130 */
firstOrNullnull131 public suspend fun <T> Flow<T>.firstOrNull(predicate: suspend (T) -> Boolean): T? {
132 var result: T? = null
133 collectWhile {
134 if (predicate(it)) {
135 result = it
136 false
137 } else {
138 true
139 }
140 }
141 return result
142 }
143
144 /**
145 * The terminal operator that returns the last element emitted by the flow.
146 *
147 * Throws [NoSuchElementException] if the flow was empty.
148 */
lastnull149 public suspend fun <T> Flow<T>.last(): T {
150 var result: Any? = NULL
151 collect {
152 result = it
153 }
154 if (result === NULL) throw NoSuchElementException("Expected at least one element")
155 return result as T
156 }
157
158 /**
159 * The terminal operator that returns the last element emitted by the flow or `null` if the flow was empty.
160 */
lastOrNullnull161 public suspend fun <T> Flow<T>.lastOrNull(): T? {
162 var result: T? = null
163 collect {
164 result = it
165 }
166 return result
167 }
168