All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.space.kscience.dataforge.actions.SplitAction.kt Maven / Gradle / Ivy

There is a newer version: 0.7.0
Show newest version
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