1717package com.tunjid.mutator.coroutines
1818
1919import com.tunjid.mutator.Mutation
20+ import kotlinx.coroutines.CoroutineScope
2021import kotlinx.coroutines.channels.BufferOverflow
2122import kotlinx.coroutines.channels.Channel
2223import kotlinx.coroutines.flow.Flow
2324import kotlinx.coroutines.flow.channelFlow
25+ import kotlinx.coroutines.flow.collect
2426import kotlinx.coroutines.flow.flatMapMerge
27+ import kotlinx.coroutines.flow.flow
2528import kotlinx.coroutines.flow.onStart
2629import kotlinx.coroutines.flow.receiveAsFlow
30+ import kotlinx.coroutines.launch
2731
2832/* *
2933 * Class holding the context of the [Action] emitted that is being split out into
@@ -32,7 +36,7 @@ import kotlinx.coroutines.flow.receiveAsFlow
3236 * Use typically involves invoking [type] to identify the [Action] stream being transformed, and
3337 * subsequently invoking [flow] to perform a custom transformation on the split out [Flow].
3438 */
35- data class TransformationContext <Action : Any >(
39+ class TransformationContext <Action : Any >(
3640 private val type : Action ,
3741 val backing : Flow <Action >,
3842) {
@@ -63,12 +67,12 @@ data class TransformationContext<Action : Any>(
6367 * [onBufferOverflow]: The behavior of the [Channel] on overflow. See the [BufferOverflow]
6468 * for details.
6569 *
66- * [keySelector]: The mapping for the [Action] to the key used to identify it. This is useful
67- * for nested class hierarchies. By default each distinct type will be split out, but if you want
68- * to treat certain subtypes as one type, this lets you do that.
70+ * [keySelector]: The mapping for the [Action] to the key used to identify it. This is useful
71+ * for nested class hierarchies. By default each distinct type will be split out, but if you want
72+ * to treat certain subtypes as one type, this lets you do that.
6973 *
70- * [transform]: a function for mapping independent [Flow]s of [Action] to [Flow]s of [State]
71- * [Mutation]s
74+ * [transform]: a function for mapping independent [Flow]s of [Action] to [Flow]s of [State]
75+ * [Mutation]s
7276 * @see [splitByType]
7377 */
7478fun <Action : Any , State : Any > Flow<Action>.toMutationStream (
@@ -85,6 +89,53 @@ fun <Action : Any, State : Any> Flow<Action>.toMutationStream(
8589 transform = transform,
8690)
8791
92+ /* *
93+ * Processes a [Flow] of [Action] in the provided [productionScope], allowing for finer grained
94+ * processing on subtypes of [Action]. This allows for certain actions to be processed differently
95+ * than others. For example: a certain action may need to be only processed on distinct
96+ * emissions, whereas other actions may need to use more complex [Flow] transformations like
97+ * [Flow.flatMapMerge] and so on. It does so by creating a [Channel] for each subtype.
98+ *
99+ * [productionScope]: The [CoroutineScope] for processing flows transformed.
100+ * [capacity]: The capacity for the [Channel] created for each subtype. See the [Channel] factory
101+ * function for details.
102+ *
103+ * [onBufferOverflow]: The behavior of the [Channel] on overflow. See the [BufferOverflow]
104+ * for details.
105+ *
106+ * [keySelector]: The mapping for the [Action] to the key used to identify it. This is useful
107+ * for nested class hierarchies. By default each distinct type will be split out, but if you want
108+ * to treat certain subtypes as one type, this lets you do that.
109+ *
110+ * [transform]: a function for processing independent [Flow]s of [Action]. Each independent flow should be collected
111+ * in this block.
112+ *
113+ * @see [splitByType]
114+ */
115+ fun <Action : Any > Flow<Action>.launchMutationsIn (
116+ productionScope : CoroutineScope ,
117+ capacity : Int = Channel .BUFFERED ,
118+ onBufferOverflow : BufferOverflow = BufferOverflow .SUSPEND ,
119+ keySelector : (Action ) -> String = Any : :defaultKeySelector,
120+ transform : suspend TransformationContext <Action >.() -> Unit ,
121+ ) {
122+ productionScope.launch {
123+ splitByType(
124+ capacity = capacity,
125+ onBufferOverflow = onBufferOverflow,
126+ typeSelector = { it },
127+ keySelector = keySelector,
128+ transform = transformation@{
129+ flow {
130+ transform(this @transformation)
131+ emit(Unit )
132+ }
133+ },
134+ )
135+ .collect()
136+ }
137+ }
138+
88139/* *
89140 * Transforms a [Flow] of [Input] to a [Flow] of [Output] by splitting the original into [Flow]s
90141 * of type [Selector]. Each independent [Flow] of the [Selector] type can then be transformed
@@ -125,10 +176,11 @@ fun <Input : Any, Selector : Any, Output : Any> Flow<Input>.splitByType(
125176 }
126177
127178 else -> {
128- existingHolder.internalSharedFlow .send(selected)
179+ existingHolder.channel .send(selected)
129180 }
130181 }
131182 }
183+ keysToFlowHolders.values.forEach { it.channel.close() }
132184 }
133185 .flatMapMerge(
134186 concurrency = Int .MAX_VALUE ,
@@ -144,16 +196,16 @@ private data class FlowHolder<Action>(
144196 val onBufferOverflow : BufferOverflow ,
145197 val firstEmission : Action ,
146198) {
147- val internalSharedFlow : Channel <Action > = Channel (
199+ val channel : Channel <Action > = Channel (
148200 capacity = capacity,
149201 onBufferOverflow = onBufferOverflow,
150202 )
151- val exposedFlow: Flow <Action > = internalSharedFlow
203+ val exposedFlow: Flow <Action > = channel
152204 .receiveAsFlow()
153205 .onStart { emit(firstEmission) }
154206}
155207
156- private fun Any.defaultKeySelector (): String = this ::class .simpleName
208+ private fun Any.defaultKeySelector (): String = this ::class .qualifiedName
157209 ? : throw IllegalArgumentException (
158210 " Only well defined classes can be split or specify a different key selector" ,
159211 )
0 commit comments