
commonMain.space.kscience.dataforge.actions.SplitAction.kt Maven / Gradle / Ivy
package space.kscience.dataforge.actions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import space.kscience.dataforge.data.*
import space.kscience.dataforge.meta.Laminate
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.toMutableMeta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.misc.DFInternal
import space.kscience.dataforge.names.Name
import kotlin.collections.set
import kotlin.reflect.KType
import kotlin.reflect.typeOf
public class SplitBuilder(public val name: Name, public val meta: Meta) {
public class FragmentRule(public val name: Name, public var meta: MutableMeta) {
public lateinit var result: suspend (T) -> R
public fun result(f: suspend (T) -> R) {
result = f;
}
}
internal val fragments: MutableMap.() -> Unit> = HashMap()
/**
* Add new fragment building rule. If the framgent not defined, result won't be available even if it is present in the map
* @param name the name of a fragment
* @param rule the rule to transform fragment name and meta using
*/
public fun fragment(name: String, rule: FragmentRule.() -> Unit) {
fragments[Name.parse(name)] = rule
}
}
/**
* Action that splits each incoming element into a number of fragments defined in builder
*/
@PublishedApi
internal class SplitAction(
private val outputType: KType,
private val action: SplitBuilder.() -> Unit,
) : Action {
@OptIn(FlowPreview::class)
override suspend fun execute(
dataSet: DataSet,
meta: Meta,
scope: CoroutineScope?,
): DataSet {
suspend fun splitOne(data: NamedData): Flow> {
val laminate = Laminate(data.meta, meta)
val split = SplitBuilder(data.name, data.meta).apply(action)
// apply individual fragment rules to result
return split.fragments.entries.asFlow().map { (fragmentName, rule) ->
val env = SplitBuilder.FragmentRule(fragmentName, laminate.toMutableMeta()).apply(rule)
//data.map(outputType, meta = env.meta) { env.result(it) }.named(fragmentName)
@OptIn(DFInternal::class) Data(outputType, meta = env.meta, dependencies = listOf(data)) {
env.result(data.await())
}.named(fragmentName)
}
}
return ActiveDataTree(outputType) {
populate(dataSet.flow().flatMapConcat(transform = ::splitOne))
scope?.launch {
dataSet.updates.collect { name ->
//clear old nodes
remove(name)
//collect new items
populate(dataSet.flowChildren(name).flatMapConcat(transform = ::splitOne))
}
}
}
}
}
/**
* Action that splits each incoming element into a number of fragments defined in builder
*/
@DFExperimental
@Suppress("FunctionName")
public inline fun Action.Companion.split(
noinline builder: SplitBuilder.() -> Unit,
): Action = SplitAction(typeOf(), builder)
© 2015 - 2025 Weber Informatics LLC | Privacy Policy