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

com.lightningkite.lightningdb.paths.kt Maven / Gradle / Ivy

The newest version!
package com.lightningkite.lightningdb

import com.lightningkite.khrysalis.IsCodableAndHashable
import kotlin.reflect.KProperty1

fun  Modification.valueSetForDataClassPath(path: DataClassPath): V? =
    (forDataClassPath(path.properties) as? Modification.Assign)?.value

fun  Modification.forDataClassPath(path: DataClassPath): Modification? =
    forDataClassPath(path.properties)

@Suppress("UNCHECKED_CAST")
private fun  Modification<*>.forDataClassPath(list: List>): Modification? {
    return when (this) {
        is Modification.OnField<*, *> -> if (list.first() == this.key) {
            if (list.size == 1) modification as Modification
            else this.modification.forDataClassPath(list.drop(1))
        } else null

        is Modification.SetPerElement<*> -> this.modification.forDataClassPath(list)
        is Modification.ListPerElement<*> -> this.modification.forDataClassPath(list)
        is Modification.Chain -> this.modifications.mapNotNull { it.forDataClassPath(list) }.let {
            when (it.size) {
                0 -> null
                1 -> it.first()
                else -> Modification.Chain(it)
            }
        }

        is Modification.IfNotNull -> this.modification.forDataClassPath(list)
        is Modification.Assign -> Modification.Assign(list.fold(value) { value, prop ->
            (prop as KProperty1).get(
                value
            )
        } as V)

        else -> throw Exception("We have no idea what the partial effect is!")
    }
}

fun Modification<*>.affects(path: DataClassPathPartial<*>): Boolean = affects(path.properties)
private fun Modification<*>.affects(list: List>): Boolean {
    return when (this) {
        is Modification.OnField<*, *> -> if (list.first() == this.key) {
            if (list.size == 1) true
            else this.modification.affects(list.drop(1))
        } else false

        is Modification.SetPerElement<*> -> this.modification.affects(list)
        is Modification.ListPerElement<*> -> this.modification.affects(list)
        is Modification.Chain -> this.modifications.any { it.affects(list) }
        is Modification.IfNotNull -> this.modification.affects(list)
        else -> true
    }
}

fun Condition<*>.reads(path: DataClassPathPartial<*>): Boolean = reads(path.properties)
private fun Condition<*>.reads(list: List>): Boolean {
    return when (this) {
        is Condition.OnField<*, *> -> if (list.first() == this.key) {
            if (list.size == 1) true
            else this.condition.reads(list.drop(1))
        } else false

        is Condition.ListAllElements<*> -> this.condition.reads(list)
        is Condition.ListAnyElements<*> -> this.condition.reads(list)
        is Condition.SetAllElements<*> -> this.condition.reads(list)
        is Condition.SetAnyElements<*> -> this.condition.reads(list)
        is Condition.And -> this.conditions.any { it.reads(list) }
        is Condition.Or -> this.conditions.any { it.reads(list) }
        is Condition.IfNotNull -> this.condition.reads(list)
        else -> true
    }
}

fun  Condition.readPaths(): Set> {
    val out = HashSet>()
    emitReadPaths { out.add(it) }
    return out
}
fun  Condition.emitReadPaths(out: (DataClassPathPartial) -> Unit) = emitReadPaths(DataClassPathSelf()) { out(it as DataClassPathPartial) }
private fun Condition<*>.emitReadPaths(soFar: DataClassPath<*, *>, out: (DataClassPathPartial<*>) -> Unit) {
    when (this) {
        is Condition.Always -> {}
        is Condition.Never -> {}
        is Condition.OnField<*, *> -> condition.emitReadPaths(DataClassPathAccess(soFar as DataClassPath, key as KProperty1), out)
        is Condition.Not -> this.condition.emitReadPaths(soFar, out)
        is Condition.And -> this.conditions.forEach { it.emitReadPaths(soFar, out) }
        is Condition.Or -> this.conditions.forEach { it.emitReadPaths(soFar, out) }
        is Condition.IfNotNull<*> -> this.condition.emitReadPaths(DataClassPathNotNull(soFar as DataClassPath), out)
        else -> out(soFar)
    }
    // COND: (a, (b, (c, x)))
    // PATH: (((root, a), b), c)
}

fun  Condition.readsResultOf(modification: Modification): Boolean {
    return when (this) {
        is Condition.Always -> false
        is Condition.Never -> false
        is Condition.OnField<*, *> -> {
            val field = modification as? Modification.OnField<*, *> ?: return false
            @Suppress("UNCHECKED_CAST")
            field.key == this.key && (this.condition as Condition).readsResultOf(field.modification as Modification)
        }

        is Condition.ListAllElements<*> -> @Suppress("UNCHECKED_CAST") (this.condition as Condition).readsResultOf(
            (modification as? Modification.ListPerElement<*>)?.modification as? Modification ?: return false
        )

        is Condition.ListAnyElements<*> -> @Suppress("UNCHECKED_CAST") (this.condition as Condition).readsResultOf(
            (modification as? Modification.ListPerElement<*>)?.modification as? Modification ?: return false
        )

        is Condition.SetAllElements<*> -> @Suppress("UNCHECKED_CAST") (this.condition as Condition).readsResultOf(
            (modification as? Modification.ListPerElement<*>)?.modification as? Modification ?: return false
        )

        is Condition.SetAnyElements<*> -> @Suppress("UNCHECKED_CAST") (this.condition as Condition).readsResultOf(
            (modification as? Modification.ListPerElement<*>)?.modification as? Modification ?: return false
        )

        is Condition.And -> this.conditions.any { it.readsResultOf(modification) }
        is Condition.Or -> this.conditions.any { it.readsResultOf(modification) }
        is Condition.IfNotNull<*> -> {
            @Suppress("UNCHECKED_CAST")
            (this.condition as Condition).readsResultOf(
                (modification as? Modification.IfNotNull)?.modification ?: modification as Modification
            )
        }

        else -> true
    }
}

@Suppress("UNCHECKED_CAST")
fun  Condition.guaranteedAfter(modification: Modification): Boolean {
    return when (modification) {
        is Modification.Assign -> this(modification.value)
        is Modification.OnField<*, *> -> {
            val field = this as? Condition.OnField<*, *> ?: return true
            @Suppress("UNCHECKED_CAST")
            field.key != modification.key || (field.condition as Condition).guaranteedAfter(modification.modification as Modification)
        }

        is Modification.SetPerElement<*> -> {
            @Suppress("UNCHECKED_CAST")
            ((this as? Condition.SetAllElements)?.condition
                ?: (this as? Condition.SetAnyElements)?.condition)?.let {
                (it as Condition).guaranteedAfter(modification.modification as Modification)
            } ?: false
        }

        is Modification.ListPerElement<*> -> {
            @Suppress("UNCHECKED_CAST")
            ((this as? Condition.ListAllElements)?.condition
                ?: (this as? Condition.ListAnyElements)?.condition)?.let {
                (it as Condition).guaranteedAfter(modification.modification as Modification)
            } ?: false
        }

        is Modification.Chain -> modification.modifications.all { guaranteedAfter(it) }
        is Modification.IfNotNull<*> -> {
            @Suppress("UNCHECKED_CAST")
            (this as Condition).guaranteedAfter(modification.modification as Modification)
        }

        else -> false
    }
}

@Suppress("UNCHECKED_CAST")
fun  Modification.map(
    path: DataClassPath,
    onModification: (Modification) -> Modification,
): Modification = (this as Modification).map(path.properties, onModification) as Modification

@Suppress("UNCHECKED_CAST")
private fun  Modification<*>.map(
    list: List>,
    onModification: (Modification) -> Modification,
): Modification<*> {
    return when (this) {
        is Modification.Chain -> modifications.map { it.map(list, onModification) as Modification }
            .let { Modification.Chain(it) }

        is Modification.OnField<*, *> -> if (list.first() == this.key) {
            if (list.size == 1) Modification.OnField(
                key = this.key as KProperty1,
                modification = onModification(modification as Modification) as Modification
            ) as Modification
            else this.modification.map(list.drop(1), onModification)
        } else this

        is Modification.SetPerElement<*> -> (this.modification as Modification).map(list, onModification)
        is Modification.ListPerElement<*> -> (this.modification as Modification).map(list, onModification)
        is Modification.IfNotNull -> (this.modification as Modification).map(list, onModification)
        is Modification.Assign -> {
            fun mapValue(
                value: Any?,
                list: List>,
                onValue: (V) -> V,
            ): Any? {
                if (value == null) return null
                if (list.isEmpty()) return onValue(value as V)
                return list.first().setCopy(value, mapValue(list.first().get(value), list.drop(1), onValue))
            }
            Modification.Assign(mapValue(value, list as List>) {
                (onModification(Modification.Assign(it)) as Modification.Assign).value
            })
        }

        else -> throw Exception("We have no idea what the partial effect is!")
    }
}


fun Condition<*>.walk(action: (Condition<*>)->Unit) {
    action(this)
    when(this) {
        is Condition.And -> this.conditions.forEach { it.walk(action) }
        is Condition.Or -> this.conditions.forEach { it.walk(action) }
        is Condition.Not -> this.condition.walk(action)
        is Condition.ListAllElements<*> -> this.condition.walk(action)
        is Condition.ListAnyElements<*> -> this.condition.walk(action)
        is Condition.SetAllElements<*> -> this.condition.walk(action)
        is Condition.SetAnyElements<*> -> this.condition.walk(action)
        is Condition.OnKey<*> -> this.condition.walk(action)
        is Condition.OnField<*, *> -> this.condition.walk(action)
        is Condition.IfNotNull -> this.condition.walk(action)
        else -> {}
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy